Merge branch '237-versioning-client' into 'master'

Client for negotiating supported clients

Closes #237

See merge request akwizgran/briar!759
This commit is contained in:
Torsten Grote
2018-04-29 16:08:08 +00:00
113 changed files with 3090 additions and 632 deletions

View File

@@ -12,19 +12,19 @@ public interface ContactGroupFactory {
/** /**
* Creates a group that is not shared with any contacts. * Creates a group that is not shared with any contacts.
*/ */
Group createLocalGroup(ClientId clientId, int clientVersion); Group createLocalGroup(ClientId clientId, int majorVersion);
/** /**
* Creates a group for the given client to share with the given contact. * Creates a group for the given client to share with the given contact.
*/ */
Group createContactGroup(ClientId clientId, int clientVersion, Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact); Contact contact);
/** /**
* Creates a group for the given client to share between the given authors * Creates a group for the given client to share between the given authors
* identified by their AuthorIds. * identified by their AuthorIds.
*/ */
Group createContactGroup(ClientId clientId, int clientVersion, Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2); AuthorId authorId1, AuthorId authorId2);
} }

View File

@@ -241,7 +241,8 @@ public interface DatabaseComponent {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException; Collection<Group> getGroups(Transaction txn, ClientId c, int majorVersion)
throws DbException;
/** /**
* Returns the given group's visibility to the given contact, or * Returns the given group's visibility to the given contact, or
@@ -266,6 +267,14 @@ public interface DatabaseComponent {
*/ */
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException; Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
/**
* Returns the IDs of all messages in the given group.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
throws DbException;
/** /**
* Returns the IDs of any messages that need to be validated. * Returns the IDs of any messages that need to be validated.
* <p/> * <p/>

View File

@@ -43,17 +43,20 @@ public interface LifecycleManager {
} }
/** /**
* Registers a {@link Service} to be started and stopped. * Registers a {@link Service} to be started and stopped. This method
* should be called before {@link #startServices(String)}.
*/ */
void registerService(Service s); void registerService(Service s);
/** /**
* Registers a {@link Client} to be started. * Registers a {@link Client} to be started. This method should be called
* before {@link #startServices(String)}.
*/ */
void registerClient(Client c); void registerClient(Client c);
/** /**
* Registers an {@link ExecutorService} to be shut down. * Registers an {@link ExecutorService} to be shut down. This method
* should be called before {@link #startServices(String)}.
*/ */
void registerForShutdown(ExecutorService e); void registerForShutdown(ExecutorService e);

View File

@@ -15,12 +15,17 @@ public interface TransportPropertyManager {
/** /**
* The unique ID of the transport property client. * The unique ID of the transport property client.
*/ */
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties"); ClientId CLIENT_ID = new ClientId("org.briarproject.bramble.properties");
/** /**
* The current version of the transport property client. * The current major version of the transport property client.
*/ */
int CLIENT_VERSION = 0; int MAJOR_VERSION = 0;
/**
* The current minor version of the transport property client.
*/
int MINOR_VERSION = 0;
/** /**
* Stores the given properties received while adding a contact - they will * Stores the given properties received while adding a contact - they will
@@ -37,8 +42,8 @@ public interface TransportPropertyManager {
/** /**
* Returns the local transport properties for all transports. * Returns the local transport properties for all transports.
* <br/> * <p/>
* TODO: Transaction can be read-only when code is simplified * Read-only.
*/ */
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn) Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
throws DbException; throws DbException;

View File

@@ -5,9 +5,24 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPT
public class Group { public class Group {
public enum Visibility { public enum Visibility {
INVISIBLE, // The group is not visible
VISIBLE, // The group is visible but messages are not shared INVISIBLE(0), // The group is not visible
SHARED // The group is visible and messages are shared VISIBLE(1), // The group is visible, messages are accepted but not sent
SHARED(2); // The group is visible, messages are accepted and sent
private final int value;
Visibility(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static Visibility min(Visibility a, Visibility b) {
return a.getValue() < b.getValue() ? a : b;
}
} }
/** /**
@@ -17,13 +32,16 @@ public class Group {
private final GroupId id; private final GroupId id;
private final ClientId clientId; private final ClientId clientId;
private final int majorVersion;
private final byte[] descriptor; private final byte[] descriptor;
public Group(GroupId id, ClientId clientId, byte[] descriptor) { public Group(GroupId id, ClientId clientId, int majorVersion,
byte[] descriptor) {
if (descriptor.length > MAX_GROUP_DESCRIPTOR_LENGTH) if (descriptor.length > MAX_GROUP_DESCRIPTOR_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
this.id = id; this.id = id;
this.clientId = clientId; this.clientId = clientId;
this.majorVersion = majorVersion;
this.descriptor = descriptor; this.descriptor = descriptor;
} }
@@ -41,6 +59,13 @@ public class Group {
return clientId; return clientId;
} }
/**
* Returns the major version of the client to which the group belongs.
*/
public int getMajorVersion() {
return majorVersion;
}
/** /**
* Returns the group's descriptor. * Returns the group's descriptor.
*/ */

View File

@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
public interface GroupFactory { public interface GroupFactory {
/** /**
* Creates a group with the given client ID, client version and descriptor. * Creates a group with the given client ID, major version and descriptor.
*/ */
Group createGroup(ClientId c, int clientVersion, byte[] descriptor); Group createGroup(ClientId c, int majorVersion, byte[] descriptor);
} }

View File

@@ -19,7 +19,9 @@ public interface SyncConstants {
*/ */
int MAX_RECORD_PAYLOAD_LENGTH = 48 * 1024; // 48 KiB int MAX_RECORD_PAYLOAD_LENGTH = 48 * 1024; // 48 KiB
/** The maximum length of a group descriptor in bytes. */ /**
* The maximum length of a group descriptor in bytes.
*/
int MAX_GROUP_DESCRIPTOR_LENGTH = 16 * 1024; // 16 KiB int MAX_GROUP_DESCRIPTOR_LENGTH = 16 * 1024; // 16 KiB
/** /**

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/** /**
@@ -33,15 +34,20 @@ public interface ValidationManager {
} }
/** /**
* Sets the message validator for the given client. * Registers the message validator for the given client. This method
* should be called before {@link LifecycleManager#startServices(String)}.
*/ */
void registerMessageValidator(ClientId c, MessageValidator v); void registerMessageValidator(ClientId c, int majorVersion,
MessageValidator v);
/** /**
* Sets the incoming message hook for the given client. The hook will be * Registers the incoming message hook for the given client. The hook will
* called once for each incoming message that passes validation. * be called once for each incoming message that passes validation. This
* method should be called before
* {@link LifecycleManager#startServices(String)}.
*/ */
void registerIncomingMessageHook(ClientId c, IncomingMessageHook hook); void registerIncomingMessageHook(ClientId c, int majorVersion,
IncomingMessageHook hook);
interface MessageValidator { interface MessageValidator {

View File

@@ -0,0 +1,50 @@
package org.briarproject.bramble.api.versioning;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ClientMajorVersion implements Comparable<ClientMajorVersion> {
private final ClientId clientId;
private final int majorVersion;
public ClientMajorVersion(ClientId clientId, int majorVersion) {
this.clientId = clientId;
this.majorVersion = majorVersion;
}
public ClientId getClientId() {
return clientId;
}
public int getMajorVersion() {
return majorVersion;
}
@Override
public boolean equals(Object o) {
if (o instanceof ClientMajorVersion) {
ClientMajorVersion cv = (ClientMajorVersion) o;
return clientId.equals(cv.clientId)
&& majorVersion == cv.majorVersion;
}
return false;
}
@Override
public int hashCode() {
return (clientId.hashCode() << 16) + majorVersion;
}
@Override
public int compareTo(ClientMajorVersion cv) {
int compare = clientId.compareTo(cv.clientId);
if (compare != 0) return compare;
return majorVersion - cv.majorVersion;
}
}

View File

@@ -0,0 +1,45 @@
package org.briarproject.bramble.api.versioning;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group.Visibility;
@NotNullByDefault
public interface ClientVersioningManager {
/**
* The unique ID of the versioning client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.bramble.versioning");
/**
* The current major version of the versioning client.
*/
int MAJOR_VERSION = 0;
/**
* Registers a client that will be advertised to contacts. The hook will
* be called when the visibility of the client changes. This method should
* be called before {@link LifecycleManager#startServices(String)}.
*/
void registerClient(ClientId clientId, int majorVersion, int minorVersion,
ClientVersioningHook hook);
/**
* Returns the visibility of the given client with respect to the given
* contact.
*/
Visibility getClientVisibility(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException;
interface ClientVersioningHook {
void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException;
}
}

View File

@@ -117,15 +117,16 @@ public class TestUtils {
return new Author(id, FORMAT_VERSION, name, publicKey); return new Author(id, FORMAT_VERSION, name, publicKey);
} }
public static Group getGroup(ClientId clientId) { public static Group getGroup(ClientId clientId, int majorVersion) {
int descriptorLength = 1 + random.nextInt(MAX_GROUP_DESCRIPTOR_LENGTH); int descriptorLength = 1 + random.nextInt(MAX_GROUP_DESCRIPTOR_LENGTH);
return getGroup(clientId, descriptorLength); return getGroup(clientId, majorVersion, descriptorLength);
} }
public static Group getGroup(ClientId clientId, int descriptorLength) { public static Group getGroup(ClientId clientId, int majorVersion,
int descriptorLength) {
GroupId groupId = new GroupId(getRandomId()); GroupId groupId = new GroupId(getRandomId());
byte[] descriptor = getRandomBytes(descriptorLength); byte[] descriptor = getRandomBytes(descriptorLength);
return new Group(groupId, clientId, descriptor); return new Group(groupId, clientId, majorVersion, descriptor);
} }
public static Message getMessage(GroupId groupId) { public static Message getMessage(GroupId groupId) {

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble; package org.briarproject.bramble;
import org.briarproject.bramble.contact.ContactModule; import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoModule; import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.db.DatabaseExecutorModule; import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.identity.IdentityModule; import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.lifecycle.LifecycleModule; import org.briarproject.bramble.lifecycle.LifecycleModule;
@@ -10,12 +10,13 @@ import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.sync.SyncModule; import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule; import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
public interface BrambleCoreEagerSingletons { public interface BrambleCoreEagerSingletons {
void inject(ContactModule.EagerSingletons init); void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init); void inject(CryptoExecutorModule.EagerSingletons init);
void inject(DatabaseExecutorModule.EagerSingletons init); void inject(DatabaseExecutorModule.EagerSingletons init);
@@ -32,4 +33,6 @@ public interface BrambleCoreEagerSingletons {
void inject(SystemModule.EagerSingletons init); void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init); void inject(TransportModule.EagerSingletons init);
void inject(VersioningModule.EagerSingletons init);
} }

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble;
import org.briarproject.bramble.client.ClientModule; import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.contact.ContactModule; import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule; import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.data.DataModule; import org.briarproject.bramble.data.DataModule;
import org.briarproject.bramble.db.DatabaseExecutorModule; import org.briarproject.bramble.db.DatabaseExecutorModule;
@@ -19,6 +20,7 @@ import org.briarproject.bramble.socks.SocksModule;
import org.briarproject.bramble.sync.SyncModule; import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule; import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import dagger.Module; import dagger.Module;
@@ -26,6 +28,7 @@ import dagger.Module;
ClientModule.class, ClientModule.class,
ContactModule.class, ContactModule.class,
CryptoModule.class, CryptoModule.class,
CryptoExecutorModule.class,
DataModule.class, DataModule.class,
DatabaseModule.class, DatabaseModule.class,
DatabaseExecutorModule.class, DatabaseExecutorModule.class,
@@ -41,13 +44,14 @@ import dagger.Module;
SocksModule.class, SocksModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class,
VersioningModule.class
}) })
public class BrambleCoreModule { public class BrambleCoreModule {
public static void initEagerSingletons(BrambleCoreEagerSingletons c) { public static void initEagerSingletons(BrambleCoreEagerSingletons c) {
c.inject(new ContactModule.EagerSingletons()); c.inject(new ContactModule.EagerSingletons());
c.inject(new CryptoModule.EagerSingletons()); c.inject(new CryptoExecutorModule.EagerSingletons());
c.inject(new DatabaseExecutorModule.EagerSingletons()); c.inject(new DatabaseExecutorModule.EagerSingletons());
c.inject(new IdentityModule.EagerSingletons()); c.inject(new IdentityModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons()); c.inject(new LifecycleModule.EagerSingletons());
@@ -56,5 +60,6 @@ public class BrambleCoreModule {
c.inject(new SyncModule.EagerSingletons()); c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons()); c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons()); c.inject(new TransportModule.EagerSingletons());
c.inject(new VersioningModule.EagerSingletons());
} }
} }

View File

@@ -32,25 +32,25 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
} }
@Override @Override
public Group createLocalGroup(ClientId clientId, int clientVersion) { public Group createLocalGroup(ClientId clientId, int majorVersion) {
return groupFactory.createGroup(clientId, clientVersion, return groupFactory.createGroup(clientId, majorVersion,
LOCAL_GROUP_DESCRIPTOR); LOCAL_GROUP_DESCRIPTOR);
} }
@Override @Override
public Group createContactGroup(ClientId clientId, int clientVersion, public Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact) { Contact contact) {
AuthorId local = contact.getLocalAuthorId(); AuthorId local = contact.getLocalAuthorId();
AuthorId remote = contact.getAuthor().getId(); AuthorId remote = contact.getAuthor().getId();
byte[] descriptor = createGroupDescriptor(local, remote); byte[] descriptor = createGroupDescriptor(local, remote);
return groupFactory.createGroup(clientId, clientVersion, descriptor); return groupFactory.createGroup(clientId, majorVersion, descriptor);
} }
@Override @Override
public Group createContactGroup(ClientId clientId, int clientVersion, public Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2) { AuthorId authorId1, AuthorId authorId2) {
byte[] descriptor = createGroupDescriptor(authorId1, authorId2); byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
return groupFactory.createGroup(clientId, clientVersion, descriptor); return groupFactory.createGroup(clientId, majorVersion, descriptor);
} }
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) { private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {

View File

@@ -0,0 +1,67 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.TimeLoggingExecutor;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static java.util.concurrent.TimeUnit.SECONDS;
@Module
public class CryptoExecutorModule {
public static class EagerSingletons {
@Inject
@CryptoExecutor
ExecutorService cryptoExecutor;
}
/**
* The maximum number of executor threads.
* <p>
* The number of available processors can change during the lifetime of the
* JVM, so this is just a reasonable guess.
*/
private static final int MAX_EXECUTOR_THREADS =
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
private final ExecutorService cryptoExecutor;
public CryptoExecutorModule() {
// Use an unbounded queue
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
// Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy();
// Create a limited # of threads and keep them in the pool for 60 secs
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
}
@Provides
@Singleton
@CryptoExecutor
ExecutorService provideCryptoExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(cryptoExecutor);
return cryptoExecutor;
}
@Provides
@CryptoExecutor
Executor provideCryptoExecutor() {
return cryptoExecutor;
}
}

View File

@@ -1,64 +1,24 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.TimeLoggingExecutor;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory; import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory; import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static java.util.concurrent.TimeUnit.SECONDS;
@Module @Module
public class CryptoModule { public class CryptoModule {
public static class EagerSingletons {
@Inject
@CryptoExecutor
ExecutorService cryptoExecutor;
}
/**
* The maximum number of executor threads.
* <p>
* The number of available processors can change during the lifetime of the
* JVM, so this is just a reasonable guess.
*/
private static final int MAX_EXECUTOR_THREADS =
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
private final ExecutorService cryptoExecutor;
public CryptoModule() {
// Use an unbounded queue
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
// Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy();
// Create a limited # of threads and keep them in the pool for 60 secs
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
}
@Provides @Provides
AuthenticatedCipher provideAuthenticatedCipher() { AuthenticatedCipher provideAuthenticatedCipher() {
return new XSalsa20Poly1305AuthenticatedCipher(); return new XSalsa20Poly1305AuthenticatedCipher();
@@ -103,21 +63,6 @@ public class CryptoModule {
return keyAgreementCrypto; return keyAgreementCrypto;
} }
@Provides
@Singleton
@CryptoExecutor
ExecutorService getCryptoExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(cryptoExecutor);
return cryptoExecutor;
}
@Provides
@CryptoExecutor
Executor getCryptoExecutor() {
return cryptoExecutor;
}
@Provides @Provides
SecureRandom getSecureRandom(CryptoComponent crypto) { SecureRandom getSecureRandom(CryptoComponent crypto) {
return crypto.getSecureRandom(); return crypto.getSecureRandom();

View File

@@ -266,7 +266,8 @@ interface Database<T> {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<Group> getGroups(T txn, ClientId c) throws DbException; Collection<Group> getGroups(T txn, ClientId c, int majorVersion)
throws DbException;
/** /**
* Returns the given group's visibility to the given contact, or * Returns the given group's visibility to the given contact, or

View File

@@ -435,10 +435,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public Collection<Group> getGroups(Transaction transaction, ClientId c) public Collection<Group> getGroups(Transaction transaction, ClientId c,
throws DbException { int majorVersion) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
return db.getGroups(txn, c); return db.getGroups(txn, c, majorVersion);
} }
@Override @Override
@@ -466,6 +466,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getLocalAuthors(txn); return db.getLocalAuthors(txn);
} }
@Override
public Collection<MessageId> getMessageIds(Transaction transaction,
GroupId g) throws DbException {
T txn = unbox(transaction);
if (!db.containsGroup(txn, g))
throw new NoSuchGroupException();
return db.getMessageIds(txn, g);
}
@Override @Override
public Collection<MessageId> getMessagesToValidate(Transaction transaction) public Collection<MessageId> getMessagesToValidate(Transaction transaction)
throws DbException { throws DbException {

View File

@@ -74,7 +74,7 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
static final int CODE_SCHEMA_VERSION = 37; static final int CODE_SCHEMA_VERSION = 38;
// Rotation period offsets for incoming transport keys // Rotation period offsets for incoming transport keys
private static final int OFFSET_PREV = -1; private static final int OFFSET_PREV = -1;
@@ -117,6 +117,7 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE groups" "CREATE TABLE groups"
+ " (groupId _HASH NOT NULL," + " (groupId _HASH NOT NULL,"
+ " clientId _STRING NOT NULL," + " clientId _STRING NOT NULL,"
+ " majorVersion INT NOT NULL,"
+ " descriptor _BINARY NOT NULL," + " descriptor _BINARY NOT NULL,"
+ " PRIMARY KEY (groupId))"; + " PRIMARY KEY (groupId))";
@@ -275,9 +276,9 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE INDEX IF NOT EXISTS contactsByAuthorId" "CREATE INDEX IF NOT EXISTS contactsByAuthorId"
+ " ON contacts (authorId)"; + " ON contacts (authorId)";
private static final String INDEX_GROUPS_BY_CLIENT_ID = private static final String INDEX_GROUPS_BY_CLIENT_ID_MAJOR_VERSION =
"CREATE INDEX IF NOT EXISTS groupsByClientId" "CREATE INDEX IF NOT EXISTS groupsByClientIdMajorVersion"
+ " ON groups (clientId)"; + " ON groups (clientId, majorVersion)";
private static final String INDEX_MESSAGE_METADATA_BY_GROUP_ID_STATE = private static final String INDEX_MESSAGE_METADATA_BY_GROUP_ID_STATE =
"CREATE INDEX IF NOT EXISTS messageMetadataByGroupIdState" "CREATE INDEX IF NOT EXISTS messageMetadataByGroupIdState"
@@ -444,7 +445,7 @@ abstract class JdbcDatabase implements Database<Connection> {
try { try {
s = txn.createStatement(); s = txn.createStatement();
s.executeUpdate(INDEX_CONTACTS_BY_AUTHOR_ID); s.executeUpdate(INDEX_CONTACTS_BY_AUTHOR_ID);
s.executeUpdate(INDEX_GROUPS_BY_CLIENT_ID); s.executeUpdate(INDEX_GROUPS_BY_CLIENT_ID_MAJOR_VERSION);
s.executeUpdate(INDEX_MESSAGE_METADATA_BY_GROUP_ID_STATE); s.executeUpdate(INDEX_MESSAGE_METADATA_BY_GROUP_ID_STATE);
s.executeUpdate(INDEX_MESSAGE_DEPENDENCIES_BY_DEPENDENCY_ID); s.executeUpdate(INDEX_MESSAGE_DEPENDENCIES_BY_DEPENDENCY_ID);
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID); s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID);
@@ -612,12 +613,14 @@ abstract class JdbcDatabase implements Database<Connection> {
public void addGroup(Connection txn, Group g) throws DbException { public void addGroup(Connection txn, Group g) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "INSERT INTO groups (groupId, clientId, descriptor)" String sql = "INSERT INTO groups"
+ " VALUES (?, ?, ?)"; + " (groupId, clientId, majorVersion, descriptor)"
+ " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getId().getBytes()); ps.setBytes(1, g.getId().getBytes());
ps.setString(2, g.getClientId().getString()); ps.setString(2, g.getClientId().getString());
ps.setBytes(3, g.getDescriptor()); ps.setInt(3, g.getMajorVersion());
ps.setBytes(4, g.getDescriptor());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -1346,17 +1349,18 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT clientId, descriptor FROM groups" String sql = "SELECT clientId, majorVersion, descriptor"
+ " WHERE groupId = ?"; + " FROM groups WHERE groupId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException(); if (!rs.next()) throw new DbStateException();
ClientId clientId = new ClientId(rs.getString(1)); ClientId clientId = new ClientId(rs.getString(1));
byte[] descriptor = rs.getBytes(2); int majorVersion = rs.getInt(2);
byte[] descriptor = rs.getBytes(3);
rs.close(); rs.close();
ps.close(); ps.close();
return new Group(g, clientId, descriptor); return new Group(g, clientId, majorVersion, descriptor);
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs); tryToClose(rs);
tryToClose(ps); tryToClose(ps);
@@ -1365,21 +1369,22 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public Collection<Group> getGroups(Connection txn, ClientId c) public Collection<Group> getGroups(Connection txn, ClientId c,
throws DbException { int majorVersion) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT groupId, descriptor FROM groups" String sql = "SELECT groupId, descriptor FROM groups"
+ " WHERE clientId = ?"; + " WHERE clientId = ? AND majorVersion = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(1, c.getString()); ps.setString(1, c.getString());
ps.setInt(2, majorVersion);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<Group> groups = new ArrayList<>(); List<Group> groups = new ArrayList<>();
while (rs.next()) { while (rs.next()) {
GroupId id = new GroupId(rs.getBytes(1)); GroupId id = new GroupId(rs.getBytes(1));
byte[] descriptor = rs.getBytes(2); byte[] descriptor = rs.getBytes(2);
groups.add(new Group(id, c, descriptor)); groups.add(new Group(id, c, majorVersion, descriptor));
} }
rs.close(); rs.close();
ps.close(); ps.close();

View File

@@ -7,6 +7,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.ValidationManager; import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -15,6 +16,8 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID; import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MINOR_VERSION;
@Module @Module
public class PropertiesModule { public class PropertiesModule {
@@ -33,7 +36,8 @@ public class PropertiesModule {
Clock clock) { Clock clock) {
TransportPropertyValidator validator = new TransportPropertyValidator( TransportPropertyValidator validator = new TransportPropertyValidator(
clientHelper, metadataEncoder, clock); clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validator); validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator; return validator;
} }
@@ -42,11 +46,14 @@ public class PropertiesModule {
TransportPropertyManager getTransportPropertyManager( TransportPropertyManager getTransportPropertyManager(
LifecycleManager lifecycleManager, LifecycleManager lifecycleManager,
ValidationManager validationManager, ContactManager contactManager, ValidationManager validationManager, ContactManager contactManager,
ClientVersioningManager clientVersioningManager,
TransportPropertyManagerImpl transportPropertyManager) { TransportPropertyManagerImpl transportPropertyManager) {
lifecycleManager.registerClient(transportPropertyManager); lifecycleManager.registerClient(transportPropertyManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
transportPropertyManager); transportPropertyManager);
contactManager.registerContactHook(transportPropertyManager); contactManager.registerContactHook(transportPropertyManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, transportPropertyManager);
return transportPropertyManager; return transportPropertyManager;
} }
} }

View File

@@ -19,12 +19,15 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Client; import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.InvalidMessageException; import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook; import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -34,15 +37,14 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class TransportPropertyManagerImpl implements TransportPropertyManager, class TransportPropertyManagerImpl implements TransportPropertyManager,
Client, ContactHook, IncomingMessageHook { Client, ContactHook, ClientVersioningHook, IncomingMessageHook {
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final ClientVersioningManager clientVersioningManager;
private final MetadataParser metadataParser; private final MetadataParser metadataParser;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
private final Clock clock; private final Clock clock;
@@ -50,22 +52,25 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Inject @Inject
TransportPropertyManagerImpl(DatabaseComponent db, TransportPropertyManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, MetadataParser metadataParser, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory, Clock clock) { ContactGroupFactory contactGroupFactory, Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.clientVersioningManager = clientVersioningManager;
this.metadataParser = metadataParser; this.metadataParser = metadataParser;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
this.clock = clock; this.clock = clock;
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID, localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
CLIENT_VERSION); MAJOR_VERSION);
} }
@Override @Override
public void createLocalState(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
if (db.containsGroup(txn, localGroup.getId())) return; if (db.containsGroup(txn, localGroup.getId())) return;
db.addGroup(txn, localGroup); db.addGroup(txn, localGroup);
// Ensure we've set things up for any pre-existing contacts // Set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c); for (Contact c : db.getContacts(txn)) addingContact(txn, c);
} }
@@ -73,11 +78,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact // Create a group to share with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c);
// Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED); // Apply the client's visibility to the contact group
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// Copy the latest local properties into the group // Copy the latest local properties into the group
Map<TransportId, TransportProperties> local = getLocalProperties(txn); Map<TransportId, TransportProperties> local = getLocalProperties(txn);
for (Entry<TransportId, TransportProperties> e : local.entrySet()) { for (Entry<TransportId, TransportProperties> e : local.entrySet()) {
@@ -91,6 +96,14 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
db.removeGroup(txn, getContactGroup(c)); db.removeGroup(txn, getContactGroup(c));
} }
@Override
public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException {
// Apply the client's visibility to the contact group
Group g = getContactGroup(c);
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
}
@Override @Override
public boolean incomingMessage(Transaction txn, Message m, Metadata meta) public boolean incomingMessage(Transaction txn, Message m, Metadata meta)
throws DbException, InvalidMessageException { throws DbException, InvalidMessageException {
@@ -289,7 +302,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
private Group getContactGroup(Contact c) { private Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
CLIENT_VERSION, c); MAJOR_VERSION, c);
} }
private void storeMessage(Transaction txn, GroupId g, TransportId t, private void storeMessage(Transaction txn, GroupId g, TransportId t,

View File

@@ -28,12 +28,12 @@ class GroupFactoryImpl implements GroupFactory {
} }
@Override @Override
public Group createGroup(ClientId c, int clientVersion, byte[] descriptor) { public Group createGroup(ClientId c, int majorVersion, byte[] descriptor) {
byte[] clientVersionBytes = new byte[INT_32_BYTES]; byte[] majorVersionBytes = new byte[INT_32_BYTES];
ByteUtils.writeUint32(clientVersion, clientVersionBytes, 0); ByteUtils.writeUint32(majorVersion, majorVersionBytes, 0);
byte[] hash = crypto.hash(LABEL, new byte[] {FORMAT_VERSION}, byte[] hash = crypto.hash(LABEL, new byte[] {FORMAT_VERSION},
StringUtils.toUtf8(c.getString()), clientVersionBytes, StringUtils.toUtf8(c.getString()), majorVersionBytes,
descriptor); descriptor);
return new Group(new GroupId(hash), c, descriptor); return new Group(new GroupId(hash), c, majorVersion, descriptor);
} }
} }

View File

@@ -20,6 +20,7 @@ import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager; import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.sync.event.MessageAddedEvent; import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
import java.util.Collection; import java.util.Collection;
import java.util.LinkedList; import java.util.LinkedList;
@@ -51,8 +52,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
private final DatabaseComponent db; private final DatabaseComponent db;
private final Executor dbExecutor, validationExecutor; private final Executor dbExecutor, validationExecutor;
private final MessageFactory messageFactory; private final MessageFactory messageFactory;
private final Map<ClientId, MessageValidator> validators; private final Map<ClientMajorVersion, MessageValidator> validators;
private final Map<ClientId, IncomingMessageHook> hooks; private final Map<ClientMajorVersion, IncomingMessageHook> hooks;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@Inject @Inject
@@ -81,14 +82,15 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
@Override @Override
public void registerMessageValidator(ClientId c, MessageValidator v) { public void registerMessageValidator(ClientId c, int majorVersion,
validators.put(c, v); MessageValidator v) {
validators.put(new ClientMajorVersion(c, majorVersion), v);
} }
@Override @Override
public void registerIncomingMessageHook(ClientId c, public void registerIncomingMessageHook(ClientId c, int majorVersion,
IncomingMessageHook hook) { IncomingMessageHook hook) {
hooks.put(c, hook); hooks.put(new ClientMajorVersion(c, majorVersion), hook);
} }
private void validateOutstandingMessagesAsync() { private void validateOutstandingMessagesAsync() {
@@ -199,9 +201,11 @@ class ValidationManagerImpl implements ValidationManager, Service,
Message m = messageFactory.createMessage(id, raw); Message m = messageFactory.createMessage(id, raw);
Group g = db.getGroup(txn, m.getGroupId()); Group g = db.getGroup(txn, m.getGroupId());
ClientId c = g.getClientId(); ClientId c = g.getClientId();
int majorVersion = g.getMajorVersion();
Metadata meta = Metadata meta =
db.getMessageMetadataForValidator(txn, id); db.getMessageMetadataForValidator(txn, id);
DeliveryResult result = deliverMessage(txn, m, c, meta); DeliveryResult result =
deliverMessage(txn, m, c, majorVersion, meta);
if (result.valid) { if (result.valid) {
pending.addAll(getPendingDependents(txn, id)); pending.addAll(getPendingDependents(txn, id));
if (result.share) { if (result.share) {
@@ -237,14 +241,16 @@ class ValidationManagerImpl implements ValidationManager, Service,
@ValidationExecutor @ValidationExecutor
private void validateMessage(Message m, Group g) { private void validateMessage(Message m, Group g) {
MessageValidator v = validators.get(g.getClientId()); ClientMajorVersion cv =
new ClientMajorVersion(g.getClientId(), g.getMajorVersion());
MessageValidator v = validators.get(cv);
if (v == null) { if (v == null) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING)) LOG.warning("No validator for " + cv);
LOG.warning("No validator for " + g.getClientId().getString());
} else { } else {
try { try {
MessageContext context = v.validateMessage(m, g); MessageContext context = v.validateMessage(m, g);
storeMessageContextAsync(m, g.getClientId(), context); storeMessageContextAsync(m, g.getClientId(),
g.getMajorVersion(), context);
} catch (InvalidMessageException e) { } catch (InvalidMessageException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.log(INFO, e.toString(), e); LOG.log(INFO, e.toString(), e);
@@ -256,12 +262,13 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
private void storeMessageContextAsync(Message m, ClientId c, private void storeMessageContextAsync(Message m, ClientId c,
MessageContext result) { int majorVersion, MessageContext result) {
dbExecutor.execute(() -> storeMessageContext(m, c, result)); dbExecutor.execute(() ->
storeMessageContext(m, c, majorVersion, result));
} }
@DatabaseExecutor @DatabaseExecutor
private void storeMessageContext(Message m, ClientId c, private void storeMessageContext(Message m, ClientId c, int majorVersion,
MessageContext context) { MessageContext context) {
try { try {
MessageId id = m.getId(); MessageId id = m.getId();
@@ -292,7 +299,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
Metadata meta = context.getMetadata(); Metadata meta = context.getMetadata();
db.mergeMessageMetadata(txn, id, meta); db.mergeMessageMetadata(txn, id, meta);
if (allDelivered) { if (allDelivered) {
DeliveryResult result = deliverMessage(txn, m, c, meta); DeliveryResult result =
deliverMessage(txn, m, c, majorVersion, meta);
if (result.valid) { if (result.valid) {
pending = getPendingDependents(txn, id); pending = getPendingDependents(txn, id);
if (result.share) { if (result.share) {
@@ -324,10 +332,11 @@ class ValidationManagerImpl implements ValidationManager, Service,
@DatabaseExecutor @DatabaseExecutor
private DeliveryResult deliverMessage(Transaction txn, Message m, private DeliveryResult deliverMessage(Transaction txn, Message m,
ClientId c, Metadata meta) throws DbException { ClientId c, int majorVersion, Metadata meta) throws DbException {
// Deliver the message to the client if it's registered a hook // Deliver the message to the client if it's registered a hook
boolean shareMsg = false; boolean shareMsg = false;
IncomingMessageHook hook = hooks.get(c); ClientMajorVersion cv = new ClientMajorVersion(c, majorVersion);
IncomingMessageHook hook = hooks.get(cv);
if (hook != null) { if (hook != null) {
try { try {
shareMsg = hook.incomingMessage(txn, m, meta); shareMsg = hook.incomingMessage(txn, m, meta);

View File

@@ -0,0 +1,10 @@
package org.briarproject.bramble.versioning;
interface ClientVersioningConstants {
// Metadata keys
String MSG_KEY_UPDATE_VERSION = "version";
String MSG_KEY_LOCAL = "local";
String GROUP_KEY_CONTACT_ID = "contactId";
}

View File

@@ -0,0 +1,622 @@
package org.briarproject.bramble.versioning;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
@NotNullByDefault
class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
Service, ContactHook, IncomingMessageHook {
private final DatabaseComponent db;
private final ClientHelper clientHelper;
private final ContactGroupFactory contactGroupFactory;
private final Clock clock;
private final Group localGroup;
private final List<ClientVersion> clients = new CopyOnWriteArrayList<>();
private final Map<ClientMajorVersion, ClientVersioningHook> hooks =
new ConcurrentHashMap<>();
@Inject
ClientVersioningManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ContactGroupFactory contactGroupFactory, Clock clock) {
this.db = db;
this.clientHelper = clientHelper;
this.contactGroupFactory = contactGroupFactory;
this.clock = clock;
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
MAJOR_VERSION);
}
@Override
public void registerClient(ClientId clientId, int majorVersion,
int minorVersion, ClientVersioningHook hook) {
ClientMajorVersion cv = new ClientMajorVersion(clientId, majorVersion);
clients.add(new ClientVersion(cv, minorVersion));
hooks.put(cv, hook);
}
@Override
public Visibility getClientVisibility(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException {
try {
Contact contact = db.getContact(txn, contactId);
Group g = getContactGroup(contact);
// Contact may be in the process of being added or removed, so
// contact group may not exist
if (!db.containsGroup(txn, g.getId())) return INVISIBLE;
LatestUpdates latest = findLatestUpdates(txn, g.getId());
if (latest.local == null) throw new DbException();
if (latest.remote == null) return INVISIBLE;
Update localUpdate = loadUpdate(txn, latest.local.messageId);
Update remoteUpdate = loadUpdate(txn, latest.remote.messageId);
Map<ClientMajorVersion, Visibility> visibilities =
getVisibilities(localUpdate.states, remoteUpdate.states);
ClientMajorVersion cv =
new ClientMajorVersion(clientId, majorVersion);
Visibility v = visibilities.get(cv);
return v == null ? INVISIBLE : v;
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override
public void createLocalState(Transaction txn) throws DbException {
if (db.containsGroup(txn, localGroup.getId())) return;
db.addGroup(txn, localGroup);
// Set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
}
@Override
public void startService() throws ServiceException {
List<ClientVersion> versions = new ArrayList<>(clients);
Collections.sort(versions);
try {
Transaction txn = db.startTransaction(false);
try {
if (updateClientVersions(txn, versions)) {
for (Contact c : db.getContacts(txn))
clientVersionsUpdated(txn, c, versions);
}
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
} catch (DbException e) {
throw new ServiceException(e);
}
}
@Override
public void stopService() throws ServiceException {
}
@Override
public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group and share it with the contact
Group g = getContactGroup(c);
db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group
BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
try {
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
// Create and store the first local update
List<ClientVersion> versions = new ArrayList<>(clients);
Collections.sort(versions);
storeFirstUpdate(txn, g.getId(), versions);
}
@Override
public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getContactGroup(c));
}
@Override
public boolean incomingMessage(Transaction txn, Message m, Metadata meta)
throws DbException, InvalidMessageException {
try {
// Parse the new remote update
Update newRemoteUpdate = parseUpdate(clientHelper.toList(m));
List<ClientState> newRemoteStates = newRemoteUpdate.states;
long newRemoteUpdateVersion = newRemoteUpdate.updateVersion;
// Find the latest local and remote updates, if any
LatestUpdates latest = findLatestUpdates(txn, m.getGroupId());
// If this update is obsolete, delete it and return
if (latest.remote != null
&& latest.remote.updateVersion > newRemoteUpdateVersion) {
db.deleteMessage(txn, m.getId());
db.deleteMessageMetadata(txn, m.getId());
return false;
}
// Load and parse the latest local update
if (latest.local == null) throw new DbException();
Update oldLocalUpdate = loadUpdate(txn, latest.local.messageId);
List<ClientState> oldLocalStates = oldLocalUpdate.states;
long oldLocalUpdateVersion = oldLocalUpdate.updateVersion;
// Load and parse the previous remote update, if any
List<ClientState> oldRemoteStates;
if (latest.remote == null) {
oldRemoteStates = emptyList();
} else {
oldRemoteStates =
loadUpdate(txn, latest.remote.messageId).states;
// Delete the previous remote update
db.deleteMessage(txn, latest.remote.messageId);
db.deleteMessageMetadata(txn, latest.remote.messageId);
}
// Update the local states from the remote states if necessary
List<ClientState> newLocalStates = updateStatesFromRemoteStates(
oldLocalStates, newRemoteStates);
if (!oldLocalStates.equals(newLocalStates)) {
// Delete the latest local update
db.deleteMessage(txn, latest.local.messageId);
db.deleteMessageMetadata(txn, latest.local.messageId);
// Store a new local update
storeUpdate(txn, m.getGroupId(), newLocalStates,
oldLocalUpdateVersion + 1);
}
// Calculate the old and new client visibilities
Map<ClientMajorVersion, Visibility> before =
getVisibilities(oldLocalStates, oldRemoteStates);
Map<ClientMajorVersion, Visibility> after =
getVisibilities(newLocalStates, newRemoteStates);
// Call hooks for any visibilities that have changed
if (!before.equals(after)) {
Contact c = getContact(txn, m.getGroupId());
callVisibilityHooks(txn, c, before, after);
}
} catch (FormatException e) {
throw new InvalidMessageException(e);
}
return false;
}
private void storeClientVersions(Transaction txn,
List<ClientVersion> versions) throws DbException {
long now = clock.currentTimeMillis();
BdfList body = encodeClientVersions(versions);
try {
Message m = clientHelper.createMessage(localGroup.getId(), now,
body);
db.addLocalMessage(txn, m, new Metadata(), false);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
private BdfList encodeClientVersions(List<ClientVersion> versions) {
BdfList encoded = new BdfList();
for (ClientVersion cv : versions) encoded.add(encodeClientVersion(cv));
return encoded;
}
private BdfList encodeClientVersion(ClientVersion cv) {
return BdfList.of(cv.majorVersion.getClientId().getString(),
cv.majorVersion.getMajorVersion(), cv.minorVersion);
}
/**
* Stores the local client versions and returns true if an update needs to
* be sent to contacts.
*/
private boolean updateClientVersions(Transaction txn,
List<ClientVersion> newVersions) throws DbException {
Collection<MessageId> ids = db.getMessageIds(txn, localGroup.getId());
if (ids.isEmpty()) {
storeClientVersions(txn, newVersions);
return true;
}
if (ids.size() != 1) throw new DbException();
MessageId m = ids.iterator().next();
List<ClientVersion> oldVersions = loadClientVersions(txn, m);
if (oldVersions.equals(newVersions)) return false;
db.removeMessage(txn, m);
storeClientVersions(txn, newVersions);
return true;
}
private List<ClientVersion> loadClientVersions(Transaction txn,
MessageId m) throws DbException {
try {
BdfList body = clientHelper.getMessageAsList(txn, m);
if (body == null) throw new DbException();
return parseClientVersions(body);
} catch (FormatException e) {
throw new DbException(e);
}
}
private List<ClientVersion> parseClientVersions(BdfList body)
throws FormatException {
int size = body.size();
List<ClientVersion> parsed = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
BdfList cv = body.getList(i);
ClientId clientId = new ClientId(cv.getString(0));
int majorVersion = cv.getLong(1).intValue();
int minorVersion = cv.getLong(2).intValue();
parsed.add(new ClientVersion(clientId, majorVersion,
minorVersion));
}
return parsed;
}
private void clientVersionsUpdated(Transaction txn, Contact c,
List<ClientVersion> versions) throws DbException {
try {
// Find the latest local and remote updates
Group g = getContactGroup(c);
LatestUpdates latest = findLatestUpdates(txn, g.getId());
// Load and parse the latest local update
if (latest.local == null) throw new DbException();
Update oldLocalUpdate = loadUpdate(txn, latest.local.messageId);
List<ClientState> oldLocalStates = oldLocalUpdate.states;
long oldLocalUpdateVersion = oldLocalUpdate.updateVersion;
// Load and parse the latest remote update, if any
List<ClientState> remoteStates;
if (latest.remote == null) remoteStates = emptyList();
else remoteStates = loadUpdate(txn, latest.remote.messageId).states;
// Update the local states if necessary
List<ClientState> newLocalStates =
updateStatesFromLocalVersions(oldLocalStates, versions);
newLocalStates = updateStatesFromRemoteStates(newLocalStates,
remoteStates);
if (!oldLocalStates.equals(newLocalStates)) {
// Delete the latest local update
db.deleteMessage(txn, latest.local.messageId);
db.deleteMessageMetadata(txn, latest.local.messageId);
// Store a new local update
storeUpdate(txn, g.getId(), newLocalStates,
oldLocalUpdateVersion + 1);
}
// Calculate the old and new client visibilities
Map<ClientMajorVersion, Visibility> before =
getVisibilities(oldLocalStates, remoteStates);
Map<ClientMajorVersion, Visibility> after =
getVisibilities(newLocalStates, remoteStates);
// Call hooks for any visibilities that have changed
callVisibilityHooks(txn, c, before, after);
} catch (FormatException e) {
throw new DbException(e);
}
}
private Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c);
}
private LatestUpdates findLatestUpdates(Transaction txn, GroupId g)
throws DbException, FormatException {
Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g);
LatestUpdate local = null, remote = null;
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue();
long updateVersion = meta.getLong(MSG_KEY_UPDATE_VERSION);
if (meta.getBoolean(MSG_KEY_LOCAL))
local = new LatestUpdate(e.getKey(), updateVersion);
else remote = new LatestUpdate(e.getKey(), updateVersion);
}
return new LatestUpdates(local, remote);
}
private Update loadUpdate(Transaction txn, MessageId m) throws DbException {
try {
BdfList body = clientHelper.getMessageAsList(txn, m);
if (body == null) throw new DbException();
return parseUpdate(body);
} catch (FormatException e) {
throw new DbException(e);
}
}
private Update parseUpdate(BdfList body) throws FormatException {
List<ClientState> states = parseClientStates(body);
long updateVersion = parseUpdateVersion(body);
return new Update(states, updateVersion);
}
private List<ClientState> parseClientStates(BdfList body)
throws FormatException {
// Client states, update version
BdfList states = body.getList(0);
int size = states.size();
List<ClientState> parsed = new ArrayList<>(size);
for (int i = 0; i < size; i++)
parsed.add(parseClientState(states.getList(i)));
return parsed;
}
private ClientState parseClientState(BdfList clientState)
throws FormatException {
// Client ID, major version, minor version, active
ClientId clientId = new ClientId(clientState.getString(0));
int majorVersion = clientState.getLong(1).intValue();
int minorVersion = clientState.getLong(2).intValue();
boolean active = clientState.getBoolean(3);
return new ClientState(clientId, majorVersion, minorVersion, active);
}
private long parseUpdateVersion(BdfList body) throws FormatException {
// Client states, update version
return body.getLong(1);
}
private List<ClientState> updateStatesFromLocalVersions(
List<ClientState> oldStates, List<ClientVersion> newVersions) {
Map<ClientMajorVersion, ClientState> oldMap = new HashMap<>();
for (ClientState cs : oldStates) oldMap.put(cs.majorVersion, cs);
List<ClientState> newStates = new ArrayList<>(newVersions.size());
for (ClientVersion newVersion : newVersions) {
ClientState oldState = oldMap.get(newVersion.majorVersion);
boolean active = oldState != null && oldState.active;
newStates.add(new ClientState(newVersion.majorVersion,
newVersion.minorVersion, active));
}
return newStates;
}
private void storeUpdate(Transaction txn, GroupId g,
List<ClientState> states, long updateVersion) throws DbException {
try {
BdfList body = encodeUpdate(states, updateVersion);
long now = clock.currentTimeMillis();
Message m = clientHelper.createMessage(g, now, body);
BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_UPDATE_VERSION, updateVersion);
meta.put(MSG_KEY_LOCAL, true);
clientHelper.addLocalMessage(txn, m, meta, true);
} catch (FormatException e) {
throw new RuntimeException(e);
}
}
private BdfList encodeUpdate(List<ClientState> states, long updateVersion) {
BdfList encoded = new BdfList();
for (ClientState cs : states) encoded.add(encodeClientState(cs));
return BdfList.of(encoded, updateVersion);
}
private BdfList encodeClientState(ClientState cs) {
return BdfList.of(cs.majorVersion.getClientId().getString(),
cs.majorVersion.getMajorVersion(), cs.minorVersion, cs.active);
}
private Map<ClientMajorVersion, Visibility> getVisibilities(
List<ClientState> localStates, List<ClientState> remoteStates) {
Map<ClientMajorVersion, ClientState> remoteMap = new HashMap<>();
for (ClientState cs : remoteStates) remoteMap.put(cs.majorVersion, cs);
Map<ClientMajorVersion, Visibility> visibilities = new HashMap<>();
for (ClientState local : localStates) {
ClientState remote = remoteMap.get(local.majorVersion);
if (remote == null) visibilities.put(local.majorVersion, INVISIBLE);
else if (remote.active)
visibilities.put(local.majorVersion, SHARED);
else visibilities.put(local.majorVersion, VISIBLE);
}
return visibilities;
}
private void callVisibilityHooks(Transaction txn, Contact c,
Map<ClientMajorVersion, Visibility> before,
Map<ClientMajorVersion, Visibility> after) throws DbException {
Set<ClientMajorVersion> keys = new TreeSet<>();
keys.addAll(before.keySet());
keys.addAll(after.keySet());
for (ClientMajorVersion cv : keys) {
Visibility vBefore = before.get(cv), vAfter = after.get(cv);
if (vAfter == null) {
callVisibilityHook(txn, cv, c, INVISIBLE);
} else if (vBefore == null || !vBefore.equals(vAfter)) {
callVisibilityHook(txn, cv, c, vAfter);
}
}
}
private void callVisibilityHook(Transaction txn, ClientMajorVersion cv,
Contact c, Visibility v) throws DbException {
ClientVersioningHook hook = hooks.get(cv);
if (hook != null) hook.onClientVisibilityChanging(txn, c, v);
}
private void storeFirstUpdate(Transaction txn, GroupId g,
List<ClientVersion> versions) throws DbException {
List<ClientState> states = new ArrayList<>(versions.size());
for (ClientVersion cv : versions) {
states.add(new ClientState(cv.majorVersion, cv.minorVersion,
false));
}
storeUpdate(txn, g, states, 1);
}
private Contact getContact(Transaction txn, GroupId g) throws DbException {
try {
BdfDictionary meta =
clientHelper.getGroupMetadataAsDictionary(txn, g);
int id = meta.getLong(GROUP_KEY_CONTACT_ID).intValue();
return db.getContact(txn, new ContactId(id));
} catch (FormatException e) {
throw new DbException(e);
}
}
private List<ClientState> updateStatesFromRemoteStates(
List<ClientState> oldLocalStates, List<ClientState> remoteStates) {
Set<ClientMajorVersion> remoteSet = new HashSet<>();
for (ClientState cs : remoteStates) remoteSet.add(cs.majorVersion);
List<ClientState> newLocalStates =
new ArrayList<>(oldLocalStates.size());
for (ClientState oldState : oldLocalStates) {
boolean active = remoteSet.contains(oldState.majorVersion);
newLocalStates.add(new ClientState(oldState.majorVersion,
oldState.minorVersion, active));
}
return newLocalStates;
}
private static class Update {
private final List<ClientState> states;
private final long updateVersion;
private Update(List<ClientState> states, long updateVersion) {
this.states = states;
this.updateVersion = updateVersion;
}
}
private static class LatestUpdate {
private final MessageId messageId;
private final long updateVersion;
private LatestUpdate(MessageId messageId, long updateVersion) {
this.messageId = messageId;
this.updateVersion = updateVersion;
}
}
private static class LatestUpdates {
@Nullable
private final LatestUpdate local, remote;
private LatestUpdates(@Nullable LatestUpdate local,
@Nullable LatestUpdate remote) {
this.local = local;
this.remote = remote;
}
}
private static class ClientVersion implements Comparable<ClientVersion> {
private final ClientMajorVersion majorVersion;
private final int minorVersion;
private ClientVersion(ClientMajorVersion majorVersion,
int minorVersion) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
}
private ClientVersion(ClientId clientId, int majorVersion,
int minorVersion) {
this(new ClientMajorVersion(clientId, majorVersion), minorVersion);
}
@Override
public boolean equals(Object o) {
if (o instanceof ClientVersion) {
ClientVersion cv = (ClientVersion) o;
return majorVersion.equals(cv.majorVersion)
&& minorVersion == cv.minorVersion;
}
return false;
}
@Override
public int hashCode() {
return majorVersion.hashCode();
}
@Override
public int compareTo(ClientVersion cv) {
int compare = majorVersion.compareTo(cv.majorVersion);
if (compare != 0) return compare;
return minorVersion - cv.minorVersion;
}
}
private static class ClientState {
private final ClientMajorVersion majorVersion;
private final int minorVersion;
private final boolean active;
private ClientState(ClientMajorVersion majorVersion, int minorVersion,
boolean active) {
this.majorVersion = majorVersion;
this.minorVersion = minorVersion;
this.active = active;
}
private ClientState(ClientId clientId, int majorVersion,
int minorVersion, boolean active) {
this(new ClientMajorVersion(clientId, majorVersion), minorVersion,
active);
}
@Override
public boolean equals(Object o) {
if (o instanceof ClientState) {
ClientState cs = (ClientState) o;
return majorVersion.equals(cs.majorVersion)
&& minorVersion == cs.minorVersion
&& active == cs.active;
}
return false;
}
@Override
public int hashCode() {
return majorVersion.hashCode();
}
}
}

View File

@@ -0,0 +1,61 @@
package org.briarproject.bramble.versioning;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.client.BdfMessageValidator;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.system.Clock;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
@Immutable
@NotNullByDefault
class ClientVersioningValidator extends BdfMessageValidator {
ClientVersioningValidator(ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock) {
super(clientHelper, metadataEncoder, clock);
}
@Override
protected BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws FormatException {
// Client states, update version
checkSize(body, 2);
// Client states
BdfList states = body.getList(0);
int size = states.size();
for (int i = 0; i < size; i++) {
BdfList clientState = states.getList(i);
// Client ID, major version, minor version, active
checkSize(clientState, 4);
String clientId = clientState.getString(0);
checkLength(clientId, 1, MAX_CLIENT_ID_LENGTH);
int majorVersion = clientState.getLong(1).intValue();
if (majorVersion < 0) throw new FormatException();
int minorVersion = clientState.getLong(2).intValue();
if (minorVersion < 0) throw new FormatException();
clientState.getBoolean(3);
}
// Update version
long updateVersion = body.getLong(1);
if (updateVersion < 0) throw new FormatException();
// Return the metadata
BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_UPDATE_VERSION, updateVersion);
meta.put(MSG_KEY_LOCAL, false);
return new BdfMessageContext(meta);
}
}

View File

@@ -0,0 +1,56 @@
package org.briarproject.bramble.versioning;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static org.briarproject.bramble.api.versioning.ClientVersioningManager.CLIENT_ID;
import static org.briarproject.bramble.api.versioning.ClientVersioningManager.MAJOR_VERSION;
@Module
public class VersioningModule {
public static class EagerSingletons {
@Inject
ClientVersioningManager clientVersioningManager;
@Inject
ClientVersioningValidator clientVersioningValidator;
}
@Provides
@Singleton
ClientVersioningManager provideClientVersioningManager(
ClientVersioningManagerImpl clientVersioningManager,
LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager) {
lifecycleManager.registerClient(clientVersioningManager);
lifecycleManager.registerService(clientVersioningManager);
contactManager.registerContactHook(clientVersioningManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
clientVersioningManager);
return clientVersioningManager;
}
@Provides
@Singleton
ClientVersioningValidator provideClientVersioningValidator(
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock, ValidationManager validationManager) {
ClientVersioningValidator validator = new ClientVersioningValidator(
clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator;
}
}

View File

@@ -89,6 +89,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final Object txn = new Object(); private final Object txn = new Object();
private final ClientId clientId; private final ClientId clientId;
private final int majorVersion;
private final GroupId groupId; private final GroupId groupId;
private final Group group; private final Group group;
private final Author author; private final Author author;
@@ -106,7 +107,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
public DatabaseComponentImplTest() { public DatabaseComponentImplTest() {
clientId = getClientId(); clientId = getClientId();
group = getGroup(clientId); majorVersion = 123;
group = getGroup(clientId, majorVersion);
groupId = group.getId(); groupId = group.getId();
author = getAuthor(); author = getAuthor();
localAuthor = getLocalAuthor(); localAuthor = getLocalAuthor();
@@ -175,7 +177,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).containsGroup(txn, groupId); oneOf(database).containsGroup(txn, groupId);
will(returnValue(true)); will(returnValue(true));
// getGroups() // getGroups()
oneOf(database).getGroups(txn, clientId); oneOf(database).getGroups(txn, clientId, majorVersion);
will(returnValue(singletonList(group))); will(returnValue(singletonList(group)));
// removeGroup() // removeGroup()
oneOf(database).containsGroup(txn, groupId); oneOf(database).containsGroup(txn, groupId);
@@ -215,7 +217,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
db.addGroup(transaction, group); // First time - listeners called db.addGroup(transaction, group); // First time - listeners called
db.addGroup(transaction, group); // Second time - not called db.addGroup(transaction, group); // Second time - not called
assertEquals(singletonList(group), assertEquals(singletonList(group),
db.getGroups(transaction, clientId)); db.getGroups(transaction, clientId, majorVersion));
db.removeGroup(transaction, group); db.removeGroup(transaction, group);
db.removeContact(transaction, contactId); db.removeContact(transaction, contactId);
db.removeLocalAuthor(transaction, localAuthor.getId()); db.removeLocalAuthor(transaction, localAuthor.getId());

View File

@@ -267,7 +267,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
String name = "getGroups(T, ClientId)"; String name = "getGroups(T, ClientId)";
benchmark(name, db -> { benchmark(name, db -> {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.getGroups(txn, pickRandom(clientIds)); db.getGroups(txn, pickRandom(clientIds), 123);
db.commitTransaction(txn); db.commitTransaction(txn);
}); });
} }
@@ -550,7 +550,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
contacts.add(db.getContact(txn, c)); contacts.add(db.getContact(txn, c));
contactGroups.put(c, new ArrayList<>()); contactGroups.put(c, new ArrayList<>());
for (int j = 0; j < GROUPS_PER_CONTACT; j++) { for (int j = 0; j < GROUPS_PER_CONTACT; j++) {
Group g = getGroup(clientIds.get(j % CLIENTS)); Group g = getGroup(clientIds.get(j % CLIENTS), 123);
groups.add(g); groups.add(g);
messageMeta.put(g.getId(), new ArrayList<>()); messageMeta.put(g.getId(), new ArrayList<>());
contactGroups.get(c).add(g); contactGroups.get(c).add(g);
@@ -584,7 +584,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
} }
} }
for (int i = 0; i < LOCAL_GROUPS; i++) { for (int i = 0; i < LOCAL_GROUPS; i++) {
Group g = getGroup(clientIds.get(i % CLIENTS)); Group g = getGroup(clientIds.get(i % CLIENTS), 123);
groups.add(g); groups.add(g);
messageMeta.put(g.getId(), new ArrayList<>()); messageMeta.put(g.getId(), new ArrayList<>());
groupMessages.put(g.getId(), new ArrayList<>()); groupMessages.put(g.getId(), new ArrayList<>());

View File

@@ -82,6 +82,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private final File testDir = TestUtils.getTestDirectory(); private final File testDir = TestUtils.getTestDirectory();
private final GroupId groupId; private final GroupId groupId;
private final ClientId clientId; private final ClientId clientId;
private final int majorVersion;
private final Group group; private final Group group;
private final Author author; private final Author author;
private final LocalAuthor localAuthor; private final LocalAuthor localAuthor;
@@ -96,7 +97,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
JdbcDatabaseTest() throws Exception { JdbcDatabaseTest() throws Exception {
clientId = getClientId(); clientId = getClientId();
group = getGroup(clientId); majorVersion = 123;
group = getGroup(clientId, majorVersion);
groupId = group.getId(); groupId = group.getId();
author = getAuthor(); author = getAuthor();
localAuthor = getLocalAuthor(); localAuthor = getLocalAuthor();
@@ -1460,7 +1462,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessage(txn, message, PENDING, true, contactId); db.addMessage(txn, message, PENDING, true, contactId);
// Add a second group // Add a second group
Group group1 = getGroup(clientId); Group group1 = getGroup(clientId, 123);
GroupId groupId1 = group1.getId(); GroupId groupId1 = group1.getId();
db.addGroup(txn, group1); db.addGroup(txn, group1);
@@ -1828,6 +1830,22 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testGetGroups() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
assertEquals(emptyList(), db.getGroups(txn, clientId, majorVersion));
db.addGroup(txn, group);
assertEquals(singletonList(group),
db.getGroups(txn, clientId, majorVersion));
db.removeGroup(txn, groupId);
assertEquals(emptyList(), db.getGroups(txn, clientId, majorVersion));
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testExceptionHandling() throws Exception { public void testExceptionHandling() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);

View File

@@ -19,6 +19,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;
@@ -29,8 +30,9 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID; import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_VERSION; import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
@@ -45,13 +47,15 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class); private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final ClientVersioningManager clientVersioningManager =
context.mock(ClientVersioningManager.class);
private final MetadataParser metadataParser = private final MetadataParser metadataParser =
context.mock(MetadataParser.class); context.mock(MetadataParser.class);
private final ContactGroupFactory contactGroupFactory = private final ContactGroupFactory contactGroupFactory =
context.mock(ContactGroupFactory.class); context.mock(ContactGroupFactory.class);
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final Group localGroup = getGroup(CLIENT_ID); private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final LocalAuthor localAuthor = getLocalAuthor(); private final LocalAuthor localAuthor = getLocalAuthor();
private final BdfDictionary fooPropertiesDict = BdfDictionary.of( private final BdfDictionary fooPropertiesDict = BdfDictionary.of(
new BdfEntry("fooKey1", "fooValue1"), new BdfEntry("fooKey1", "fooValue1"),
@@ -77,49 +81,41 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
private TransportPropertyManagerImpl createInstance() { private TransportPropertyManagerImpl createInstance() {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID, oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID,
CLIENT_VERSION); MAJOR_VERSION);
will(returnValue(localGroup)); will(returnValue(localGroup));
}}); }});
return new TransportPropertyManagerImpl(db, clientHelper, return new TransportPropertyManagerImpl(db, clientHelper,
metadataParser, contactGroupFactory, clock); clientVersioningManager, metadataParser, contactGroupFactory,
clock);
} }
@Test @Test
public void testCreatesGroupsAtStartup() throws Exception { public void testCreatesGroupsAtStartup() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact1 = getContact(true); Contact contact = getContact(true);
Contact contact2 = getContact(true); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
List<Contact> contacts = Arrays.asList(contact1, contact2);
Group contactGroup1 = getGroup(CLIENT_ID);
Group contactGroup2 = getGroup(CLIENT_ID);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).containsGroup(txn, localGroup.getId()); oneOf(db).containsGroup(txn, localGroup.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(db).addGroup(txn, localGroup); oneOf(db).addGroup(txn, localGroup);
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(contacts)); will(returnValue(singletonList(contact)));
// The first contact's group has already been set up
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact1); MAJOR_VERSION, contact);
will(returnValue(contactGroup1)); will(returnValue(contactGroup));
oneOf(db).containsGroup(txn, contactGroup1.getId()); oneOf(db).addGroup(txn, contactGroup);
will(returnValue(true)); oneOf(clientVersioningManager).getClientVisibility(txn,
// The second contact's group hasn't been set up contact.getId(), CLIENT_ID, MAJOR_VERSION);
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, will(returnValue(SHARED));
CLIENT_VERSION, contact2); oneOf(db).setGroupVisibility(txn, contact.getId(),
will(returnValue(contactGroup2)); contactGroup.getId(), SHARED);
oneOf(db).containsGroup(txn, contactGroup2.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, contactGroup2);
oneOf(db).setGroupVisibility(txn, contact2.getId(),
contactGroup2.getId(), SHARED);
}}); }});
// Copy the latest local properties into the group // Copy the latest local properties into the group
expectGetLocalProperties(txn); expectGetLocalProperties(txn);
expectStoreMessage(txn, contactGroup2.getId(), "foo", fooPropertiesDict, expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
1, true, true); 1, true, true);
expectStoreMessage(txn, contactGroup2.getId(), "bar", barPropertiesDict, expectStoreMessage(txn, contactGroup.getId(), "bar", barPropertiesDict,
1, true, true); 1, true, true);
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
@@ -144,16 +140,17 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
public void testCreatesContactGroupWhenAddingContact() throws Exception { public void testCreatesContactGroupWhenAddingContact() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact = getContact(true); Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Create the group and share it with the contact // Create the group and share it with the contact
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).containsGroup(txn, contactGroup.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, contactGroup); oneOf(db).addGroup(txn, contactGroup);
oneOf(clientVersioningManager).getClientVisibility(txn,
contact.getId(), CLIENT_ID, MAJOR_VERSION);
will(returnValue(SHARED));
oneOf(db).setGroupVisibility(txn, contact.getId(), oneOf(db).setGroupVisibility(txn, contact.getId(),
contactGroup.getId(), SHARED); contactGroup.getId(), SHARED);
}}); }});
@@ -172,11 +169,11 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
public void testRemovesGroupWhenRemovingContact() throws Exception { public void testRemovesGroupWhenRemovingContact() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact = getContact(true); Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).removeGroup(txn, contactGroup); oneOf(db).removeGroup(txn, contactGroup);
}}); }});
@@ -307,7 +304,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testStoresRemotePropertiesWithVersion0() throws Exception { public void testStoresRemotePropertiesWithVersion0() throws Exception {
Contact contact = getContact(true); Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Map<TransportId, TransportProperties> properties = Map<TransportId, TransportProperties> properties =
new LinkedHashMap<>(); new LinkedHashMap<>();
@@ -318,7 +315,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
oneOf(db).getContact(txn, contact.getId()); oneOf(db).getContact(txn, contact.getId());
will(returnValue(contact)); will(returnValue(contact));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
}}); }});
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict, expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
@@ -421,8 +418,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Contact contact3 = getContact(true); Contact contact3 = getContact(true);
List<Contact> contacts = List<Contact> contacts =
Arrays.asList(contact1, contact2, contact3); Arrays.asList(contact1, contact2, contact3);
Group contactGroup2 = getGroup(CLIENT_ID); Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION);
Group contactGroup3 = getGroup(CLIENT_ID); Group contactGroup3 = getGroup(CLIENT_ID, MAJOR_VERSION);
Map<MessageId, BdfDictionary> messageMetadata3 = Map<MessageId, BdfDictionary> messageMetadata3 =
new LinkedHashMap<>(); new LinkedHashMap<>();
// A remote update for another transport should be ignored // A remote update for another transport should be ignored
@@ -456,14 +453,14 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// First contact: skipped because not active // First contact: skipped because not active
// Second contact: no updates // Second contact: no updates
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact2); MAJOR_VERSION, contact2);
will(returnValue(contactGroup2)); will(returnValue(contactGroup2));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup2.getId()); contactGroup2.getId());
will(returnValue(Collections.emptyMap())); will(returnValue(Collections.emptyMap()));
// Third contact: returns an update // Third contact: returns an update
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact3); MAJOR_VERSION, contact3);
will(returnValue(contactGroup3)); will(returnValue(contactGroup3));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup3.getId()); contactGroup3.getId());
@@ -524,7 +521,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
public void testMergingNewPropertiesCreatesUpdate() throws Exception { public void testMergingNewPropertiesCreatesUpdate() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact = getContact(true); Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
@@ -538,9 +535,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
fooPropertiesDict, 1, true, false); fooPropertiesDict, 1, true, false);
// Store the new properties in each contact's group, version 1 // Store the new properties in each contact's group, version 1
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(Collections.singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());
@@ -559,7 +556,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
public void testMergingUpdatedPropertiesCreatesUpdate() throws Exception { public void testMergingUpdatedPropertiesCreatesUpdate() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
Contact contact = getContact(true); Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
BdfDictionary oldMetadata = BdfDictionary.of( BdfDictionary oldMetadata = BdfDictionary.of(
new BdfEntry("transportId", "foo"), new BdfEntry("transportId", "foo"),
new BdfEntry("version", 1), new BdfEntry("version", 1),
@@ -597,9 +594,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
oneOf(db).removeMessage(txn, localGroupUpdateId); oneOf(db).removeMessage(txn, localGroupUpdateId);
// Store the merged properties in each contact's group, version 2 // Store the merged properties in each contact's group, version 2
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(Collections.singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact); MAJOR_VERSION, contact);
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());

View File

@@ -18,7 +18,8 @@ import org.junit.Test;
import java.io.IOException; import java.io.IOException;
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH; import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -42,7 +43,7 @@ public class TransportPropertyValidatorTest extends BrambleMockTestCase {
transportProperties = new TransportProperties(); transportProperties = new TransportProperties();
transportProperties.put("foo", "bar"); transportProperties.put("foo", "bar");
group = getGroup(getClientId()); group = getGroup(CLIENT_ID, MAJOR_VERSION);
message = getMessage(group.getId()); message = getMessage(group.getId());
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class); MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);

View File

@@ -81,9 +81,9 @@ public class SyncIntegrationTest extends BrambleTestCase {
streamNumber = 123; streamNumber = 123;
// Create a group // Create a group
ClientId clientId = getClientId(); ClientId clientId = getClientId();
int clientVersion = 1234567890; int majorVersion = 1234567890;
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH]; byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
Group group = groupFactory.createGroup(clientId, clientVersion, Group group = groupFactory.createGroup(clientId, majorVersion,
descriptor); descriptor);
// Add two messages to the group // Add two messages to the group
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();

View File

@@ -53,10 +53,11 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
private final Executor dbExecutor = new ImmediateExecutor(); private final Executor dbExecutor = new ImmediateExecutor();
private final Executor validationExecutor = new ImmediateExecutor(); private final Executor validationExecutor = new ImmediateExecutor();
private final ClientId clientId = getClientId(); private final ClientId clientId = getClientId();
private final int majorVersion = 123;
private final MessageId messageId = new MessageId(getRandomId()); private final MessageId messageId = new MessageId(getRandomId());
private final MessageId messageId1 = new MessageId(getRandomId()); private final MessageId messageId1 = new MessageId(getRandomId());
private final MessageId messageId2 = new MessageId(getRandomId()); private final MessageId messageId2 = new MessageId(getRandomId());
private final Group group = getGroup(clientId); private final Group group = getGroup(clientId, majorVersion);
private final GroupId groupId = group.getId(); private final GroupId groupId = group.getId();
private final long timestamp = System.currentTimeMillis(); private final long timestamp = System.currentTimeMillis();
private final byte[] raw = new byte[123]; private final byte[] raw = new byte[123];
@@ -85,8 +86,8 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
public void setUp() { public void setUp() {
vm = new ValidationManagerImpl(db, dbExecutor, validationExecutor, vm = new ValidationManagerImpl(db, dbExecutor, validationExecutor,
messageFactory); messageFactory);
vm.registerMessageValidator(clientId, validator); vm.registerMessageValidator(clientId, majorVersion, validator);
vm.registerIncomingMessageHook(clientId, hook); vm.registerIncomingMessageHook(clientId, majorVersion, hook);
} }
@Test @Test

View File

@@ -0,0 +1,21 @@
package org.briarproject.bramble.test;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import java.util.concurrent.Executor;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class TestCryptoExecutorModule {
@Provides
@Singleton
@CryptoExecutor
Executor provideCryptoExecutor() {
return new ImmediateExecutor();
}
}

View File

@@ -24,7 +24,7 @@ public abstract class ValidatorTestCase extends BrambleMockTestCase {
context.mock(MetadataEncoder.class); context.mock(MetadataEncoder.class);
protected final Clock clock = context.mock(Clock.class); protected final Clock clock = context.mock(Clock.class);
protected final Group group = getGroup(getClientId()); protected final Group group = getGroup(getClientId(), 123);
protected final GroupId groupId = group.getId(); protected final GroupId groupId = group.getId();
protected final byte[] descriptor = group.getDescriptor(); protected final byte[] descriptor = group.getDescriptor();
protected final Message message = getMessage(groupId); protected final Message message = getMessage(groupId);

View File

@@ -0,0 +1,669 @@
package org.briarproject.bramble.versioning;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.bramble.api.versioning.ClientVersioningManager.CLIENT_ID;
import static org.briarproject.bramble.api.versioning.ClientVersioningManager.MAJOR_VERSION;
import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
import static org.junit.Assert.assertFalse;
public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final ContactGroupFactory contactGroupFactory =
context.mock(ContactGroupFactory.class);
private final Clock clock = context.mock(Clock.class);
private final ClientVersioningHook hook =
context.mock(ClientVersioningHook.class);
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Contact contact = new Contact(new ContactId(123),
getAuthor(), getLocalAuthor().getId(), true, true);
private final ClientId clientId = getClientId();
private final long now = System.currentTimeMillis();
private final Transaction txn = new Transaction(null, false);
private ClientVersioningManagerImpl createInstance() throws Exception {
context.checking(new Expectations() {{
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID,
MAJOR_VERSION);
will(returnValue(localGroup));
}});
return new ClientVersioningManagerImpl(db, clientHelper,
contactGroupFactory, clock);
}
@Test
public void testCreatesGroupsAtStartup() throws Exception {
context.checking(new Expectations() {{
oneOf(db).containsGroup(txn, localGroup.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, localGroup);
oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact)));
}});
expectAddingContact();
ClientVersioningManagerImpl c = createInstance();
c.createLocalState(txn);
}
@Test
public void testDoesNotCreateGroupsAtStartupIfAlreadyCreated()
throws Exception {
context.checking(new Expectations() {{
oneOf(db).containsGroup(txn, localGroup.getId());
will(returnValue(true));
}});
ClientVersioningManagerImpl c = createInstance();
c.createLocalState(txn);
}
@Test
public void testCreatesContactGroupWhenAddingContact() throws Exception {
expectAddingContact();
ClientVersioningManagerImpl c = createInstance();
c.addingContact(txn, contact);
}
private void expectAddingContact() throws Exception {
BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
long now = System.currentTimeMillis();
BdfList localUpdateBody = BdfList.of(new BdfList(), 1L);
Message localUpdate = getMessage(contactGroup.getId());
BdfDictionary localUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, true));
context.checking(new Expectations() {{
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(db).addGroup(txn, contactGroup);
oneOf(db).setGroupVisibility(txn, contact.getId(),
contactGroup.getId(), SHARED);
oneOf(clientHelper).mergeGroupMetadata(txn, contactGroup.getId(),
groupMeta);
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(clientHelper).createMessage(contactGroup.getId(), now,
localUpdateBody);
will(returnValue(localUpdate));
oneOf(clientHelper).addLocalMessage(txn, localUpdate,
localUpdateMeta, true);
}});
}
@Test
public void testRemovesGroupWhenRemovingContact() throws Exception {
context.checking(new Expectations() {{
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(db).removeGroup(txn, contactGroup);
}});
ClientVersioningManagerImpl c = createInstance();
c.removingContact(txn, contact);
}
@Test
public void testStoresClientVersionsAtFirstStartup() throws Exception {
BdfList localVersionsBody =
BdfList.of(BdfList.of(clientId.getString(), 123, 234));
Message localVersions = getMessage(localGroup.getId());
MessageId localUpdateId = new MessageId(getRandomId());
BdfDictionary localUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, true));
BdfList localUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 234, false)), 1L);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
// No client versions have been stored yet
oneOf(db).getMessageIds(txn, localGroup.getId());
will(returnValue(emptyList()));
// Store the client versions
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(clientHelper).createMessage(localGroup.getId(), now,
localVersionsBody);
will(returnValue(localVersions));
oneOf(db).addLocalMessage(txn, localVersions, new Metadata(),
false);
// Inform contacts that client versions have changed
oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
// Find the latest local and remote updates (no remote update)
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(singletonMap(localUpdateId, localUpdateMeta)));
// Load the latest local update
oneOf(clientHelper).getMessageAsList(txn, localUpdateId);
will(returnValue(localUpdateBody));
// Latest local update is up-to-date, no visibilities have changed
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
ClientVersioningManagerImpl c = createInstance();
c.registerClient(clientId, 123, 234, hook);
c.startService();
}
@Test
public void testComparesClientVersionsAtSubsequentStartup()
throws Exception {
MessageId localVersionsId = new MessageId(getRandomId());
BdfList localVersionsBody =
BdfList.of(BdfList.of(clientId.getString(), 123, 234));
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
// Load the old client versions
oneOf(db).getMessageIds(txn, localGroup.getId());
will(returnValue(singletonList(localVersionsId)));
oneOf(clientHelper).getMessageAsList(txn, localVersionsId);
will(returnValue(localVersionsBody));
// Client versions are up-to-date
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
ClientVersioningManagerImpl c = createInstance();
c.registerClient(clientId, 123, 234, hook);
c.startService();
}
@Test
public void testStoresClientVersionsAtSubsequentStartupIfChanged()
throws Exception {
// The client had minor version 234 in the old client versions
BdfList oldLocalVersionsBody =
BdfList.of(BdfList.of(clientId.getString(), 123, 234));
// The client has minor version 345 in the new client versions
BdfList newLocalVersionsBody =
BdfList.of(BdfList.of(clientId.getString(), 123, 345));
// The client had minor version 234 in the old local update
BdfList oldLocalUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 234, false)), 1L);
// The client has minor version 345 in the new local update
BdfList newLocalUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 345, false)), 2L);
MessageId oldLocalVersionsId = new MessageId(getRandomId());
Message newLocalVersions = getMessage(localGroup.getId());
MessageId oldLocalUpdateId = new MessageId(getRandomId());
BdfDictionary oldLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, true));
Message newLocalUpdate = getMessage(contactGroup.getId());
BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
new BdfEntry(MSG_KEY_LOCAL, true));
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
// Load the old client versions
oneOf(db).getMessageIds(txn, localGroup.getId());
will(returnValue(singletonList(oldLocalVersionsId)));
oneOf(clientHelper).getMessageAsList(txn, oldLocalVersionsId);
will(returnValue(oldLocalVersionsBody));
// Delete the old client versions
oneOf(db).removeMessage(txn, oldLocalVersionsId);
// Store the new client versions
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(clientHelper).createMessage(localGroup.getId(), now,
newLocalVersionsBody);
will(returnValue(newLocalVersions));
oneOf(db).addLocalMessage(txn, newLocalVersions, new Metadata(),
false);
// Inform contacts that client versions have changed
oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
// Find the latest local and remote updates (no remote update)
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(singletonMap(oldLocalUpdateId,
oldLocalUpdateMeta)));
// Load the latest local update
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
will(returnValue(oldLocalUpdateBody));
// Delete the latest local update
oneOf(db).deleteMessage(txn, oldLocalUpdateId);
oneOf(db).deleteMessageMetadata(txn, oldLocalUpdateId);
// Store the new local update
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(clientHelper).createMessage(contactGroup.getId(), now,
newLocalUpdateBody);
will(returnValue(newLocalUpdate));
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
newLocalUpdateMeta, true);
// No visibilities have changed
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
ClientVersioningManagerImpl c = createInstance();
c.registerClient(clientId, 123, 345, hook);
c.startService();
}
@Test
public void testActivatesNewClientAtStartupIfAlreadyAdvertisedByContact()
throws Exception {
testActivatesNewClientAtStartup(false, VISIBLE);
}
@Test
public void testActivatesNewClientAtStartupIfAlreadyActivatedByContact()
throws Exception {
testActivatesNewClientAtStartup(true, SHARED);
}
private void testActivatesNewClientAtStartup(boolean remoteActive,
Visibility visibility) throws Exception {
// The client was missing from the old client versions
BdfList oldLocalVersionsBody = new BdfList();
// The client is included in the new client versions
BdfList newLocalVersionsBody =
BdfList.of(BdfList.of(clientId.getString(), 123, 234));
// The client was missing from the old local update
BdfList oldLocalUpdateBody = BdfList.of(new BdfList(), 1L);
// The client was included in the old remote update
BdfList oldRemoteUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 345, remoteActive)), 1L);
// The client is active in the new local update
BdfList newLocalUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 234, true)), 2L);
MessageId oldLocalVersionsId = new MessageId(getRandomId());
Message newLocalVersions = getMessage(localGroup.getId());
MessageId oldLocalUpdateId = new MessageId(getRandomId());
BdfDictionary oldLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, true));
MessageId oldRemoteUpdateId = new MessageId(getRandomId());
BdfDictionary oldRemoteUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, false));
Map<MessageId, BdfDictionary> messageMetadata = new HashMap<>();
messageMetadata.put(oldLocalUpdateId, oldLocalUpdateMeta);
messageMetadata.put(oldRemoteUpdateId, oldRemoteUpdateMeta);
Message newLocalUpdate = getMessage(localGroup.getId());
BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
new BdfEntry(MSG_KEY_LOCAL, true));
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
// Load the old client versions
oneOf(db).getMessageIds(txn, localGroup.getId());
will(returnValue(singletonList(oldLocalVersionsId)));
oneOf(clientHelper).getMessageAsList(txn, oldLocalVersionsId);
will(returnValue(oldLocalVersionsBody));
// Delete the old client versions
oneOf(db).removeMessage(txn, oldLocalVersionsId);
// Store the new client versions
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(clientHelper).createMessage(localGroup.getId(), now,
newLocalVersionsBody);
will(returnValue(newLocalVersions));
oneOf(db).addLocalMessage(txn, newLocalVersions, new Metadata(),
false);
// Inform contacts that client versions have changed
oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
// Find the latest local and remote updates
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(messageMetadata));
// Load the latest local update
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
will(returnValue(oldLocalUpdateBody));
// Load the latest remote update
oneOf(clientHelper).getMessageAsList(txn, oldRemoteUpdateId);
will(returnValue(oldRemoteUpdateBody));
// Delete the latest local update
oneOf(db).deleteMessage(txn, oldLocalUpdateId);
oneOf(db).deleteMessageMetadata(txn, oldLocalUpdateId);
// Store the new local update
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(clientHelper).createMessage(contactGroup.getId(), now,
newLocalUpdateBody);
will(returnValue(newLocalUpdate));
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
newLocalUpdateMeta, true);
// The client's visibility has changed
oneOf(hook).onClientVisibilityChanging(txn, contact, visibility);
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
ClientVersioningManagerImpl c = createInstance();
c.registerClient(clientId, 123, 234, hook);
c.startService();
}
@Test
public void testDeletesObsoleteRemoteUpdate() throws Exception {
Message newRemoteUpdate = getMessage(contactGroup.getId());
BdfList newRemoteUpdateBody = BdfList.of(new BdfList(), 1L);
MessageId oldLocalUpdateId = new MessageId(getRandomId());
BdfDictionary oldLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, true));
MessageId oldRemoteUpdateId = new MessageId(getRandomId());
BdfDictionary oldRemoteUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
new BdfEntry(MSG_KEY_LOCAL, false));
Map<MessageId, BdfDictionary> messageMetadata = new HashMap<>();
messageMetadata.put(oldLocalUpdateId, oldLocalUpdateMeta);
messageMetadata.put(oldRemoteUpdateId, oldRemoteUpdateMeta);
context.checking(new Expectations() {{
oneOf(clientHelper).toList(newRemoteUpdate);
will(returnValue(newRemoteUpdateBody));
// Find the latest local and remote updates
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(messageMetadata));
// Delete the new remote update, which is obsolete
oneOf(db).deleteMessage(txn, newRemoteUpdate.getId());
oneOf(db).deleteMessageMetadata(txn, newRemoteUpdate.getId());
}});
ClientVersioningManagerImpl c = createInstance();
c.registerClient(clientId, 123, 234, hook);
assertFalse(c.incomingMessage(txn, newRemoteUpdate, new Metadata()));
}
@Test
public void testDeletesPreviousRemoteUpdate() throws Exception {
Message newRemoteUpdate = getMessage(contactGroup.getId());
BdfList newRemoteUpdateBody = BdfList.of(new BdfList(), 2L);
MessageId oldLocalUpdateId = new MessageId(getRandomId());
BdfDictionary oldLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, true));
MessageId oldRemoteUpdateId = new MessageId(getRandomId());
BdfDictionary oldRemoteUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, false));
Map<MessageId, BdfDictionary> messageMetadata = new HashMap<>();
messageMetadata.put(oldLocalUpdateId, oldLocalUpdateMeta);
messageMetadata.put(oldRemoteUpdateId, oldRemoteUpdateMeta);
BdfList oldLocalUpdateBody = BdfList.of(new BdfList(), 1L);
BdfList oldRemoteUpdateBody = BdfList.of(new BdfList(), 1L);
context.checking(new Expectations() {{
oneOf(clientHelper).toList(newRemoteUpdate);
will(returnValue(newRemoteUpdateBody));
// Find the latest local and remote updates
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(messageMetadata));
// Load the latest local update
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
will(returnValue(oldLocalUpdateBody));
// Load the latest remote update
oneOf(clientHelper).getMessageAsList(txn, oldRemoteUpdateId);
will(returnValue(oldRemoteUpdateBody));
// Delete the old remote update
oneOf(db).deleteMessage(txn, oldRemoteUpdateId);
oneOf(db).deleteMessageMetadata(txn, oldRemoteUpdateId);
// No states or visibilities have changed
}});
ClientVersioningManagerImpl c = createInstance();
c.registerClient(clientId, 123, 234, hook);
assertFalse(c.incomingMessage(txn, newRemoteUpdate, new Metadata()));
}
@Test
public void testAcceptsFirstRemoteUpdate() throws Exception {
Message newRemoteUpdate = getMessage(contactGroup.getId());
BdfList newRemoteUpdateBody = BdfList.of(new BdfList(), 1L);
MessageId oldLocalUpdateId = new MessageId(getRandomId());
BdfDictionary oldLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, true));
BdfList oldLocalUpdateBody = BdfList.of(new BdfList(), 1L);
context.checking(new Expectations() {{
oneOf(clientHelper).toList(newRemoteUpdate);
will(returnValue(newRemoteUpdateBody));
// Find the latest local and remote updates (no remote update)
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(singletonMap(oldLocalUpdateId,
oldLocalUpdateMeta)));
// Load the latest local update
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
will(returnValue(oldLocalUpdateBody));
// No states or visibilities have changed
}});
ClientVersioningManagerImpl c = createInstance();
c.registerClient(clientId, 123, 234, hook);
assertFalse(c.incomingMessage(txn, newRemoteUpdate, new Metadata()));
}
@Test
public void testActivatesClientOnIncomingMessageWhenAdvertisedByContact()
throws Exception {
testActivatesClientOnIncomingMessage(false, VISIBLE);
}
@Test
public void testActivatesClientOnIncomingMessageWhenActivatedByContact()
throws Exception {
testActivatesClientOnIncomingMessage(true, SHARED);
}
private void testActivatesClientOnIncomingMessage(boolean remoteActive,
Visibility visibility) throws Exception {
// The client was missing from the old remote update
BdfList oldRemoteUpdateBody = BdfList.of(new BdfList(), 1L);
// The client was inactive in the old local update
BdfList oldLocalUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 234, false)), 1L);
// The client is included in the new remote update
BdfList newRemoteUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 234, remoteActive)), 2L);
// The client is active in the new local update
BdfList newLocalUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 234, true)), 2L);
Message newRemoteUpdate = getMessage(contactGroup.getId());
MessageId oldLocalUpdateId = new MessageId(getRandomId());
BdfDictionary oldLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, true));
MessageId oldRemoteUpdateId = new MessageId(getRandomId());
BdfDictionary oldRemoteUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, false));
Map<MessageId, BdfDictionary> messageMetadata = new HashMap<>();
messageMetadata.put(oldLocalUpdateId, oldLocalUpdateMeta);
messageMetadata.put(oldRemoteUpdateId, oldRemoteUpdateMeta);
Message newLocalUpdate = getMessage(contactGroup.getId());
BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
new BdfEntry(MSG_KEY_LOCAL, true));
BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
context.checking(new Expectations() {{
oneOf(clientHelper).toList(newRemoteUpdate);
will(returnValue(newRemoteUpdateBody));
// Find the latest local and remote updates
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(messageMetadata));
// Load the latest local update
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
will(returnValue(oldLocalUpdateBody));
// Load the latest remote update
oneOf(clientHelper).getMessageAsList(txn, oldRemoteUpdateId);
will(returnValue(oldRemoteUpdateBody));
// Delete the old remote update
oneOf(db).deleteMessage(txn, oldRemoteUpdateId);
oneOf(db).deleteMessageMetadata(txn, oldRemoteUpdateId);
// Delete the old local update
oneOf(db).deleteMessage(txn, oldLocalUpdateId);
oneOf(db).deleteMessageMetadata(txn, oldLocalUpdateId);
// Store the new local update
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(clientHelper).createMessage(contactGroup.getId(), now,
newLocalUpdateBody);
will(returnValue(newLocalUpdate));
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
newLocalUpdateMeta, true);
// The client's visibility has changed
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(groupMeta));
oneOf(db).getContact(txn, contact.getId());
will(returnValue(contact));
oneOf(hook).onClientVisibilityChanging(txn, contact, visibility);
}});
ClientVersioningManagerImpl c = createInstance();
c.registerClient(clientId, 123, 234, hook);
assertFalse(c.incomingMessage(txn, newRemoteUpdate, new Metadata()));
}
@Test
public void testDeactivatesClientOnIncomingMessage() throws Exception {
// The client was active in the old local and remote updates
BdfList oldLocalUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 234, true)), 1L);
BdfList oldRemoteUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 234, true)), 1L);
// The client is missing from the new remote update
BdfList newRemoteUpdateBody = BdfList.of(new BdfList(), 2L);
// The client is inactive in the new local update
BdfList newLocalUpdateBody = BdfList.of(BdfList.of(
BdfList.of(clientId.getString(), 123, 234, false)), 2L);
Message newRemoteUpdate = getMessage(contactGroup.getId());
MessageId oldLocalUpdateId = new MessageId(getRandomId());
BdfDictionary oldLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, true));
MessageId oldRemoteUpdateId = new MessageId(getRandomId());
BdfDictionary oldRemoteUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 1L),
new BdfEntry(MSG_KEY_LOCAL, false));
Map<MessageId, BdfDictionary> messageMetadata = new HashMap<>();
messageMetadata.put(oldLocalUpdateId, oldLocalUpdateMeta);
messageMetadata.put(oldRemoteUpdateId, oldRemoteUpdateMeta);
Message newLocalUpdate = getMessage(contactGroup.getId());
BdfDictionary newLocalUpdateMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 2L),
new BdfEntry(MSG_KEY_LOCAL, true));
BdfDictionary groupMeta = BdfDictionary.of(
new BdfEntry(GROUP_KEY_CONTACT_ID, contact.getId().getInt()));
context.checking(new Expectations() {{
oneOf(clientHelper).toList(newRemoteUpdate);
will(returnValue(newRemoteUpdateBody));
// Find the latest local and remote updates
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(messageMetadata));
// Load the latest local update
oneOf(clientHelper).getMessageAsList(txn, oldLocalUpdateId);
will(returnValue(oldLocalUpdateBody));
// Load the latest remote update
oneOf(clientHelper).getMessageAsList(txn, oldRemoteUpdateId);
will(returnValue(oldRemoteUpdateBody));
// Delete the old remote update
oneOf(db).deleteMessage(txn, oldRemoteUpdateId);
oneOf(db).deleteMessageMetadata(txn, oldRemoteUpdateId);
// Delete the old local update
oneOf(db).deleteMessage(txn, oldLocalUpdateId);
oneOf(db).deleteMessageMetadata(txn, oldLocalUpdateId);
// Store the new local update
oneOf(clock).currentTimeMillis();
will(returnValue(now));
oneOf(clientHelper).createMessage(contactGroup.getId(), now,
newLocalUpdateBody);
will(returnValue(newLocalUpdate));
oneOf(clientHelper).addLocalMessage(txn, newLocalUpdate,
newLocalUpdateMeta, true);
// The client's visibility has changed
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
contactGroup.getId());
will(returnValue(groupMeta));
oneOf(db).getContact(txn, contact.getId());
will(returnValue(contact));
oneOf(hook).onClientVisibilityChanging(txn, contact, INVISIBLE);
}});
ClientVersioningManagerImpl c = createInstance();
c.registerClient(clientId, 123, 234, hook);
assertFalse(c.incomingMessage(txn, newRemoteUpdate, new Metadata()));
}
}

View File

@@ -0,0 +1,274 @@
package org.briarproject.bramble.versioning;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.junit.Test;
import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.sync.ClientId.MAX_CLIENT_ID_LENGTH;
import static org.briarproject.bramble.api.versioning.ClientVersioningManager.CLIENT_ID;
import static org.briarproject.bramble.api.versioning.ClientVersioningManager.MAJOR_VERSION;
import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_LOCAL;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
import static org.junit.Assert.assertEquals;
public class ClientVersioningValidatorTest extends BrambleMockTestCase {
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final MetadataEncoder metadataEncoder =
context.mock(MetadataEncoder.class);
private final Clock clock = context.mock(Clock.class);
private final ClientVersioningValidator validator =
new ClientVersioningValidator(clientHelper, metadataEncoder, clock);
private final Group group = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Message message = getMessage(group.getId());
private final ClientId clientId = getClientId();
@Test(expected = FormatException.class)
public void testRejectsTooShortBody() throws Exception {
BdfList body = BdfList.of(new BdfList());
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBody() throws Exception {
BdfList body = BdfList.of(new BdfList(), 123, null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNullStatesList() throws Exception {
BdfList body = BdfList.of(null, 123);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNonListStatesList() throws Exception {
BdfList body = BdfList.of("", 123);
validator.validateMessage(message, group, body);
}
@Test
public void testAcceptsEmptyStatesList() throws Exception {
BdfList body = BdfList.of(new BdfList(), 123);
BdfMessageContext context =
validator.validateMessage(message, group, body);
assertEquals(emptyList(), context.getDependencies());
BdfDictionary expectedMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 123L),
new BdfEntry(MSG_KEY_LOCAL, false));
assertEquals(expectedMeta, context.getDictionary());
}
@Test(expected = FormatException.class)
public void testRejectsNullUpdateVersion() throws Exception {
BdfList body = BdfList.of(new BdfList(), null);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNonLongUpdateVersion() throws Exception {
BdfList body = BdfList.of(new BdfList(), "123");
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNegativeUpdateVersion() throws Exception {
BdfList body = BdfList.of(new BdfList(), -1);
validator.validateMessage(message, group, body);
}
@Test
public void testAcceptsZeroUpdateVersion() throws Exception {
BdfList body = BdfList.of(new BdfList(), 0);
BdfMessageContext context =
validator.validateMessage(message, group, body);
assertEquals(emptyList(), context.getDependencies());
BdfDictionary expectedMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 0L),
new BdfEntry(MSG_KEY_LOCAL, false));
assertEquals(expectedMeta, context.getDictionary());
}
@Test(expected = FormatException.class)
public void testRejectsTooShortClientState() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 123, 234);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongClientState() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 123, 234, true, null);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNullClientId() throws Exception {
BdfList state = BdfList.of(null, 123, 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNonStringClientId() throws Exception {
byte[] id = getRandomBytes(MAX_CLIENT_ID_LENGTH);
BdfList state = BdfList.of(id, 123, 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsTooShortClientId() throws Exception {
BdfList state = BdfList.of("", 123, 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test
public void testAcceptsMinLengthClientId() throws Exception {
BdfList state = BdfList.of(getRandomString(1), 123, 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
BdfMessageContext context =
validator.validateMessage(message, group, body);
assertEquals(emptyList(), context.getDependencies());
BdfDictionary expectedMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 345L),
new BdfEntry(MSG_KEY_LOCAL, false));
assertEquals(expectedMeta, context.getDictionary());
}
@Test(expected = FormatException.class)
public void testRejectsTooLongClientId() throws Exception {
String id = getRandomString(MAX_CLIENT_ID_LENGTH + 1);
BdfList state = BdfList.of(id, 123, 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test
public void testAcceptsMaxLengthClientId() throws Exception {
String id = getRandomString(MAX_CLIENT_ID_LENGTH);
BdfList state = BdfList.of(id, 123, 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
BdfMessageContext context =
validator.validateMessage(message, group, body);
assertEquals(emptyList(), context.getDependencies());
BdfDictionary expectedMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 345L),
new BdfEntry(MSG_KEY_LOCAL, false));
assertEquals(expectedMeta, context.getDictionary());
}
@Test(expected = FormatException.class)
public void testRejectsNullMajorVersion() throws Exception {
BdfList state = BdfList.of(clientId.getString(), null, 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNonLongMajorVersion() throws Exception {
BdfList state = BdfList.of(clientId.getString(), "123", 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNegativeMajorVersion() throws Exception {
BdfList state = BdfList.of(clientId.getString(), -1, 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test
public void testAcceptsZeroMajorVersion() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 0, 234, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
BdfMessageContext context =
validator.validateMessage(message, group, body);
assertEquals(emptyList(), context.getDependencies());
BdfDictionary expectedMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 345L),
new BdfEntry(MSG_KEY_LOCAL, false));
assertEquals(expectedMeta, context.getDictionary());
}
@Test(expected = FormatException.class)
public void testRejectsNullMinorVersion() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 123, null, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNonLongMinorVersion() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 123, "234", true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNegativeMinorVersion() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 123, -1, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test
public void testAcceptsZeroMinorVersion() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 123, 0, true);
BdfList body = BdfList.of(BdfList.of(state), 345);
BdfMessageContext context =
validator.validateMessage(message, group, body);
assertEquals(emptyList(), context.getDependencies());
BdfDictionary expectedMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 345L),
new BdfEntry(MSG_KEY_LOCAL, false));
assertEquals(expectedMeta, context.getDictionary());
}
@Test(expected = FormatException.class)
public void testRejectsNullActiveFlag() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 123, 234, null);
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test(expected = FormatException.class)
public void testRejectsNonBooleanActiveFlag() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 123, 234, "true");
BdfList body = BdfList.of(BdfList.of(state), 345);
validator.validateMessage(message, group, body);
}
@Test
public void testAcceptsNegativeActiveFlag() throws Exception {
BdfList state = BdfList.of(clientId.getString(), 123, 234, false);
BdfList body = BdfList.of(BdfList.of(state), 345);
BdfMessageContext context =
validator.validateMessage(message, group, body);
assertEquals(emptyList(), context.getDependencies());
BdfDictionary expectedMeta = BdfDictionary.of(
new BdfEntry(MSG_KEY_UPDATE_VERSION, 345L),
new BdfEntry(MSG_KEY_LOCAL, false));
assertEquals(expectedMeta, context.getDictionary());
}
}

View File

@@ -22,9 +22,14 @@ public interface BlogManager {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog");
/** /**
* The current version of the blog client. * The current major version of the blog client.
*/ */
int CLIENT_VERSION = 0; int MAJOR_VERSION = 0;
/**
* The current minor version of the blog client.
*/
int MINOR_VERSION = 0;
/** /**
* Adds the given {@link Blog).} * Adds the given {@link Blog).}

View File

@@ -11,7 +11,12 @@ public interface BlogSharingManager extends SharingManager<Blog> {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog.sharing"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog.sharing");
/** /**
* The current version of the blog sharing client. * The current major version of the blog sharing client.
*/ */
int CLIENT_VERSION = 0; int MAJOR_VERSION = 0;
/**
* The current minor version of the blog sharing client.
*/
int MINOR_VERSION = 0;
} }

View File

@@ -16,9 +16,9 @@ public interface FeedManager {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed");
/** /**
* The current version of the RSS feed client. * The current major version of the RSS feed client.
*/ */
int CLIENT_VERSION = 0; int MAJOR_VERSION = 0;
/** /**
* Adds an RSS feed as a new dedicated blog. * Adds an RSS feed as a new dedicated blog.

View File

@@ -23,9 +23,14 @@ public interface ForumManager {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum");
/** /**
* The current version of the forum client. * The current major version of the forum client.
*/ */
int CLIENT_VERSION = 0; int MAJOR_VERSION = 0;
/**
* The current minor version of the forum client.
*/
int MINOR_VERSION = 0;
/** /**
* Subscribes to a forum. * Subscribes to a forum.

View File

@@ -11,7 +11,12 @@ public interface ForumSharingManager extends SharingManager<Forum> {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum.sharing"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum.sharing");
/** /**
* The current version of the forum sharing client. * The current major version of the forum sharing client.
*/ */
int CLIENT_VERSION = 0; int MAJOR_VERSION = 0;
/**
* The current minor version of the forum sharing client.
*/
int MINOR_VERSION = 0;
} }

View File

@@ -21,15 +21,20 @@ public interface IntroductionManager extends ConversationClient {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.introduction"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.introduction");
/** /**
* The current version of the introduction client. * The current major version of the introduction client.
*/ */
int CLIENT_VERSION = 1; int MAJOR_VERSION = 1;
/** /**
* Returns true if both contacts can be introduced at this moment. * Returns true if both contacts can be introduced at this moment.
*/ */
boolean canIntroduce(Contact c1, Contact c2) throws DbException; boolean canIntroduce(Contact c1, Contact c2) throws DbException;
/**
* The current minor version of the introduction client.
*/
int MINOR_VERSION = 0;
/** /**
* Sends two initial introduction messages. * Sends two initial introduction messages.
*/ */

View File

@@ -19,9 +19,14 @@ public interface MessagingManager extends ConversationClient {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.messaging"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.messaging");
/** /**
* The current version of the messaging client. * The current major version of the messaging client.
*/ */
int CLIENT_VERSION = 0; int MAJOR_VERSION = 0;
/**
* The current minor version of the messaging client.
*/
int MINOR_VERSION = 0;
/** /**
* Stores a local private message. * Stores a local private message.

View File

@@ -22,9 +22,14 @@ public interface PrivateGroupManager {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.privategroup"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.privategroup");
/** /**
* The current version of the private group client. * The current major version of the private group client.
*/ */
int CLIENT_VERSION = 0; int MAJOR_VERSION = 0;
/**
* The current minor version of the private group client.
*/
int MINOR_VERSION = 0;
/** /**
* Adds a new private group and joins it. * Adds a new private group and joins it.

View File

@@ -26,9 +26,14 @@ public interface GroupInvitationManager extends ConversationClient {
new ClientId("org.briarproject.briar.privategroup.invitation"); new ClientId("org.briarproject.briar.privategroup.invitation");
/** /**
* The current version of the private group invitation client. * The current major version of the private group invitation client.
*/ */
int CLIENT_VERSION = 0; int MAJOR_VERSION = 0;
/**
* The current minor version of the private group invitation client.
*/
int MINOR_VERSION = 0;
/** /**
* Sends an invitation to share the given private group with the given * Sends an invitation to share the given private group with the given

View File

@@ -15,7 +15,7 @@ import javax.inject.Inject;
import static org.briarproject.bramble.util.ValidationUtils.checkSize; import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID; import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_VERSION; import static org.briarproject.briar.api.blog.BlogManager.MAJOR_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -45,7 +45,7 @@ class BlogFactoryImpl implements BlogFactory {
try { try {
BdfList blog = BdfList.of(clientHelper.toList(a), rssFeed); BdfList blog = BdfList.of(clientHelper.toList(a), rssFeed);
byte[] descriptor = clientHelper.toByteArray(blog); byte[] descriptor = clientHelper.toByteArray(blog);
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION, Group g = groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor); descriptor);
return new Blog(g, a, rssFeed); return new Blog(g, a, rssFeed);
} catch (FormatException e) { } catch (FormatException e) {

View File

@@ -425,7 +425,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
Collection<Group> groups; Collection<Group> groups;
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
groups = db.getGroups(txn, CLIENT_ID); groups = db.getGroups(txn, CLIENT_ID, MAJOR_VERSION);
for (Group g : groups) { for (Group g : groups) {
blogs.add(blogFactory.parseBlog(g)); blogs.add(blogFactory.parseBlog(g));
} }

View File

@@ -18,7 +18,8 @@ import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static org.briarproject.briar.blog.BlogManagerImpl.CLIENT_ID; import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
import static org.briarproject.briar.api.blog.BlogManager.MAJOR_VERSION;
@Module @Module
public class BlogModule { public class BlogModule {
@@ -35,10 +36,10 @@ public class BlogModule {
BlogManager provideBlogManager(BlogManagerImpl blogManager, BlogManager provideBlogManager(BlogManagerImpl blogManager,
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager) { ValidationManager validationManager) {
lifecycleManager.registerClient(blogManager); lifecycleManager.registerClient(blogManager);
contactManager.registerContactHook(blogManager); contactManager.registerContactHook(blogManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, blogManager); validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
blogManager);
return blogManager; return blogManager;
} }
@@ -60,12 +61,11 @@ public class BlogModule {
MessageFactory messageFactory, BlogFactory blogFactory, MessageFactory messageFactory, BlogFactory blogFactory,
ClientHelper clientHelper, MetadataEncoder metadataEncoder, ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock) { Clock clock) {
BlogPostValidator validator = new BlogPostValidator(groupFactory, BlogPostValidator validator = new BlogPostValidator(groupFactory,
messageFactory, blogFactory, clientHelper, metadataEncoder, messageFactory, blogFactory, clientHelper, metadataEncoder,
clock); clock);
validationManager.registerMessageValidator(CLIENT_ID, validator); validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator; return validator;
} }

View File

@@ -42,7 +42,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE;
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_COMMENT_LENGTH; import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_COMMENT_LENGTH;
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH; import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID; import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_VERSION; import static org.briarproject.briar.api.blog.BlogManager.MAJOR_VERSION;
import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_COMMENT; import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_COMMENT;
import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_POST; import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_POST;
import static org.briarproject.briar.api.blog.MessageType.COMMENT; import static org.briarproject.briar.api.blog.MessageType.COMMENT;
@@ -195,7 +195,7 @@ class BlogPostValidator extends BdfMessageValidator {
checkLength(signature, 1, MAX_SIGNATURE_LENGTH); checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
// Get and Validate the Wrapped Message // Get and Validate the Wrapped Message
Group wGroup = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION, Group wGroup = groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor); descriptor);
Blog wBlog = blogFactory.parseBlog(wGroup); Blog wBlog = blogFactory.parseBlog(wGroup);
BdfList wBodyList = BdfList.of(POST.getInt(), content, signature); BdfList wBodyList = BdfList.of(POST.getInt(), content, signature);
@@ -258,7 +258,7 @@ class BlogPostValidator extends BdfMessageValidator {
MessageId parentId = new MessageId(parentIdBytes); MessageId parentId = new MessageId(parentIdBytes);
// Get and Validate the Wrapped Comment // Get and Validate the Wrapped Comment
Group wGroup = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION, Group wGroup = groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor); descriptor);
BdfList wBodyList = BdfList.of(COMMENT.getInt(), comment, pOriginalId, BdfList wBodyList = BdfList.of(COMMENT.getInt(), comment, pOriginalId,
oldId, signature); oldId, signature);

View File

@@ -496,7 +496,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
} }
private Group getLocalGroup() { private Group getLocalGroup() {
return contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION); return contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION);
} }
} }

View File

@@ -18,7 +18,7 @@ import javax.inject.Inject;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH; import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH; import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID; import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_VERSION; import static org.briarproject.briar.api.forum.ForumManager.MAJOR_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -52,7 +52,7 @@ class ForumFactoryImpl implements ForumFactory {
try { try {
BdfList forum = BdfList.of(name, salt); BdfList forum = BdfList.of(name, salt);
byte[] descriptor = clientHelper.toByteArray(forum); byte[] descriptor = clientHelper.toByteArray(forum);
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION, Group g = groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor); descriptor);
return new Forum(g, name, salt); return new Forum(g, name, salt);
} catch (FormatException e) { } catch (FormatException e) {

View File

@@ -188,7 +188,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
Collection<Group> groups; Collection<Group> groups;
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
groups = db.getGroups(txn, CLIENT_ID); groups = db.getGroups(txn, CLIENT_ID, MAJOR_VERSION);
db.commitTransaction(txn); db.commitTransaction(txn);
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);

View File

@@ -15,6 +15,7 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID; import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
import static org.briarproject.briar.api.forum.ForumManager.MAJOR_VERSION;
@Module @Module
public class ForumModule { public class ForumModule {
@@ -30,7 +31,7 @@ public class ForumModule {
@Singleton @Singleton
ForumManager provideForumManager(ForumManagerImpl forumManager, ForumManager provideForumManager(ForumManagerImpl forumManager,
ValidationManager validationManager) { ValidationManager validationManager) {
validationManager.registerIncomingMessageHook(CLIENT_ID, validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
forumManager); forumManager);
return forumManager; return forumManager;
} }
@@ -53,7 +54,8 @@ public class ForumModule {
MetadataEncoder metadataEncoder, Clock clock) { MetadataEncoder metadataEncoder, Clock clock) {
ForumPostValidator validator = new ForumPostValidator(clientHelper, ForumPostValidator validator = new ForumPostValidator(clientHelper,
metadataEncoder, clock); metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validator); validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator; return validator;
} }

View File

@@ -29,7 +29,7 @@ import static org.briarproject.briar.api.introduction.IntroductionConstants.LABE
import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_BOB_MAC_KEY; import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_BOB_MAC_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_MASTER_KEY; import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_MASTER_KEY;
import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_SESSION_ID; import static org.briarproject.briar.api.introduction.IntroductionConstants.LABEL_SESSION_ID;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION; import static org.briarproject.briar.api.introduction.IntroductionManager.MAJOR_VERSION;
import static org.briarproject.briar.introduction.IntroduceeSession.Local; import static org.briarproject.briar.introduction.IntroduceeSession.Local;
@Immutable @Immutable
@@ -94,7 +94,7 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
LABEL_MASTER_KEY, LABEL_MASTER_KEY,
remoteEphemeralPublicKey, remoteEphemeralPublicKey,
keyPair, keyPair,
new byte[] {CLIENT_VERSION}, new byte[] {MAJOR_VERSION},
alice ? publicKey : remotePublicKey, alice ? publicKey : remotePublicKey,
alice ? remotePublicKey : publicKey alice ? remotePublicKey : publicKey
); );

View File

@@ -20,10 +20,13 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client; import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.introduction.IntroductionManager; import org.briarproject.briar.api.introduction.IntroductionManager;
@@ -44,7 +47,6 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.briar.api.introduction.Role.INTRODUCEE; import static org.briarproject.briar.api.introduction.Role.INTRODUCEE;
import static org.briarproject.briar.api.introduction.Role.INTRODUCER; import static org.briarproject.briar.api.introduction.Role.INTRODUCER;
import static org.briarproject.briar.introduction.IntroducerState.START; import static org.briarproject.briar.introduction.IntroducerState.START;
@@ -59,8 +61,10 @@ import static org.briarproject.briar.introduction.MessageType.REQUEST;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class IntroductionManagerImpl extends ConversationClientImpl class IntroductionManagerImpl extends ConversationClientImpl
implements IntroductionManager, Client, ContactHook { implements IntroductionManager, Client, ContactHook,
ClientVersioningHook {
private final ClientVersioningManager clientVersioningManager;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
private final ContactManager contactManager; private final ContactManager contactManager;
private final MessageParser messageParser; private final MessageParser messageParser;
@@ -77,6 +81,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
IntroductionManagerImpl( IntroductionManagerImpl(
DatabaseComponent db, DatabaseComponent db,
ClientHelper clientHelper, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MetadataParser metadataParser,
MessageTracker messageTracker, MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory, ContactGroupFactory contactGroupFactory,
@@ -89,6 +94,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
IntroductionCrypto crypto, IntroductionCrypto crypto,
IdentityManager identityManager) { IdentityManager identityManager) {
super(db, clientHelper, metadataParser, messageTracker); super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
this.contactManager = contactManager; this.contactManager = contactManager;
this.messageParser = messageParser; this.messageParser = messageParser;
@@ -99,7 +105,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
this.crypto = crypto; this.crypto = crypto;
this.identityManager = identityManager; this.identityManager = identityManager;
this.localGroup = this.localGroup =
contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION); contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION);
} }
@Override @Override
@@ -112,13 +118,14 @@ class IntroductionManagerImpl extends ConversationClientImpl
} }
@Override @Override
// TODO adapt to use upcoming ClientVersioning client
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact // Create a group to share with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c);
// Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED); // Apply the client's visibility to the contact group
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// Attach the contact ID to the group // Attach the contact ID to the group
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt()); meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
@@ -138,10 +145,18 @@ class IntroductionManagerImpl extends ConversationClientImpl
db.removeGroup(txn, getContactGroup(c)); db.removeGroup(txn, getContactGroup(c));
} }
@Override
public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException {
// Apply the client's visibility to the contact group
Group g = getContactGroup(c);
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
}
@Override @Override
public Group getContactGroup(Contact c) { public Group getContactGroup(Contact c) {
return contactGroupFactory return contactGroupFactory
.createContactGroup(CLIENT_ID, CLIENT_VERSION, c); .createContactGroup(CLIENT_ID, MAJOR_VERSION, c);
} }
@Override @Override

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.ValidationManager; import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.introduction.IntroductionManager; import org.briarproject.briar.api.introduction.IntroductionManager;
import org.briarproject.briar.api.messaging.ConversationManager; import org.briarproject.briar.api.messaging.ConversationManager;
@@ -16,6 +17,8 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID; import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
import static org.briarproject.briar.api.introduction.IntroductionManager.MAJOR_VERSION;
import static org.briarproject.briar.api.introduction.IntroductionManager.MINOR_VERSION;
@Module @Module
public class IntroductionModule { public class IntroductionModule {
@@ -32,13 +35,11 @@ public class IntroductionModule {
IntroductionValidator provideValidator(ValidationManager validationManager, IntroductionValidator provideValidator(ValidationManager validationManager,
MessageEncoder messageEncoder, MetadataEncoder metadataEncoder, MessageEncoder messageEncoder, MetadataEncoder metadataEncoder,
ClientHelper clientHelper, Clock clock) { ClientHelper clientHelper, Clock clock) {
IntroductionValidator introductionValidator = IntroductionValidator introductionValidator =
new IntroductionValidator(messageEncoder, clientHelper, new IntroductionValidator(messageEncoder, clientHelper,
metadataEncoder, clock); metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
introductionValidator); introductionValidator);
return introductionValidator; return introductionValidator;
} }
@@ -48,13 +49,15 @@ public class IntroductionModule {
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager, ValidationManager validationManager,
ConversationManager conversationManager, ConversationManager conversationManager,
ClientVersioningManager clientVersioningManager,
IntroductionManagerImpl introductionManager) { IntroductionManagerImpl introductionManager) {
lifecycleManager.registerClient(introductionManager); lifecycleManager.registerClient(introductionManager);
contactManager.registerContactHook(introductionManager); contactManager.registerContactHook(introductionManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, validationManager.registerIncomingMessageHook(CLIENT_ID,
introductionManager); MAJOR_VERSION, introductionManager);
conversationManager.registerConversationClient(introductionManager); conversationManager.registerConversationClient(introductionManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, introductionManager);
return introductionManager; return introductionManager;
} }

View File

@@ -15,10 +15,13 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client; import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage; import org.briarproject.briar.api.messaging.PrivateMessage;
@@ -33,21 +36,23 @@ import java.util.Map;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ; import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class MessagingManagerImpl extends ConversationClientImpl class MessagingManagerImpl extends ConversationClientImpl
implements MessagingManager, Client, ContactHook { implements MessagingManager, Client, ContactHook, ClientVersioningHook {
private final ClientVersioningManager clientVersioningManager;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
@Inject @Inject
MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper, MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageTracker messageTracker, MetadataParser metadataParser, MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory) { ContactGroupFactory contactGroupFactory) {
super(db, clientHelper, metadataParser, messageTracker); super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
} }
@@ -55,36 +60,36 @@ class MessagingManagerImpl extends ConversationClientImpl
public void createLocalState(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
// Create a local group to indicate that we've set this client up // Create a local group to indicate that we've set this client up
Group localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID, Group localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
CLIENT_VERSION); MAJOR_VERSION);
if (db.containsGroup(txn, localGroup.getId())) return; if (db.containsGroup(txn, localGroup.getId())) return;
db.addGroup(txn, localGroup); db.addGroup(txn, localGroup);
// Ensure we've set things up for any pre-existing contacts // Set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c); for (Contact c : db.getContacts(txn)) addingContact(txn, c);
} }
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact
Group g = getContactGroup(c);
db.addGroup(txn, g);
// Apply the client's visibility to the contact group
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// Attach the contact ID to the group
BdfDictionary d = new BdfDictionary();
d.put("contactId", c.getId().getInt());
try { try {
// Create a group to share with the contact
Group g = getContactGroup(c);
// Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact
db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group
BdfDictionary d = new BdfDictionary();
d.put("contactId", c.getId().getInt());
clientHelper.mergeGroupMetadata(txn, g.getId(), d); clientHelper.mergeGroupMetadata(txn, g.getId(), d);
} catch (FormatException e) { } catch (FormatException e) {
throw new RuntimeException(e); throw new AssertionError(e);
} }
} }
@Override @Override
public Group getContactGroup(Contact c) { public Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
CLIENT_VERSION, c); MAJOR_VERSION, c);
} }
@Override @Override
@@ -92,6 +97,14 @@ class MessagingManagerImpl extends ConversationClientImpl
db.removeGroup(txn, getContactGroup(c)); db.removeGroup(txn, getContactGroup(c));
} }
@Override
public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException {
// Apply the client's visibility to the contact group
Group g = getContactGroup(c);
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
}
@Override @Override
protected boolean incomingMessage(Transaction txn, Message m, BdfList body, protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary meta) throws DbException, FormatException { BdfDictionary meta) throws DbException, FormatException {

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.ValidationManager; import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.messaging.ConversationManager; import org.briarproject.briar.api.messaging.ConversationManager;
import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessageFactory; import org.briarproject.briar.api.messaging.PrivateMessageFactory;
@@ -16,7 +17,9 @@ import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static org.briarproject.briar.messaging.MessagingManagerImpl.CLIENT_ID; import static org.briarproject.briar.api.messaging.MessagingManager.CLIENT_ID;
import static org.briarproject.briar.api.messaging.MessagingManager.MAJOR_VERSION;
import static org.briarproject.briar.api.messaging.MessagingManager.MINOR_VERSION;
@Module @Module
public class MessagingModule { public class MessagingModule {
@@ -43,7 +46,8 @@ public class MessagingModule {
Clock clock) { Clock clock) {
PrivateMessageValidator validator = new PrivateMessageValidator( PrivateMessageValidator validator = new PrivateMessageValidator(
clientHelper, metadataEncoder, clock); clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validator); validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator; return validator;
} }
@@ -52,12 +56,15 @@ public class MessagingModule {
MessagingManager getMessagingManager(LifecycleManager lifecycleManager, MessagingManager getMessagingManager(LifecycleManager lifecycleManager,
ContactManager contactManager, ValidationManager validationManager, ContactManager contactManager, ValidationManager validationManager,
ConversationManager conversationManager, ConversationManager conversationManager,
ClientVersioningManager clientVersioningManager,
MessagingManagerImpl messagingManager) { MessagingManagerImpl messagingManager) {
lifecycleManager.registerClient(messagingManager); lifecycleManager.registerClient(messagingManager);
contactManager.registerContactHook(messagingManager); contactManager.registerContactHook(messagingManager);
validationManager validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
.registerIncomingMessageHook(CLIENT_ID, messagingManager); messagingManager);
conversationManager.registerConversationClient(messagingManager); conversationManager.registerConversationClient(messagingManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, messagingManager);
return messagingManager; return messagingManager;
} }

View File

@@ -21,7 +21,7 @@ import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH; import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH; import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID; import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_VERSION; import static org.briarproject.briar.api.privategroup.PrivateGroupManager.MAJOR_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -57,7 +57,7 @@ class PrivateGroupFactoryImpl implements PrivateGroupFactory {
BdfList creatorList = clientHelper.toList(creator); BdfList creatorList = clientHelper.toList(creator);
BdfList group = BdfList.of(creatorList, name, salt); BdfList group = BdfList.of(creatorList, name, salt);
byte[] descriptor = clientHelper.toByteArray(group); byte[] descriptor = clientHelper.toByteArray(group);
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION, Group g = groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor); descriptor);
return new PrivateGroup(g, name, creator, salt); return new PrivateGroup(g, name, creator, salt);
} catch (FormatException e) { } catch (FormatException e) {

View File

@@ -271,7 +271,7 @@ class PrivateGroupManagerImpl extends BdfIncomingMessageHook
Collection<Group> groups; Collection<Group> groups;
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
groups = db.getGroups(txn, CLIENT_ID); groups = db.getGroups(txn, CLIENT_ID, MAJOR_VERSION);
db.commitTransaction(txn); db.commitTransaction(txn);
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);

View File

@@ -16,6 +16,7 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID; import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.MAJOR_VERSION;
@Module @Module
public class PrivateGroupModule { public class PrivateGroupModule {
@@ -32,7 +33,8 @@ public class PrivateGroupModule {
PrivateGroupManager provideGroupManager( PrivateGroupManager provideGroupManager(
PrivateGroupManagerImpl groupManager, PrivateGroupManagerImpl groupManager,
ValidationManager validationManager) { ValidationManager validationManager) {
validationManager.registerIncomingMessageHook(CLIENT_ID, groupManager); validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
groupManager);
return groupManager; return groupManager;
} }
@@ -58,7 +60,8 @@ public class PrivateGroupModule {
GroupMessageValidator validator = new GroupMessageValidator( GroupMessageValidator validator = new GroupMessageValidator(
privateGroupFactory, clientHelper, metadataEncoder, clock, privateGroupFactory, clientHelper, metadataEncoder, clock,
groupInvitationFactory); groupInvitationFactory);
validationManager.registerMessageValidator(CLIENT_ID, validator); validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator; return validator;
} }

View File

@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.privategroup.GroupMessage; import org.briarproject.briar.api.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
@@ -28,6 +29,8 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.MAJOR_VERSION;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.GROUP_KEY_CONTACT_ID; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT; import static org.briarproject.briar.privategroup.invitation.MessageType.ABORT;
import static org.briarproject.briar.privategroup.invitation.MessageType.INVITE; import static org.briarproject.briar.privategroup.invitation.MessageType.INVITE;
@@ -45,6 +48,7 @@ abstract class AbstractProtocolEngine<S extends Session>
protected final PrivateGroupFactory privateGroupFactory; protected final PrivateGroupFactory privateGroupFactory;
protected final MessageTracker messageTracker; protected final MessageTracker messageTracker;
private final ClientVersioningManager clientVersioningManager;
private final GroupMessageFactory groupMessageFactory; private final GroupMessageFactory groupMessageFactory;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final MessageParser messageParser; private final MessageParser messageParser;
@@ -52,6 +56,7 @@ abstract class AbstractProtocolEngine<S extends Session>
private final Clock clock; private final Clock clock;
AbstractProtocolEngine(DatabaseComponent db, ClientHelper clientHelper, AbstractProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory, PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
@@ -60,6 +65,7 @@ abstract class AbstractProtocolEngine<S extends Session>
Clock clock) { Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.clientVersioningManager = clientVersioningManager;
this.privateGroupManager = privateGroupManager; this.privateGroupManager = privateGroupManager;
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
this.groupMessageFactory = groupMessageFactory; this.groupMessageFactory = groupMessageFactory;
@@ -90,10 +96,14 @@ abstract class AbstractProtocolEngine<S extends Session>
return expected != null && dependency.equals(expected); return expected != null && dependency.equals(expected);
} }
void setPrivateGroupVisibility(Transaction txn, S session, Visibility v) void setPrivateGroupVisibility(Transaction txn, S session,
throws DbException, FormatException { Visibility preferred) throws DbException, FormatException {
// Apply min of preferred visibility and client's visibility
ContactId contactId = getContactId(txn, session.getContactGroupId()); ContactId contactId = getContactId(txn, session.getContactGroupId());
db.setGroupVisibility(txn, contactId, session.getPrivateGroupId(), v); Visibility client = clientVersioningManager.getClientVisibility(txn,
contactId, CLIENT_ID, MAJOR_VERSION);
Visibility min = Visibility.min(preferred, client);
db.setGroupVisibility(txn, contactId, session.getPrivateGroupId(), min);
} }
Message sendInviteMessage(Transaction txn, S session, Message sendInviteMessage(Transaction txn, S session,

View File

@@ -10,6 +10,7 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
@@ -36,15 +37,16 @@ import static org.briarproject.briar.privategroup.invitation.CreatorState.START;
class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> { class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
CreatorProtocolEngine(DatabaseComponent db, ClientHelper clientHelper, CreatorProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory, PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
IdentityManager identityManager, MessageParser messageParser, IdentityManager identityManager, MessageParser messageParser,
MessageEncoder messageEncoder, MessageTracker messageTracker, MessageEncoder messageEncoder, MessageTracker messageTracker,
Clock clock) { Clock clock) {
super(db, clientHelper, privateGroupManager, privateGroupFactory, super(db, clientHelper, clientVersioningManager, privateGroupManager,
groupMessageFactory, identityManager, messageParser, privateGroupFactory, groupMessageFactory, identityManager,
messageEncoder, messageTracker, clock); messageParser, messageEncoder, messageTracker, clock);
} }
@Override @Override

View File

@@ -2,19 +2,30 @@ package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group.Visibility;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
enum CreatorState implements State { enum CreatorState implements State {
START(0), INVITED(1), JOINED(2), LEFT(3), DISSOLVED(4), ERROR(5); START(0, INVISIBLE),
INVITED(1, INVISIBLE),
JOINED(2, SHARED),
LEFT(3, INVISIBLE),
DISSOLVED(4, INVISIBLE),
ERROR(5, INVISIBLE);
private final int value; private final int value;
private final Visibility visibility;
CreatorState(int value) { CreatorState(int value, Visibility visibility) {
this.value = value; this.value = value;
this.visibility = visibility;
} }
@Override @Override
@@ -22,6 +33,11 @@ enum CreatorState implements State {
return value; return value;
} }
@Override
public Visibility getVisibility() {
return visibility;
}
static CreatorState fromValue(int value) throws FormatException { static CreatorState fromValue(int value) throws FormatException {
for (CreatorState s : values()) if (s.value == value) return s; for (CreatorState s : values()) if (s.value == value) return s;
throw new FormatException(); throw new FormatException();

View File

@@ -15,6 +15,7 @@ interface GroupInvitationConstants {
String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted"; String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted";
// Session keys // Session keys
String SESSION_KEY_IS_SESSION = "isSession";
String SESSION_KEY_SESSION_ID = "sessionId"; String SESSION_KEY_SESSION_ID = "sessionId";
String SESSION_KEY_PRIVATE_GROUP_ID = "privateGroupId"; String SESSION_KEY_PRIVATE_GROUP_ID = "privateGroupId";
String SESSION_KEY_LAST_LOCAL_MESSAGE_ID = "lastLocalMessageId"; String SESSION_KEY_LAST_LOCAL_MESSAGE_ID = "lastLocalMessageId";

View File

@@ -17,7 +17,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID; import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.CLIENT_VERSION; import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.MAJOR_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -53,7 +53,7 @@ class GroupInvitationFactoryImpl implements GroupInvitationFactory {
public BdfList createInviteToken(AuthorId creatorId, AuthorId memberId, public BdfList createInviteToken(AuthorId creatorId, AuthorId memberId,
GroupId privateGroupId, long timestamp) { GroupId privateGroupId, long timestamp) {
Group contactGroup = contactGroupFactory.createContactGroup(CLIENT_ID, Group contactGroup = contactGroupFactory.createContactGroup(CLIENT_ID,
CLIENT_VERSION, creatorId, memberId); MAJOR_VERSION, creatorId, memberId);
return BdfList.of( return BdfList.of(
timestamp, timestamp,
contactGroup.getId(), contactGroup.getId(),

View File

@@ -17,10 +17,13 @@ import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client; import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.privategroup.PrivateGroup; import org.briarproject.briar.api.privategroup.PrivateGroup;
@@ -36,6 +39,7 @@ import org.briarproject.briar.client.ConversationClientImpl;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -59,8 +63,9 @@ import static org.briarproject.briar.privategroup.invitation.Role.PEER;
@NotNullByDefault @NotNullByDefault
class GroupInvitationManagerImpl extends ConversationClientImpl class GroupInvitationManagerImpl extends ConversationClientImpl
implements GroupInvitationManager, Client, ContactHook, implements GroupInvitationManager, Client, ContactHook,
PrivateGroupHook { PrivateGroupHook, ClientVersioningHook {
private final ClientVersioningManager clientVersioningManager;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
private final PrivateGroupFactory privateGroupFactory; private final PrivateGroupFactory privateGroupFactory;
private final PrivateGroupManager privateGroupManager; private final PrivateGroupManager privateGroupManager;
@@ -73,8 +78,9 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
@Inject @Inject
GroupInvitationManagerImpl(DatabaseComponent db, GroupInvitationManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, MetadataParser metadataParser, ClientHelper clientHelper,
MessageTracker messageTracker, ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory, ContactGroupFactory contactGroupFactory,
PrivateGroupFactory privateGroupFactory, PrivateGroupFactory privateGroupFactory,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
@@ -82,6 +88,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
SessionEncoder sessionEncoder, SessionEncoder sessionEncoder,
ProtocolEngineFactory engineFactory) { ProtocolEngineFactory engineFactory) {
super(db, clientHelper, metadataParser, messageTracker); super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
this.privateGroupManager = privateGroupManager; this.privateGroupManager = privateGroupManager;
@@ -97,10 +104,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
public void createLocalState(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
// Create a local group to indicate that we've set this client up // Create a local group to indicate that we've set this client up
Group localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID, Group localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
CLIENT_VERSION); MAJOR_VERSION);
if (db.containsGroup(txn, localGroup.getId())) return; if (db.containsGroup(txn, localGroup.getId())) return;
db.addGroup(txn, localGroup); db.addGroup(txn, localGroup);
// Ensure we've set things up for any pre-existing contacts // Set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c); for (Contact c : db.getContacts(txn)) addingContact(txn, c);
} }
@@ -108,11 +115,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact // Create a group to share with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c);
// Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact
db.addGroup(txn, g); db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED); Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), CLIENT_ID, MAJOR_VERSION);
db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// Attach the contact ID to the group // Attach the contact ID to the group
BdfDictionary meta = new BdfDictionary(); BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt()); meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
@@ -122,7 +128,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
throw new AssertionError(e); throw new AssertionError(e);
} }
// If the contact belongs to any private groups, create a peer session // If the contact belongs to any private groups, create a peer session
for (Group pg : db.getGroups(txn, PrivateGroupManager.CLIENT_ID)) { for (Group pg : db.getGroups(txn, PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION)) {
if (privateGroupManager.isMember(txn, pg.getId(), c.getAuthor())) if (privateGroupManager.isMember(txn, pg.getId(), c.getAuthor()))
addingMember(txn, pg.getId(), c); addingMember(txn, pg.getId(), c);
} }
@@ -137,7 +144,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
@Override @Override
public Group getContactGroup(Contact c) { public Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
CLIENT_VERSION, c); MAJOR_VERSION, c);
} }
@Override @Override
@@ -461,8 +468,13 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
SessionId sessionId = getSessionId(privateGroupId); SessionId sessionId = getSessionId(privateGroupId);
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION);
StoredSession ss = getSession(txn, contactGroupId, sessionId); StoredSession ss = getSession(txn, contactGroupId, sessionId);
db.commitTransaction(txn); db.commitTransaction(txn);
// The group can't be shared unless the contact supports the client
if (client != SHARED) return false;
// If there's no session, the contact can be invited // If there's no session, the contact can be invited
if (ss == null) return true; if (ss == null) return true;
// If the session's in the start state, the contact can be invited // If the session's in the start state, the contact can be invited
@@ -566,6 +578,65 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
} }
} }
@Override
public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException {
// Apply the client's visibility to the contact group
Group g = getContactGroup(c);
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
}
ClientVersioningHook getPrivateGroupClientVersioningHook() {
return this::onPrivateGroupClientVisibilityChanging;
}
private void onPrivateGroupClientVisibilityChanging(Transaction txn,
Contact c, Visibility client) throws DbException {
try {
Collection<Group> shareables =
db.getGroups(txn, PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION);
Map<GroupId, Visibility> m = getPreferredVisibilities(txn, c);
for (Group g : shareables) {
Visibility preferred = m.get(g.getId());
if (preferred == null) continue; // No session for this group
// Apply min of preferred visibility and client's visibility
Visibility min = Visibility.min(preferred, client);
db.setGroupVisibility(txn, c.getId(), g.getId(), min);
}
} catch (FormatException e) {
throw new DbException(e);
}
}
private Map<GroupId, Visibility> getPreferredVisibilities(Transaction txn,
Contact c) throws DbException, FormatException {
GroupId contactGroupId = getContactGroup(c).getId();
BdfDictionary query = sessionParser.getAllSessionsQuery();
Map<MessageId, BdfDictionary> results = clientHelper
.getMessageMetadataAsDictionary(txn, contactGroupId, query);
Map<GroupId, Visibility> m = new HashMap<>();
for (BdfDictionary d : results.values()) {
Role role = sessionParser.getRole(d);
if (role == CREATOR) {
CreatorSession s =
sessionParser.parseCreatorSession(contactGroupId, d);
m.put(s.getPrivateGroupId(), s.getState().getVisibility());
} else if (role == INVITEE) {
InviteeSession s =
sessionParser.parseInviteeSession(contactGroupId, d);
m.put(s.getPrivateGroupId(), s.getState().getVisibility());
} else if (role == PEER) {
PeerSession s =
sessionParser.parsePeerSession(contactGroupId, d);
m.put(s.getPrivateGroupId(), s.getState().getVisibility());
} else {
throw new AssertionError();
}
}
return m;
}
private static class StoredSession { private static class StoredSession {
private final MessageId storageId; private final MessageId storageId;

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.ValidationManager; import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.messaging.ConversationManager; import org.briarproject.briar.api.messaging.ConversationManager;
import org.briarproject.briar.api.privategroup.PrivateGroupFactory; import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
@@ -19,6 +20,8 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID; import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.MAJOR_VERSION;
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.MINOR_VERSION;
@Module @Module
public class GroupInvitationModule { public class GroupInvitationModule {
@@ -37,13 +40,22 @@ public class GroupInvitationModule {
LifecycleManager lifecycleManager, LifecycleManager lifecycleManager,
ValidationManager validationManager, ContactManager contactManager, ValidationManager validationManager, ContactManager contactManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
ConversationManager conversationManager) { ConversationManager conversationManager,
ClientVersioningManager clientVersioningManager) {
lifecycleManager.registerClient(groupInvitationManager); lifecycleManager.registerClient(groupInvitationManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
groupInvitationManager); groupInvitationManager);
contactManager.registerContactHook(groupInvitationManager); contactManager.registerContactHook(groupInvitationManager);
privateGroupManager.registerPrivateGroupHook(groupInvitationManager); privateGroupManager.registerPrivateGroupHook(groupInvitationManager);
conversationManager.registerConversationClient(groupInvitationManager); conversationManager.registerConversationClient(groupInvitationManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, groupInvitationManager);
// The group invitation manager handles client visibility changes for
// the private group manager
clientVersioningManager.registerClient(PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION,
PrivateGroupManager.MINOR_VERSION,
groupInvitationManager.getPrivateGroupClientVersioningHook());
return groupInvitationManager; return groupInvitationManager;
} }
@@ -57,7 +69,8 @@ public class GroupInvitationModule {
GroupInvitationValidator validator = new GroupInvitationValidator( GroupInvitationValidator validator = new GroupInvitationValidator(
clientHelper, metadataEncoder, clock, privateGroupFactory, clientHelper, metadataEncoder, clock, privateGroupFactory,
messageEncoder); messageEncoder);
validationManager.registerMessageValidator(CLIENT_ID, validator); validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator; return validator;
} }

View File

@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
@@ -41,15 +42,16 @@ import static org.briarproject.briar.privategroup.invitation.InviteeState.START;
class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> { class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
InviteeProtocolEngine(DatabaseComponent db, ClientHelper clientHelper, InviteeProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory, PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
IdentityManager identityManager, MessageParser messageParser, IdentityManager identityManager, MessageParser messageParser,
MessageEncoder messageEncoder, MessageTracker messageTracker, MessageEncoder messageEncoder, MessageTracker messageTracker,
Clock clock) { Clock clock) {
super(db, clientHelper, privateGroupManager, privateGroupFactory, super(db, clientHelper, clientVersioningManager, privateGroupManager,
groupMessageFactory, identityManager, messageParser, privateGroupFactory, groupMessageFactory, identityManager,
messageEncoder, messageTracker, clock); messageParser, messageEncoder, messageTracker, clock);
} }
@Override @Override
@@ -212,6 +214,12 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
throws DbException { throws DbException {
// Send a LEAVE message // Send a LEAVE message
Message sent = sendLeaveMessage(txn, s, false); Message sent = sendLeaveMessage(txn, s, false);
try {
// Make the private group invisible to the contact
setPrivateGroupVisibility(txn, s, INVISIBLE);
} catch (FormatException e) {
throw new DbException(e); // Invalid group metadata
}
// Move to the LEFT state // Move to the LEFT state
return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(), return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(),
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(), sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),

View File

@@ -2,20 +2,32 @@ package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group.Visibility;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
enum InviteeState implements State { enum InviteeState implements State {
START(0), INVITED(1), ACCEPTED(2), JOINED(3), LEFT(4), DISSOLVED(5), START(0, INVISIBLE),
ERROR(6); INVITED(1, INVISIBLE),
ACCEPTED(2, VISIBLE),
JOINED(3, SHARED),
LEFT(4, INVISIBLE),
DISSOLVED(5, INVISIBLE),
ERROR(6, INVISIBLE);
private final int value; private final int value;
private final Visibility visibility;
InviteeState(int value) { InviteeState(int value, Visibility visibility) {
this.value = value; this.value = value;
this.visibility = visibility;
} }
@Override @Override
@@ -23,6 +35,11 @@ enum InviteeState implements State {
return value; return value;
} }
@Override
public Visibility getVisibility() {
return visibility;
}
static InviteeState fromValue(int value) throws FormatException { static InviteeState fromValue(int value) throws FormatException {
for (InviteeState s : values()) if (s.value == value) return s; for (InviteeState s : values()) if (s.value == value) return s;
throw new FormatException(); throw new FormatException();

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
@@ -36,15 +37,16 @@ import static org.briarproject.briar.privategroup.invitation.PeerState.START;
class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> { class PeerProtocolEngine extends AbstractProtocolEngine<PeerSession> {
PeerProtocolEngine(DatabaseComponent db, ClientHelper clientHelper, PeerProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory, PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
IdentityManager identityManager, MessageParser messageParser, IdentityManager identityManager, MessageParser messageParser,
MessageEncoder messageEncoder, MessageTracker messageTracker, MessageEncoder messageEncoder, MessageTracker messageTracker,
Clock clock) { Clock clock) {
super(db, clientHelper, privateGroupManager, privateGroupFactory, super(db, clientHelper, clientVersioningManager, privateGroupManager,
groupMessageFactory, identityManager, messageParser, privateGroupFactory, groupMessageFactory, identityManager,
messageEncoder, messageTracker, clock); messageParser, messageEncoder, messageTracker, clock);
} }
@Override @Override

View File

@@ -2,20 +2,32 @@ package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group.Visibility;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
enum PeerState implements State { enum PeerState implements State {
START(0), AWAIT_MEMBER(1), NEITHER_JOINED(2), LOCAL_JOINED(3), START(0, INVISIBLE),
BOTH_JOINED(4), LOCAL_LEFT(5), ERROR(6); AWAIT_MEMBER(1, INVISIBLE),
NEITHER_JOINED(2, INVISIBLE),
LOCAL_JOINED(3, VISIBLE),
BOTH_JOINED(4, SHARED),
LOCAL_LEFT(5, INVISIBLE),
ERROR(6, INVISIBLE);
private final int value; private final int value;
private final Visibility visibility;
PeerState(int value) { PeerState(int value, Visibility visibility) {
this.value = value; this.value = value;
this.visibility = visibility;
} }
@Override @Override
@@ -23,6 +35,11 @@ enum PeerState implements State {
return value; return value;
} }
@Override
public Visibility getVisibility() {
return visibility;
}
static PeerState fromValue(int value) throws FormatException { static PeerState fromValue(int value) throws FormatException {
for (PeerState s : values()) if (s.value == value) return s; for (PeerState s : values()) if (s.value == value) return s;
throw new FormatException(); throw new FormatException();

View File

@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.privategroup.GroupMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupFactory; import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
@@ -19,6 +20,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final ClientVersioningManager clientVersioningManager;
private final PrivateGroupManager privateGroupManager; private final PrivateGroupManager privateGroupManager;
private final PrivateGroupFactory privateGroupFactory; private final PrivateGroupFactory privateGroupFactory;
private final GroupMessageFactory groupMessageFactory; private final GroupMessageFactory groupMessageFactory;
@@ -30,6 +32,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
@Inject @Inject
ProtocolEngineFactoryImpl(DatabaseComponent db, ClientHelper clientHelper, ProtocolEngineFactoryImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory, PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory, GroupMessageFactory groupMessageFactory,
@@ -38,6 +41,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
Clock clock) { Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.clientVersioningManager = clientVersioningManager;
this.privateGroupManager = privateGroupManager; this.privateGroupManager = privateGroupManager;
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
this.groupMessageFactory = groupMessageFactory; this.groupMessageFactory = groupMessageFactory;
@@ -50,21 +54,24 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
@Override @Override
public ProtocolEngine<CreatorSession> createCreatorEngine() { public ProtocolEngine<CreatorSession> createCreatorEngine() {
return new CreatorProtocolEngine(db, clientHelper, privateGroupManager, return new CreatorProtocolEngine(db, clientHelper,
clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager, privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, clock); messageParser, messageEncoder, messageTracker, clock);
} }
@Override @Override
public ProtocolEngine<InviteeSession> createInviteeEngine() { public ProtocolEngine<InviteeSession> createInviteeEngine() {
return new InviteeProtocolEngine(db, clientHelper, privateGroupManager, return new InviteeProtocolEngine(db, clientHelper,
clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager, privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, clock); messageParser, messageEncoder, messageTracker, clock);
} }
@Override @Override
public ProtocolEngine<PeerSession> createPeerEngine() { public ProtocolEngine<PeerSession> createPeerEngine() {
return new PeerProtocolEngine(db, clientHelper, privateGroupManager, return new PeerProtocolEngine(db, clientHelper,
clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager, privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, clock); messageParser, messageEncoder, messageTracker, clock);
} }

View File

@@ -9,6 +9,7 @@ import javax.inject.Inject;
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE; import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_INVITE_TIMESTAMP; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_INVITE_TIMESTAMP;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_IS_SESSION;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LOCAL_TIMESTAMP; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LOCAL_TIMESTAMP;
@@ -28,6 +29,7 @@ class SessionEncoderImpl implements SessionEncoder {
@Override @Override
public BdfDictionary encodeSession(Session s) { public BdfDictionary encodeSession(Session s) {
BdfDictionary d = new BdfDictionary(); BdfDictionary d = new BdfDictionary();
d.put(SESSION_KEY_IS_SESSION, true);
d.put(SESSION_KEY_SESSION_ID, s.getPrivateGroupId()); d.put(SESSION_KEY_SESSION_ID, s.getPrivateGroupId());
d.put(SESSION_KEY_PRIVATE_GROUP_ID, s.getPrivateGroupId()); d.put(SESSION_KEY_PRIVATE_GROUP_ID, s.getPrivateGroupId());
MessageId lastLocalMessageId = s.getLastLocalMessageId(); MessageId lastLocalMessageId = s.getLastLocalMessageId();

View File

@@ -11,6 +11,8 @@ interface SessionParser {
BdfDictionary getSessionQuery(SessionId s); BdfDictionary getSessionQuery(SessionId s);
BdfDictionary getAllSessionsQuery();
Role getRole(BdfDictionary d) throws FormatException; Role getRole(BdfDictionary d) throws FormatException;
CreatorSession parseCreatorSession(GroupId contactGroupId, BdfDictionary d) CreatorSession parseCreatorSession(GroupId contactGroupId, BdfDictionary d)

View File

@@ -13,6 +13,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_INVITE_TIMESTAMP; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_INVITE_TIMESTAMP;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_IS_SESSION;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LOCAL_TIMESTAMP; import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LOCAL_TIMESTAMP;
@@ -37,6 +38,11 @@ class SessionParserImpl implements SessionParser {
return BdfDictionary.of(new BdfEntry(SESSION_KEY_SESSION_ID, s)); return BdfDictionary.of(new BdfEntry(SESSION_KEY_SESSION_ID, s));
} }
@Override
public BdfDictionary getAllSessionsQuery() {
return BdfDictionary.of(new BdfEntry(SESSION_KEY_IS_SESSION, true));
}
@Override @Override
public Role getRole(BdfDictionary d) throws FormatException { public Role getRole(BdfDictionary d) throws FormatException {
return Role.fromValue(d.getLong(SESSION_KEY_ROLE).intValue()); return Role.fromValue(d.getLong(SESSION_KEY_ROLE).intValue());

View File

@@ -1,6 +1,10 @@
package org.briarproject.briar.privategroup.invitation; package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.sync.Group.Visibility;
interface State { interface State {
int getValue(); int getValue();
Visibility getVisibility();
} }

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogInvitationResponse; import org.briarproject.briar.api.blog.BlogInvitationResponse;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
@@ -22,6 +23,9 @@ import org.briarproject.briar.api.sharing.InvitationRequest;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
import static org.briarproject.briar.api.blog.BlogManager.MAJOR_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> { class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
@@ -31,13 +35,14 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
invitationFactory; invitationFactory;
@Inject @Inject
BlogProtocolEngineImpl(DatabaseComponent db, BlogProtocolEngineImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientHelper clientHelper, MessageEncoder messageEncoder, ClientVersioningManager clientVersioningManager,
MessageParser<Blog> messageParser, MessageTracker messageTracker, MessageEncoder messageEncoder, MessageParser<Blog> messageParser,
Clock clock, BlogManager blogManager, MessageTracker messageTracker, Clock clock, BlogManager blogManager,
InvitationFactory<Blog, BlogInvitationResponse> invitationFactory) { InvitationFactory<Blog, BlogInvitationResponse> invitationFactory) {
super(db, clientHelper, messageEncoder, messageParser, messageTracker, super(db, clientHelper, clientVersioningManager, messageEncoder,
clock); messageParser, messageTracker, clock, CLIENT_ID,
MAJOR_VERSION);
this.blogManager = blogManager; this.blogManager = blogManager;
this.invitationFactory = invitationFactory; this.invitationFactory = invitationFactory;
} }
@@ -46,8 +51,8 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
Event getInvitationRequestReceivedEvent(InviteMessage<Blog> m, Event getInvitationRequestReceivedEvent(InviteMessage<Blog> m,
ContactId contactId, boolean available, boolean canBeOpened) { ContactId contactId, boolean available, boolean canBeOpened) {
InvitationRequest<Blog> request = invitationFactory InvitationRequest<Blog> request = invitationFactory
.createInvitationRequest(false, false, true, false, m, .createInvitationRequest(false, false, true, false, m,
contactId, available, canBeOpened); contactId, available, canBeOpened);
return new BlogInvitationRequestReceivedEvent(m.getShareable(), return new BlogInvitationRequestReceivedEvent(m.getShareable(),
contactId, request); contactId, request);
} }
@@ -74,7 +79,7 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
@Override @Override
protected ClientId getShareableClientId() { protected ClientId getShareableClientId() {
return BlogManager.CLIENT_ID; return CLIENT_ID;
} }
@Override @Override

View File

@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogInvitationResponse; import org.briarproject.briar.api.blog.BlogInvitationResponse;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
@@ -32,6 +33,7 @@ class BlogSharingManagerImpl extends SharingManagerImpl<Blog>
@Inject @Inject
BlogSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper, BlogSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageParser<Blog> messageParser, MetadataParser metadataParser, MessageParser<Blog> messageParser,
SessionEncoder sessionEncoder, SessionParser sessionParser, SessionEncoder sessionEncoder, SessionParser sessionParser,
MessageTracker messageTracker, MessageTracker messageTracker,
@@ -39,9 +41,9 @@ class BlogSharingManagerImpl extends SharingManagerImpl<Blog>
ProtocolEngine<Blog> engine, ProtocolEngine<Blog> engine,
InvitationFactory<Blog, BlogInvitationResponse> invitationFactory, InvitationFactory<Blog, BlogInvitationResponse> invitationFactory,
IdentityManager identityManager, BlogManager blogManager) { IdentityManager identityManager, BlogManager blogManager) {
super(db, clientHelper, metadataParser, messageParser, sessionEncoder, super(db, clientHelper, clientVersioningManager, metadataParser,
sessionParser, messageTracker, contactGroupFactory, engine, messageParser, sessionEncoder, sessionParser, messageTracker,
invitationFactory); contactGroupFactory, engine, invitationFactory);
this.identityManager = identityManager; this.identityManager = identityManager;
this.blogManager = blogManager; this.blogManager = blogManager;
} }
@@ -52,27 +54,31 @@ class BlogSharingManagerImpl extends SharingManagerImpl<Blog>
} }
@Override @Override
protected int getClientVersion() { protected int getMajorVersion() {
return CLIENT_VERSION; return MAJOR_VERSION;
}
@Override
protected ClientId getShareableClientId() {
return BlogManager.CLIENT_ID;
}
@Override
protected int getShareableMajorVersion() {
return BlogManager.MAJOR_VERSION;
} }
/**
* This is called during each startup for each existing Contact.
*/
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Return if we've already set things up for this contact // Create a group to share with the contact
if (db.containsGroup(txn, getContactGroup(c).getId())) return;
// creates a group to share with the contact
super.addingContact(txn, c); super.addingContact(txn, c);
// get our blog and that of Contact c // Get our blog and that of the contact
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn); LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
Blog ourBlog = blogManager.getPersonalBlog(localAuthor); Blog ourBlog = blogManager.getPersonalBlog(localAuthor);
Blog theirBlog = blogManager.getPersonalBlog(c.getAuthor()); Blog theirBlog = blogManager.getPersonalBlog(c.getAuthor());
// pre-share both blogs, if they have not been shared already // Pre-share both blogs, if they have not been shared already
try { try {
preShareShareable(txn, c, ourBlog); preShareShareable(txn, c, ourBlog);
preShareShareable(txn, c, theirBlog); preShareShareable(txn, c, theirBlog);

View File

@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumInvitationResponse;
@@ -22,6 +23,9 @@ import org.briarproject.briar.api.sharing.InvitationRequest;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
import static org.briarproject.briar.api.forum.ForumManager.MAJOR_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> { class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
@@ -32,12 +36,15 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
@Inject @Inject
ForumProtocolEngineImpl(DatabaseComponent db, ForumProtocolEngineImpl(DatabaseComponent db,
ClientHelper clientHelper, MessageEncoder messageEncoder, ClientHelper clientHelper,
MessageParser<Forum> messageParser, MessageTracker messageTracker, ClientVersioningManager clientVersioningManager,
Clock clock, ForumManager forumManager, MessageEncoder messageEncoder, MessageParser<Forum> messageParser,
MessageTracker messageTracker, Clock clock,
ForumManager forumManager,
InvitationFactory<Forum, ForumInvitationResponse> invitationFactory) { InvitationFactory<Forum, ForumInvitationResponse> invitationFactory) {
super(db, clientHelper, messageEncoder, messageParser, messageTracker, super(db, clientHelper, clientVersioningManager, messageEncoder,
clock); messageParser, messageTracker, clock, CLIENT_ID,
MAJOR_VERSION);
this.forumManager = forumManager; this.forumManager = forumManager;
this.invitationFactory = invitationFactory; this.invitationFactory = invitationFactory;
} }
@@ -46,8 +53,8 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
Event getInvitationRequestReceivedEvent(InviteMessage<Forum> m, Event getInvitationRequestReceivedEvent(InviteMessage<Forum> m,
ContactId contactId, boolean available, boolean canBeOpened) { ContactId contactId, boolean available, boolean canBeOpened) {
InvitationRequest<Forum> request = invitationFactory InvitationRequest<Forum> request = invitationFactory
.createInvitationRequest(false, false, true, false, m, .createInvitationRequest(false, false, true, false, m,
contactId, available, canBeOpened); contactId, available, canBeOpened);
return new ForumInvitationRequestReceivedEvent(m.getShareable(), return new ForumInvitationRequestReceivedEvent(m.getShareable(),
contactId, request); contactId, request);
} }
@@ -74,7 +81,7 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
@Override @Override
protected ClientId getShareableClientId() { protected ClientId getShareableClientId() {
return ForumManager.CLIENT_ID; return CLIENT_ID;
} }
@Override @Override

View File

@@ -8,9 +8,11 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumManager.RemoveForumHook; import org.briarproject.briar.api.forum.ForumManager.RemoveForumHook;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
@@ -22,15 +24,16 @@ class ForumSharingManagerImpl extends SharingManagerImpl<Forum>
@Inject @Inject
ForumSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper, ForumSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageParser<Forum> messageParser, MetadataParser metadataParser, MessageParser<Forum> messageParser,
SessionEncoder sessionEncoder, SessionParser sessionParser, SessionEncoder sessionEncoder, SessionParser sessionParser,
MessageTracker messageTracker, MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory, ContactGroupFactory contactGroupFactory,
ProtocolEngine<Forum> engine, ProtocolEngine<Forum> engine,
InvitationFactory<Forum, ForumInvitationResponse> invitationFactory) { InvitationFactory<Forum, ForumInvitationResponse> invitationFactory) {
super(db, clientHelper, metadataParser, messageParser, sessionEncoder, super(db, clientHelper, clientVersioningManager, metadataParser,
sessionParser, messageTracker, contactGroupFactory, engine, messageParser, sessionEncoder, sessionParser, messageTracker,
invitationFactory); contactGroupFactory, engine, invitationFactory);
} }
@Override @Override
@@ -39,8 +42,18 @@ class ForumSharingManagerImpl extends SharingManagerImpl<Forum>
} }
@Override @Override
protected int getClientVersion() { protected int getMajorVersion() {
return CLIENT_VERSION; return MAJOR_VERSION;
}
@Override
protected ClientId getShareableClientId() {
return ForumManager.CLIENT_ID;
}
@Override
protected int getShareableMajorVersion() {
return ForumManager.MAJOR_VERSION;
} }
@Override @Override

View File

@@ -17,6 +17,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.sharing.Shareable; import org.briarproject.briar.api.sharing.Shareable;
@@ -52,19 +53,27 @@ abstract class ProtocolEngineImpl<S extends Shareable>
protected final ClientHelper clientHelper; protected final ClientHelper clientHelper;
protected final MessageParser<S> messageParser; protected final MessageParser<S> messageParser;
private final ClientVersioningManager clientVersioningManager;
private final MessageEncoder messageEncoder; private final MessageEncoder messageEncoder;
private final MessageTracker messageTracker; private final MessageTracker messageTracker;
private final Clock clock; private final Clock clock;
private final ClientId shareableClientId;
private final int shareableClientVersion;
ProtocolEngineImpl(DatabaseComponent db, ClientHelper clientHelper, ProtocolEngineImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MessageEncoder messageEncoder, MessageParser<S> messageParser, MessageEncoder messageEncoder, MessageParser<S> messageParser,
MessageTracker messageTracker, Clock clock) { MessageTracker messageTracker, Clock clock,
ClientId shareableClientId, int shareableClientVersion) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.clientVersioningManager = clientVersioningManager;
this.messageEncoder = messageEncoder; this.messageEncoder = messageEncoder;
this.messageParser = messageParser; this.messageParser = messageParser;
this.messageTracker = messageTracker; this.messageTracker = messageTracker;
this.clock = clock; this.clock = clock;
this.shareableClientId = shareableClientId;
this.shareableClientVersion = shareableClientVersion;
} }
@Override @Override
@@ -598,9 +607,13 @@ abstract class ProtocolEngineImpl<S extends Shareable>
} }
private void setShareableVisibility(Transaction txn, Session session, private void setShareableVisibility(Transaction txn, Session session,
Visibility v) throws DbException, FormatException { Visibility preferred) throws DbException, FormatException {
// Apply min of preferred visibility and client's visibility
ContactId contactId = getContactId(txn, session.getContactGroupId()); ContactId contactId = getContactId(txn, session.getContactGroupId());
db.setGroupVisibility(txn, contactId, session.getShareableId(), v); Visibility client = clientVersioningManager.getClientVisibility(txn,
contactId, shareableClientId, shareableClientVersion);
Visibility min = Visibility.min(preferred, client);
db.setGroupVisibility(txn, contactId, session.getShareableId(), min);
} }
private ContactId getContactId(Transaction txn, GroupId contactGroupId) private ContactId getContactId(Transaction txn, GroupId contactGroupId)

View File

@@ -9,6 +9,7 @@ import javax.inject.Inject;
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE; import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_INVITE_TIMESTAMP; import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_INVITE_TIMESTAMP;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_IS_SESSION;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID; import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LOCAL_TIMESTAMP; import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LOCAL_TIMESTAMP;
@@ -27,6 +28,7 @@ class SessionEncoderImpl implements SessionEncoder {
@Override @Override
public BdfDictionary encodeSession(Session s) { public BdfDictionary encodeSession(Session s) {
BdfDictionary d = new BdfDictionary(); BdfDictionary d = new BdfDictionary();
d.put(SESSION_KEY_IS_SESSION, true);
d.put(SESSION_KEY_SESSION_ID, s.getShareableId()); d.put(SESSION_KEY_SESSION_ID, s.getShareableId());
d.put(SESSION_KEY_SHAREABLE_ID, s.getShareableId()); d.put(SESSION_KEY_SHAREABLE_ID, s.getShareableId());
MessageId lastLocalMessageId = s.getLastLocalMessageId(); MessageId lastLocalMessageId = s.getLastLocalMessageId();

View File

@@ -11,6 +11,8 @@ interface SessionParser {
BdfDictionary getSessionQuery(SessionId s); BdfDictionary getSessionQuery(SessionId s);
BdfDictionary getAllSessionsQuery();
Session parseSession(GroupId contactGroupId, BdfDictionary d) Session parseSession(GroupId contactGroupId, BdfDictionary d)
throws FormatException; throws FormatException;

View File

@@ -13,6 +13,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_INVITE_TIMESTAMP; import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_INVITE_TIMESTAMP;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_IS_SESSION;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID; import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID; import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LOCAL_TIMESTAMP; import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LOCAL_TIMESTAMP;
@@ -33,6 +34,11 @@ class SessionParserImpl implements SessionParser {
return BdfDictionary.of(new BdfEntry(SESSION_KEY_SESSION_ID, s)); return BdfDictionary.of(new BdfEntry(SESSION_KEY_SESSION_ID, s));
} }
@Override
public BdfDictionary getAllSessionsQuery() {
return BdfDictionary.of(new BdfEntry(SESSION_KEY_IS_SESSION, true));
}
@Override @Override
public Session parseSession(GroupId contactGroupId, public Session parseSession(GroupId contactGroupId,
BdfDictionary d) throws FormatException { BdfDictionary d) throws FormatException {

View File

@@ -18,6 +18,7 @@ interface SharingConstants {
String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted"; String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted";
// Session keys // Session keys
String SESSION_KEY_IS_SESSION = "isSession";
String SESSION_KEY_STATE = "state"; String SESSION_KEY_STATE = "state";
String SESSION_KEY_SESSION_ID = "sessionId"; String SESSION_KEY_SESSION_ID = "sessionId";
String SESSION_KEY_SHAREABLE_ID = "shareableId"; String SESSION_KEY_SHAREABLE_ID = "shareableId";

View File

@@ -17,10 +17,13 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client; import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.sharing.InvitationMessage; import org.briarproject.briar.api.sharing.InvitationMessage;
@@ -52,8 +55,10 @@ import static org.briarproject.briar.sharing.State.SHARING;
@NotNullByDefault @NotNullByDefault
abstract class SharingManagerImpl<S extends Shareable> abstract class SharingManagerImpl<S extends Shareable>
extends ConversationClientImpl extends ConversationClientImpl
implements SharingManager<S>, Client, ContactHook { implements SharingManager<S>, Client, ContactHook,
ClientVersioningHook {
private final ClientVersioningManager clientVersioningManager;
private final MessageParser<S> messageParser; private final MessageParser<S> messageParser;
private final SessionEncoder sessionEncoder; private final SessionEncoder sessionEncoder;
private final SessionParser sessionParser; private final SessionParser sessionParser;
@@ -62,12 +67,14 @@ abstract class SharingManagerImpl<S extends Shareable>
private final InvitationFactory<S, ?> invitationFactory; private final InvitationFactory<S, ?> invitationFactory;
SharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper, SharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageParser<S> messageParser, MetadataParser metadataParser, MessageParser<S> messageParser,
SessionEncoder sessionEncoder, SessionParser sessionParser, SessionEncoder sessionEncoder, SessionParser sessionParser,
MessageTracker messageTracker, MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory, ProtocolEngine<S> engine, ContactGroupFactory contactGroupFactory, ProtocolEngine<S> engine,
InvitationFactory<S, ?> invitationFactory) { InvitationFactory<S, ?> invitationFactory) {
super(db, clientHelper, metadataParser, messageTracker); super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.messageParser = messageParser; this.messageParser = messageParser;
this.sessionEncoder = sessionEncoder; this.sessionEncoder = sessionEncoder;
this.sessionParser = sessionParser; this.sessionParser = sessionParser;
@@ -78,48 +85,51 @@ abstract class SharingManagerImpl<S extends Shareable>
protected abstract ClientId getClientId(); protected abstract ClientId getClientId();
protected abstract int getClientVersion(); protected abstract int getMajorVersion();
protected abstract ClientId getShareableClientId();
protected abstract int getShareableMajorVersion();
@Override @Override
public void createLocalState(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
// Create a local group to indicate that we've set this client up // Create a local group to indicate that we've set this client up
Group localGroup = contactGroupFactory.createLocalGroup(getClientId(), Group localGroup = contactGroupFactory.createLocalGroup(getClientId(),
getClientVersion()); getMajorVersion());
if (db.containsGroup(txn, localGroup.getId())) return; if (db.containsGroup(txn, localGroup.getId())) return;
db.addGroup(txn, localGroup); db.addGroup(txn, localGroup);
// Ensure we've set things up for any pre-existing contacts // Set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c); for (Contact c : db.getContacts(txn)) addingContact(txn, c);
} }
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact
Group g = getContactGroup(c);
db.addGroup(txn, g);
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), getClientId(), getMajorVersion());
db.setGroupVisibility(txn, c.getId(), g.getId(), client);
// Attach the contact ID to the group
BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
try { try {
// Create a group to share with the contact
Group g = getContactGroup(c);
// Return if we've already set things up for this contact
if (db.containsGroup(txn, g.getId())) return;
// Store the group and share it with the contact
db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group
BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
clientHelper.mergeGroupMetadata(txn, g.getId(), meta); clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new AssertionError(e);
} }
} }
@Override @Override
public void removingContact(Transaction txn, Contact c) throws DbException { public void removingContact(Transaction txn, Contact c) throws DbException {
// remove the contact group (all messages will be removed with it) // Remove the contact group (all messages will be removed with it)
db.removeGroup(txn, getContactGroup(c)); db.removeGroup(txn, getContactGroup(c));
} }
@Override @Override
public Group getContactGroup(Contact c) { public Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(getClientId(), return contactGroupFactory.createContactGroup(getClientId(),
getClientVersion(), c); getMajorVersion(), c);
} }
@Override @Override
@@ -152,17 +162,21 @@ abstract class SharingManagerImpl<S extends Shareable>
*/ */
void preShareShareable(Transaction txn, Contact c, S shareable) void preShareShareable(Transaction txn, Contact c, S shareable)
throws DbException, FormatException { throws DbException, FormatException {
// return if a session already exists with that Contact // Return if a session already exists with the contact
GroupId contactGroupId = getContactGroup(c).getId(); GroupId contactGroupId = getContactGroup(c).getId();
StoredSession existingSession = getSession(txn, contactGroupId, StoredSession existingSession = getSession(txn, contactGroupId,
getSessionId(shareable.getId())); getSessionId(shareable.getId()));
if (existingSession != null) return; if (existingSession != null) return;
// add and shares the shareable with the Contact // Add the shareable
db.addGroup(txn, shareable.getGroup()); db.addGroup(txn, shareable.getGroup());
db.setGroupVisibility(txn, c.getId(), shareable.getId(), SHARED);
// initialize session in sharing state // Apply the client's visibility
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), getShareableClientId(), getShareableMajorVersion());
db.setGroupVisibility(txn, c.getId(), shareable.getId(), client);
// Initialize session in sharing state
Session session = new Session(SHARING, contactGroupId, Session session = new Session(SHARING, contactGroupId,
shareable.getId(), null, null, 0, 0); shareable.getId(), null, null, 0, 0);
MessageId storageId = createStorageId(txn, contactGroupId); MessageId storageId = createStorageId(txn, contactGroupId);
@@ -446,6 +460,10 @@ abstract class SharingManagerImpl<S extends Shareable>
private boolean canBeShared(Transaction txn, GroupId g, Contact c) private boolean canBeShared(Transaction txn, GroupId g, Contact c)
throws DbException { throws DbException {
// The group can't be shared unless the contact supports the client
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), getShareableClientId(), getShareableMajorVersion());
if (client != SHARED) return false;
GroupId contactGroupId = getContactGroup(c).getId(); GroupId contactGroupId = getContactGroup(c).getId();
SessionId sessionId = getSessionId(g); SessionId sessionId = getSessionId(g);
try { try {
@@ -482,6 +500,51 @@ abstract class SharingManagerImpl<S extends Shareable>
} }
} }
@Override
public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException {
// Apply the client's visibility to the contact group
Group g = getContactGroup(c);
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
}
ClientVersioningHook getShareableClientVersioningHook() {
return this::onShareableClientVisibilityChanging;
}
// Versioning hook for the shareable client
private void onShareableClientVisibilityChanging(Transaction txn, Contact c,
Visibility client) throws DbException {
try {
Collection<Group> shareables = db.getGroups(txn,
getShareableClientId(), getShareableMajorVersion());
Map<GroupId, Visibility> m = getPreferredVisibilities(txn, c);
for (Group g : shareables) {
Visibility preferred = m.get(g.getId());
if (preferred == null) continue; // No session for this group
// Apply min of preferred visibility and client's visibility
Visibility min = Visibility.min(preferred, client);
db.setGroupVisibility(txn, c.getId(), g.getId(), min);
}
} catch (FormatException e) {
throw new DbException(e);
}
}
private Map<GroupId, Visibility> getPreferredVisibilities(Transaction txn,
Contact c) throws DbException, FormatException {
GroupId contactGroupId = getContactGroup(c).getId();
BdfDictionary query = sessionParser.getAllSessionsQuery();
Map<MessageId, BdfDictionary> results = clientHelper
.getMessageMetadataAsDictionary(txn, contactGroupId, query);
Map<GroupId, Visibility> m = new HashMap<>();
for (BdfDictionary d : results.values()) {
Session s = sessionParser.parseSession(contactGroupId, d);
m.put(s.getShareableId(), s.getState().getVisibility());
}
return m;
}
private static class StoredSession { private static class StoredSession {
private final MessageId storageId; private final MessageId storageId;

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.ValidationManager; import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogFactory; import org.briarproject.briar.api.blog.BlogFactory;
import org.briarproject.briar.api.blog.BlogInvitationResponse; import org.briarproject.briar.api.blog.BlogInvitationResponse;
@@ -59,11 +60,11 @@ public class SharingModule {
ValidationManager validationManager, MessageEncoder messageEncoder, ValidationManager validationManager, MessageEncoder messageEncoder,
ClientHelper clientHelper, MetadataEncoder metadataEncoder, ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock, BlogFactory blogFactory) { Clock clock, BlogFactory blogFactory) {
BlogSharingValidator validator = BlogSharingValidator validator = new BlogSharingValidator(
new BlogSharingValidator(messageEncoder, clientHelper, messageEncoder, clientHelper, metadataEncoder, clock,
metadataEncoder, clock, blogFactory); blogFactory);
validationManager.registerMessageValidator(BlogSharingManager.CLIENT_ID, validationManager.registerMessageValidator(BlogSharingManager.CLIENT_ID,
validator); BlogSharingManager.MAJOR_VERSION, validator);
return validator; return validator;
} }
@@ -73,14 +74,23 @@ public class SharingModule {
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager, ValidationManager validationManager,
ConversationManager conversationManager, BlogManager blogManager, ConversationManager conversationManager, BlogManager blogManager,
ClientVersioningManager clientVersioningManager,
BlogSharingManagerImpl blogSharingManager) { BlogSharingManagerImpl blogSharingManager) {
lifecycleManager.registerClient(blogSharingManager); lifecycleManager.registerClient(blogSharingManager);
contactManager.registerContactHook(blogSharingManager); contactManager.registerContactHook(blogSharingManager);
validationManager.registerIncomingMessageHook( validationManager.registerIncomingMessageHook(
BlogSharingManager.CLIENT_ID, blogSharingManager); BlogSharingManager.CLIENT_ID, BlogSharingManager.MAJOR_VERSION,
blogSharingManager);
conversationManager.registerConversationClient(blogSharingManager); conversationManager.registerConversationClient(blogSharingManager);
blogManager.registerRemoveBlogHook(blogSharingManager); blogManager.registerRemoveBlogHook(blogSharingManager);
clientVersioningManager.registerClient(BlogSharingManager.CLIENT_ID,
BlogSharingManager.MAJOR_VERSION,
BlogSharingManager.MINOR_VERSION, blogSharingManager);
// The blog sharing manager handles client visibility changes for the
// blog manager
clientVersioningManager.registerClient(BlogManager.CLIENT_ID,
BlogManager.MAJOR_VERSION, BlogManager.MINOR_VERSION,
blogSharingManager.getShareableClientVersioningHook());
return blogSharingManager; return blogSharingManager;
} }
@@ -108,12 +118,12 @@ public class SharingModule {
ValidationManager validationManager, MessageEncoder messageEncoder, ValidationManager validationManager, MessageEncoder messageEncoder,
ClientHelper clientHelper, MetadataEncoder metadataEncoder, ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock, ForumFactory forumFactory) { Clock clock, ForumFactory forumFactory) {
ForumSharingValidator validator = ForumSharingValidator validator = new ForumSharingValidator(
new ForumSharingValidator(messageEncoder, clientHelper, messageEncoder, clientHelper, metadataEncoder, clock,
metadataEncoder, clock, forumFactory); forumFactory);
validationManager validationManager.registerMessageValidator(
.registerMessageValidator(ForumSharingManager.CLIENT_ID, ForumSharingManager.CLIENT_ID,
validator); ForumSharingManager.MAJOR_VERSION, validator);
return validator; return validator;
} }
@@ -123,15 +133,23 @@ public class SharingModule {
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager, ValidationManager validationManager,
ConversationManager conversationManager, ForumManager forumManager, ConversationManager conversationManager, ForumManager forumManager,
ClientVersioningManager clientVersioningManager,
ForumSharingManagerImpl forumSharingManager) { ForumSharingManagerImpl forumSharingManager) {
lifecycleManager.registerClient(forumSharingManager); lifecycleManager.registerClient(forumSharingManager);
contactManager.registerContactHook(forumSharingManager); contactManager.registerContactHook(forumSharingManager);
validationManager.registerIncomingMessageHook( validationManager.registerIncomingMessageHook(
ForumSharingManager.CLIENT_ID, forumSharingManager); ForumSharingManager.CLIENT_ID,
ForumSharingManager.MAJOR_VERSION, forumSharingManager);
conversationManager.registerConversationClient(forumSharingManager); conversationManager.registerConversationClient(forumSharingManager);
forumManager.registerRemoveForumHook(forumSharingManager); forumManager.registerRemoveForumHook(forumSharingManager);
clientVersioningManager.registerClient(ForumSharingManager.CLIENT_ID,
ForumSharingManager.MAJOR_VERSION,
ForumSharingManager.MINOR_VERSION, forumSharingManager);
// The forum sharing manager handles client visibility changes for the
// forum manager
clientVersioningManager.registerClient(ForumManager.CLIENT_ID,
ForumManager.MAJOR_VERSION, ForumManager.MINOR_VERSION,
forumSharingManager.getShareableClientVersioningHook());
return forumSharingManager; return forumSharingManager;
} }

View File

@@ -2,26 +2,41 @@ package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group.Visibility;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
enum State { enum State {
START(0), LOCAL_INVITED(1), REMOTE_INVITED(2), SHARING(3), LOCAL_LEFT(4), START(0, INVISIBLE),
REMOTE_HANGING(5); LOCAL_INVITED(1, INVISIBLE),
REMOTE_INVITED(2, VISIBLE),
SHARING(3, SHARED),
LOCAL_LEFT(4, INVISIBLE),
REMOTE_HANGING(5, INVISIBLE);
private final int value; private final int value;
private final Visibility visibility;
State(int value) { State(int value, Visibility visibility) {
this.value = value; this.value = value;
this.visibility = visibility;
} }
public int getValue() { public int getValue() {
return value; return value;
} }
public Visibility getVisibility() {
return visibility;
}
public boolean canInvite() { public boolean canInvite() {
return this == START; return this == START;
} }

View File

@@ -14,6 +14,7 @@ import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.Blog;
@@ -49,6 +50,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE;
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_COMMENT_LENGTH; import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_COMMENT_LENGTH;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID; import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
import static org.briarproject.briar.api.blog.BlogManager.MAJOR_VERSION;
import static org.briarproject.briar.api.blog.MessageType.COMMENT; import static org.briarproject.briar.api.blog.MessageType.COMMENT;
import static org.briarproject.briar.api.blog.MessageType.POST; import static org.briarproject.briar.api.blog.MessageType.POST;
import static org.briarproject.briar.api.blog.MessageType.WRAPPED_COMMENT; import static org.briarproject.briar.api.blog.MessageType.WRAPPED_COMMENT;
@@ -866,7 +868,8 @@ public class BlogManagerImplTest extends BriarTestCase {
} }
private Blog createBlog(LocalAuthor localAuthor, boolean rssFeed) { private Blog createBlog(LocalAuthor localAuthor, boolean rssFeed) {
return new Blog(getGroup(CLIENT_ID), localAuthor, rssFeed); Group group = getGroup(CLIENT_ID, MAJOR_VERSION);
return new Blog(group, localAuthor, rssFeed);
} }
private BdfList authorToBdfList(Author a) { private BdfList authorToBdfList(Author a) {

View File

@@ -36,7 +36,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_PARENT_MSG_ID;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ; import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ;
import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED; import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID; import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_VERSION; import static org.briarproject.briar.api.blog.BlogManager.MAJOR_VERSION;
import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_COMMENT; import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_COMMENT;
import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_POST; import static org.briarproject.briar.api.blog.BlogPostFactory.SIGNING_LABEL_POST;
import static org.briarproject.briar.api.blog.MessageType.COMMENT; import static org.briarproject.briar.api.blog.MessageType.COMMENT;
@@ -64,7 +64,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
private final String body = getRandomString(42); private final String body = getRandomString(42);
public BlogPostValidatorTest() { public BlogPostValidatorTest() {
group = getGroup(CLIENT_ID); group = getGroup(CLIENT_ID, MAJOR_VERSION);
descriptor = group.getDescriptor(); descriptor = group.getDescriptor();
author = getAuthor(); author = getAuthor();
authorList = BdfList.of( authorList = BdfList.of(
@@ -206,7 +206,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
byte[] originalBody = getRandomBytes(42); byte[] originalBody = getRandomBytes(42);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(groupFactory).createGroup(CLIENT_ID, CLIENT_VERSION, oneOf(groupFactory).createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor); descriptor);
will(returnValue(b.getGroup())); will(returnValue(b.getGroup()));
oneOf(blogFactory).parseBlog(b.getGroup()); oneOf(blogFactory).parseBlog(b.getGroup());
@@ -250,7 +250,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
byte[] originalBody = getRandomBytes(42); byte[] originalBody = getRandomBytes(42);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(groupFactory).createGroup(CLIENT_ID, CLIENT_VERSION, oneOf(groupFactory).createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor); descriptor);
will(returnValue(blog.getGroup())); will(returnValue(blog.getGroup()));
oneOf(clientHelper).toByteArray(originalList); oneOf(clientHelper).toByteArray(originalList);

View File

@@ -43,7 +43,7 @@ import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEEDS; import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEEDS;
import static org.briarproject.briar.api.feed.FeedManager.CLIENT_ID; import static org.briarproject.briar.api.feed.FeedManager.CLIENT_ID;
import static org.briarproject.briar.api.feed.FeedManager.CLIENT_VERSION; import static org.briarproject.briar.api.feed.FeedManager.MAJOR_VERSION;
public class FeedManagerImplTest extends BrambleMockTestCase { public class FeedManagerImplTest extends BrambleMockTestCase {
@@ -61,9 +61,10 @@ public class FeedManagerImplTest extends BrambleMockTestCase {
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final Dns noDnsLookups = context.mock(Dns.class); private final Dns noDnsLookups = context.mock(Dns.class);
private final Group localGroup = getGroup(CLIENT_ID); private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final GroupId localGroupId = localGroup.getId(); private final GroupId localGroupId = localGroup.getId();
private final Group blogGroup = getGroup(BlogManager.CLIENT_ID); private final Group blogGroup =
getGroup(BlogManager.CLIENT_ID, BlogManager.MAJOR_VERSION);
private final GroupId blogGroupId = blogGroup.getId(); private final GroupId blogGroupId = blogGroup.getId();
private final LocalAuthor localAuthor = getLocalAuthor(); private final LocalAuthor localAuthor = getLocalAuthor();
private final Blog blog = new Blog(blogGroup, localAuthor, true); private final Blog blog = new Blog(blogGroup, localAuthor, true);
@@ -131,7 +132,7 @@ public class FeedManagerImplTest extends BrambleMockTestCase {
private void expectGetLocalGroup() { private void expectGetLocalGroup() {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID, oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID,
CLIENT_VERSION); MAJOR_VERSION);
will(returnValue(localGroup)); will(returnValue(localGroup));
}}); }});
} }

View File

@@ -2,7 +2,7 @@ package org.briarproject.briar.feed;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.contact.ContactModule; import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoModule; import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.identity.IdentityModule; import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.lifecycle.LifecycleModule; import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.sync.SyncModule; import org.briarproject.bramble.sync.SyncModule;
@@ -10,6 +10,7 @@ import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.test.TestDatabaseModule; import org.briarproject.bramble.test.TestDatabaseModule;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostHeader; import org.briarproject.briar.api.blog.BlogPostHeader;
@@ -113,15 +114,16 @@ public class FeedManagerIntegrationTest extends BriarTestCase {
protected void injectEagerSingletons( protected void injectEagerSingletons(
FeedManagerIntegrationTestComponent component) { FeedManagerIntegrationTestComponent component) {
component.inject(new FeedModule.EagerSingletons());
component.inject(new BlogModule.EagerSingletons()); component.inject(new BlogModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons()); component.inject(new ContactModule.EagerSingletons());
component.inject(new CryptoModule.EagerSingletons()); component.inject(new CryptoExecutorModule.EagerSingletons());
component.inject(new FeedModule.EagerSingletons());
component.inject(new IdentityModule.EagerSingletons()); component.inject(new IdentityModule.EagerSingletons());
component.inject(new LifecycleModule.EagerSingletons()); component.inject(new LifecycleModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons()); component.inject(new SyncModule.EagerSingletons());
component.inject(new SystemModule.EagerSingletons()); component.inject(new SystemModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons()); component.inject(new TransportModule.EagerSingletons());
component.inject(new VersioningModule.EagerSingletons());
} }
} }

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.feed;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.client.ClientModule; import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.contact.ContactModule; import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule; import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.data.DataModule; import org.briarproject.bramble.data.DataModule;
import org.briarproject.bramble.db.DatabaseModule; import org.briarproject.bramble.db.DatabaseModule;
@@ -16,6 +17,7 @@ import org.briarproject.bramble.test.TestPluginConfigModule;
import org.briarproject.bramble.test.TestSecureRandomModule; import org.briarproject.bramble.test.TestSecureRandomModule;
import org.briarproject.bramble.test.TestSocksModule; import org.briarproject.bramble.test.TestSocksModule;
import org.briarproject.bramble.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.feed.FeedManager; import org.briarproject.briar.api.feed.FeedManager;
import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.blog.BlogModule;
@@ -33,32 +35,34 @@ import dagger.Component;
TestSecureRandomModule.class, TestSecureRandomModule.class,
TestSocksModule.class, TestSocksModule.class,
TestDnsModule.class, TestDnsModule.class,
LifecycleModule.class,
BriarClientModule.class, BriarClientModule.class,
ClientModule.class, ClientModule.class,
ContactModule.class, ContactModule.class,
CryptoModule.class, CryptoModule.class,
CryptoExecutorModule.class,
BlogModule.class, BlogModule.class,
FeedModule.class, FeedModule.class,
DataModule.class, DataModule.class,
DatabaseModule.class, DatabaseModule.class,
EventModule.class, EventModule.class,
IdentityModule.class, IdentityModule.class,
LifecycleModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class,
VersioningModule.class
}) })
interface FeedManagerIntegrationTestComponent { interface FeedManagerIntegrationTestComponent {
void inject(FeedManagerIntegrationTest testCase); void inject(FeedManagerIntegrationTest testCase);
void inject(FeedModule.EagerSingletons init);
void inject(BlogModule.EagerSingletons init); void inject(BlogModule.EagerSingletons init);
void inject(ContactModule.EagerSingletons init); void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init); void inject(CryptoExecutorModule.EagerSingletons init);
void inject(FeedModule.EagerSingletons init);
void inject(IdentityModule.EagerSingletons init); void inject(IdentityModule.EagerSingletons init);
@@ -70,6 +74,8 @@ interface FeedManagerIntegrationTestComponent {
void inject(TransportModule.EagerSingletons init); void inject(TransportModule.EagerSingletons init);
void inject(VersioningModule.EagerSingletons init);
LifecycleManager getLifecycleManager(); LifecycleManager getLifecycleManager();
FeedManager getFeedManager(); FeedManager getFeedManager();

View File

@@ -47,7 +47,7 @@ import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getTransportProperties; import static org.briarproject.bramble.test.TestUtils.getTransportProperties;
import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap; import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID; import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_VERSION; import static org.briarproject.briar.api.introduction.IntroductionManager.MAJOR_VERSION;
import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_RESPONSES; import static org.briarproject.briar.introduction.IntroduceeState.AWAIT_RESPONSES;
import static org.briarproject.briar.introduction.IntroduceeState.LOCAL_DECLINED; import static org.briarproject.briar.introduction.IntroduceeState.LOCAL_DECLINED;
import static org.briarproject.briar.introduction.IntroducerState.A_DECLINED; import static org.briarproject.briar.introduction.IntroducerState.A_DECLINED;
@@ -1284,7 +1284,7 @@ public class IntroductionIntegrationTest
} }
private Group getLocalGroup() { private Group getLocalGroup() {
return contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION); return contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION);
} }
} }

View File

@@ -2,6 +2,7 @@ package org.briarproject.briar.introduction;
import org.briarproject.bramble.client.ClientModule; import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.contact.ContactModule; import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule; import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.data.DataModule; import org.briarproject.bramble.data.DataModule;
import org.briarproject.bramble.db.DatabaseModule; import org.briarproject.bramble.db.DatabaseModule;
@@ -15,6 +16,7 @@ import org.briarproject.bramble.test.TestDatabaseModule;
import org.briarproject.bramble.test.TestPluginConfigModule; import org.briarproject.bramble.test.TestPluginConfigModule;
import org.briarproject.bramble.test.TestSecureRandomModule; import org.briarproject.bramble.test.TestSecureRandomModule;
import org.briarproject.bramble.transport.TransportModule; import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule; import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.forum.ForumModule; import org.briarproject.briar.forum.ForumModule;
@@ -38,6 +40,7 @@ import dagger.Component;
ClientModule.class, ClientModule.class,
ContactModule.class, ContactModule.class,
CryptoModule.class, CryptoModule.class,
CryptoExecutorModule.class,
DataModule.class, DataModule.class,
DatabaseModule.class, DatabaseModule.class,
EventModule.class, EventModule.class,
@@ -52,7 +55,8 @@ import dagger.Component;
SharingModule.class, SharingModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class,
VersioningModule.class
}) })
interface IntroductionIntegrationTestComponent interface IntroductionIntegrationTestComponent
extends BriarIntegrationTestComponent { extends BriarIntegrationTestComponent {

View File

@@ -25,12 +25,14 @@ import javax.inject.Inject;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES; import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES; import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_BYTES;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap; import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_REQUEST_MESSAGE_LENGTH;
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID; import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
import static org.briarproject.briar.api.introduction.IntroductionManager.MAJOR_VERSION;
import static org.briarproject.briar.introduction.MessageType.ABORT; import static org.briarproject.briar.introduction.MessageType.ABORT;
import static org.briarproject.briar.introduction.MessageType.REQUEST; import static org.briarproject.briar.introduction.MessageType.REQUEST;
import static org.briarproject.briar.test.BriarTestUtils.getRealAuthor; import static org.briarproject.briar.test.BriarTestUtils.getRealAuthor;
@@ -57,8 +59,8 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
private final MessageParser messageParser; private final MessageParser messageParser;
private final IntroductionValidator validator; private final IntroductionValidator validator;
private final GroupId groupId = new GroupId(getRandomId()); private final Group group = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group group = new Group(groupId, CLIENT_ID, getRandomId()); private final GroupId groupId = group.getId();
private final long timestamp = 42L; private final long timestamp = 42L;
private final SessionId sessionId = new SessionId(getRandomId()); private final SessionId sessionId = new SessionId(getRandomId());
private final MessageId previousMsgId = new MessageId(getRandomId()); private final MessageId previousMsgId = new MessageId(getRandomId());

View File

@@ -7,11 +7,18 @@ import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule; import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import org.briarproject.briar.api.forum.ForumPost; import org.briarproject.briar.api.forum.ForumPost;
import org.briarproject.briar.api.forum.ForumPostFactory; import org.briarproject.briar.api.forum.ForumPostFactory;
import org.briarproject.briar.api.messaging.PrivateMessage; import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory; import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.forum.ForumModule;
import org.briarproject.briar.test.BriarTestCase; import org.briarproject.briar.test.BriarTestCase;
import org.junit.Test; import org.junit.Test;
@@ -85,6 +92,14 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
private static void injectEagerSingletons( private static void injectEagerSingletons(
MessageSizeIntegrationTestComponent component) { MessageSizeIntegrationTestComponent component) {
component.inject(new ContactModule.EagerSingletons());
component.inject(new CryptoExecutorModule.EagerSingletons());
component.inject(new ForumModule.EagerSingletons());
component.inject(new IdentityModule.EagerSingletons());
component.inject(new MessagingModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new SystemModule.EagerSingletons()); component.inject(new SystemModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons());
component.inject(new VersioningModule.EagerSingletons());
} }
} }

Some files were not shown because too many files have changed in this diff Show More