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.
*/
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.
*/
Group createContactGroup(ClientId clientId, int clientVersion,
Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact);
/**
* Creates a group for the given client to share between the given authors
* identified by their AuthorIds.
*/
Group createContactGroup(ClientId clientId, int clientVersion,
Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2);
}

View File

@@ -241,7 +241,8 @@ public interface DatabaseComponent {
* <p/>
* 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
@@ -266,6 +267,14 @@ public interface DatabaseComponent {
*/
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.
* <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);
/**
* 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);
/**
* 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);

View File

@@ -15,12 +15,17 @@ public interface TransportPropertyManager {
/**
* 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
@@ -37,8 +42,8 @@ public interface TransportPropertyManager {
/**
* Returns the local transport properties for all transports.
* <br/>
* TODO: Transaction can be read-only when code is simplified
* <p/>
* Read-only.
*/
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
throws DbException;

View File

@@ -5,9 +5,24 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPT
public class Group {
public enum Visibility {
INVISIBLE, // The group is not visible
VISIBLE, // The group is visible but messages are not shared
SHARED // The group is visible and messages are shared
INVISIBLE(0), // The group is not visible
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 ClientId clientId;
private final int majorVersion;
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)
throw new IllegalArgumentException();
this.id = id;
this.clientId = clientId;
this.majorVersion = majorVersion;
this.descriptor = descriptor;
}
@@ -41,6 +59,13 @@ public class Group {
return clientId;
}
/**
* Returns the major version of the client to which the group belongs.
*/
public int getMajorVersion() {
return majorVersion;
}
/**
* Returns the group's descriptor.
*/

View File

@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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
/** 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
/**

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.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
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
* called once for each incoming message that passes validation.
* Registers the incoming message hook for the given client. The hook will
* 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 {

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);
}
public static Group getGroup(ClientId clientId) {
public static Group getGroup(ClientId clientId, int majorVersion) {
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());
byte[] descriptor = getRandomBytes(descriptorLength);
return new Group(groupId, clientId, descriptor);
return new Group(groupId, clientId, majorVersion, descriptor);
}
public static Message getMessage(GroupId groupId) {

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble;
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.identity.IdentityModule;
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.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
public interface BrambleCoreEagerSingletons {
void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(CryptoExecutorModule.EagerSingletons init);
void inject(DatabaseExecutorModule.EagerSingletons init);
@@ -32,4 +33,6 @@ public interface BrambleCoreEagerSingletons {
void inject(SystemModule.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.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.data.DataModule;
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.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import dagger.Module;
@@ -26,6 +28,7 @@ import dagger.Module;
ClientModule.class,
ContactModule.class,
CryptoModule.class,
CryptoExecutorModule.class,
DataModule.class,
DatabaseModule.class,
DatabaseExecutorModule.class,
@@ -41,13 +44,14 @@ import dagger.Module;
SocksModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
TransportModule.class,
VersioningModule.class
})
public class BrambleCoreModule {
public static void initEagerSingletons(BrambleCoreEagerSingletons c) {
c.inject(new ContactModule.EagerSingletons());
c.inject(new CryptoModule.EagerSingletons());
c.inject(new CryptoExecutorModule.EagerSingletons());
c.inject(new DatabaseExecutorModule.EagerSingletons());
c.inject(new IdentityModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons());
@@ -56,5 +60,6 @@ public class BrambleCoreModule {
c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons());
c.inject(new VersioningModule.EagerSingletons());
}
}

View File

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

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;
import org.briarproject.bramble.TimeLoggingExecutor;
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.PasswordStrengthEstimator;
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.SecureRandomProvider;
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.Singleton;
import dagger.Module;
import dagger.Provides;
import static java.util.concurrent.TimeUnit.SECONDS;
@Module
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
AuthenticatedCipher provideAuthenticatedCipher() {
return new XSalsa20Poly1305AuthenticatedCipher();
@@ -103,21 +63,6 @@ public class CryptoModule {
return keyAgreementCrypto;
}
@Provides
@Singleton
@CryptoExecutor
ExecutorService getCryptoExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(cryptoExecutor);
return cryptoExecutor;
}
@Provides
@CryptoExecutor
Executor getCryptoExecutor() {
return cryptoExecutor;
}
@Provides
SecureRandom getSecureRandom(CryptoComponent crypto) {
return crypto.getSecureRandom();

View File

@@ -266,7 +266,8 @@ interface Database<T> {
* <p/>
* 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

View File

@@ -435,10 +435,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public Collection<Group> getGroups(Transaction transaction, ClientId c)
throws DbException {
public Collection<Group> getGroups(Transaction transaction, ClientId c,
int majorVersion) throws DbException {
T txn = unbox(transaction);
return db.getGroups(txn, c);
return db.getGroups(txn, c, majorVersion);
}
@Override
@@ -466,6 +466,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public Collection<MessageId> getMessagesToValidate(Transaction transaction)
throws DbException {

View File

@@ -74,7 +74,7 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
abstract class JdbcDatabase implements Database<Connection> {
// 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
private static final int OFFSET_PREV = -1;
@@ -117,6 +117,7 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE groups"
+ " (groupId _HASH NOT NULL,"
+ " clientId _STRING NOT NULL,"
+ " majorVersion INT NOT NULL,"
+ " descriptor _BINARY NOT NULL,"
+ " PRIMARY KEY (groupId))";
@@ -275,9 +276,9 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE INDEX IF NOT EXISTS contactsByAuthorId"
+ " ON contacts (authorId)";
private static final String INDEX_GROUPS_BY_CLIENT_ID =
"CREATE INDEX IF NOT EXISTS groupsByClientId"
+ " ON groups (clientId)";
private static final String INDEX_GROUPS_BY_CLIENT_ID_MAJOR_VERSION =
"CREATE INDEX IF NOT EXISTS groupsByClientIdMajorVersion"
+ " ON groups (clientId, majorVersion)";
private static final String INDEX_MESSAGE_METADATA_BY_GROUP_ID_STATE =
"CREATE INDEX IF NOT EXISTS messageMetadataByGroupIdState"
@@ -444,7 +445,7 @@ abstract class JdbcDatabase implements Database<Connection> {
try {
s = txn.createStatement();
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_DEPENDENCIES_BY_DEPENDENCY_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 {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO groups (groupId, clientId, descriptor)"
+ " VALUES (?, ?, ?)";
String sql = "INSERT INTO groups"
+ " (groupId, clientId, majorVersion, descriptor)"
+ " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getId().getBytes());
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();
if (affected != 1) throw new DbStateException();
ps.close();
@@ -1346,17 +1349,18 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT clientId, descriptor FROM groups"
+ " WHERE groupId = ?";
String sql = "SELECT clientId, majorVersion, descriptor"
+ " FROM groups WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
ClientId clientId = new ClientId(rs.getString(1));
byte[] descriptor = rs.getBytes(2);
int majorVersion = rs.getInt(2);
byte[] descriptor = rs.getBytes(3);
rs.close();
ps.close();
return new Group(g, clientId, descriptor);
return new Group(g, clientId, majorVersion, descriptor);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
@@ -1365,21 +1369,22 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public Collection<Group> getGroups(Connection txn, ClientId c)
throws DbException {
public Collection<Group> getGroups(Connection txn, ClientId c,
int majorVersion) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT groupId, descriptor FROM groups"
+ " WHERE clientId = ?";
+ " WHERE clientId = ? AND majorVersion = ?";
ps = txn.prepareStatement(sql);
ps.setString(1, c.getString());
ps.setInt(2, majorVersion);
rs = ps.executeQuery();
List<Group> groups = new ArrayList<>();
while (rs.next()) {
GroupId id = new GroupId(rs.getBytes(1));
byte[] descriptor = rs.getBytes(2);
groups.add(new Group(id, c, descriptor));
groups.add(new Group(id, c, majorVersion, descriptor));
}
rs.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.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -15,6 +16,8 @@ import dagger.Module;
import dagger.Provides;
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
public class PropertiesModule {
@@ -33,7 +36,8 @@ public class PropertiesModule {
Clock clock) {
TransportPropertyValidator validator = new TransportPropertyValidator(
clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validator);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator;
}
@@ -42,11 +46,14 @@ public class PropertiesModule {
TransportPropertyManager getTransportPropertyManager(
LifecycleManager lifecycleManager,
ValidationManager validationManager, ContactManager contactManager,
ClientVersioningManager clientVersioningManager,
TransportPropertyManagerImpl transportPropertyManager) {
lifecycleManager.registerClient(transportPropertyManager);
validationManager.registerIncomingMessageHook(CLIENT_ID,
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
transportPropertyManager);
contactManager.registerContactHook(transportPropertyManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, 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.sync.Client;
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.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import java.util.HashMap;
import java.util.Map;
@@ -34,15 +37,14 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
@Immutable
@NotNullByDefault
class TransportPropertyManagerImpl implements TransportPropertyManager,
Client, ContactHook, IncomingMessageHook {
Client, ContactHook, ClientVersioningHook, IncomingMessageHook {
private final DatabaseComponent db;
private final ClientHelper clientHelper;
private final ClientVersioningManager clientVersioningManager;
private final MetadataParser metadataParser;
private final ContactGroupFactory contactGroupFactory;
private final Clock clock;
@@ -50,22 +52,25 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Inject
TransportPropertyManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, MetadataParser metadataParser,
ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory, Clock clock) {
this.db = db;
this.clientHelper = clientHelper;
this.clientVersioningManager = clientVersioningManager;
this.metadataParser = metadataParser;
this.contactGroupFactory = contactGroupFactory;
this.clock = clock;
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
CLIENT_VERSION);
MAJOR_VERSION);
}
@Override
public void createLocalState(Transaction txn) throws DbException {
if (db.containsGroup(txn, localGroup.getId())) return;
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);
}
@@ -73,11 +78,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
public void addingContact(Transaction txn, Contact c) throws DbException {
// 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);
// 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
Map<TransportId, TransportProperties> local = getLocalProperties(txn);
for (Entry<TransportId, TransportProperties> e : local.entrySet()) {
@@ -91,6 +96,14 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
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
public boolean incomingMessage(Transaction txn, Message m, Metadata meta)
throws DbException, InvalidMessageException {
@@ -289,7 +302,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
private Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID,
CLIENT_VERSION, c);
MAJOR_VERSION, c);
}
private void storeMessage(Transaction txn, GroupId g, TransportId t,

View File

@@ -28,12 +28,12 @@ class GroupFactoryImpl implements GroupFactory {
}
@Override
public Group createGroup(ClientId c, int clientVersion, byte[] descriptor) {
byte[] clientVersionBytes = new byte[INT_32_BYTES];
ByteUtils.writeUint32(clientVersion, clientVersionBytes, 0);
public Group createGroup(ClientId c, int majorVersion, byte[] descriptor) {
byte[] majorVersionBytes = new byte[INT_32_BYTES];
ByteUtils.writeUint32(majorVersion, majorVersionBytes, 0);
byte[] hash = crypto.hash(LABEL, new byte[] {FORMAT_VERSION},
StringUtils.toUtf8(c.getString()), clientVersionBytes,
StringUtils.toUtf8(c.getString()), majorVersionBytes,
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.ValidationManager;
import org.briarproject.bramble.api.sync.event.MessageAddedEvent;
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
import java.util.Collection;
import java.util.LinkedList;
@@ -51,8 +52,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
private final DatabaseComponent db;
private final Executor dbExecutor, validationExecutor;
private final MessageFactory messageFactory;
private final Map<ClientId, MessageValidator> validators;
private final Map<ClientId, IncomingMessageHook> hooks;
private final Map<ClientMajorVersion, MessageValidator> validators;
private final Map<ClientMajorVersion, IncomingMessageHook> hooks;
private final AtomicBoolean used = new AtomicBoolean(false);
@Inject
@@ -81,14 +82,15 @@ class ValidationManagerImpl implements ValidationManager, Service,
}
@Override
public void registerMessageValidator(ClientId c, MessageValidator v) {
validators.put(c, v);
public void registerMessageValidator(ClientId c, int majorVersion,
MessageValidator v) {
validators.put(new ClientMajorVersion(c, majorVersion), v);
}
@Override
public void registerIncomingMessageHook(ClientId c,
public void registerIncomingMessageHook(ClientId c, int majorVersion,
IncomingMessageHook hook) {
hooks.put(c, hook);
hooks.put(new ClientMajorVersion(c, majorVersion), hook);
}
private void validateOutstandingMessagesAsync() {
@@ -199,9 +201,11 @@ class ValidationManagerImpl implements ValidationManager, Service,
Message m = messageFactory.createMessage(id, raw);
Group g = db.getGroup(txn, m.getGroupId());
ClientId c = g.getClientId();
int majorVersion = g.getMajorVersion();
Metadata meta =
db.getMessageMetadataForValidator(txn, id);
DeliveryResult result = deliverMessage(txn, m, c, meta);
DeliveryResult result =
deliverMessage(txn, m, c, majorVersion, meta);
if (result.valid) {
pending.addAll(getPendingDependents(txn, id));
if (result.share) {
@@ -237,14 +241,16 @@ class ValidationManagerImpl implements ValidationManager, Service,
@ValidationExecutor
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 (LOG.isLoggable(WARNING))
LOG.warning("No validator for " + g.getClientId().getString());
if (LOG.isLoggable(WARNING)) LOG.warning("No validator for " + cv);
} else {
try {
MessageContext context = v.validateMessage(m, g);
storeMessageContextAsync(m, g.getClientId(), context);
storeMessageContextAsync(m, g.getClientId(),
g.getMajorVersion(), context);
} catch (InvalidMessageException e) {
if (LOG.isLoggable(INFO))
LOG.log(INFO, e.toString(), e);
@@ -256,12 +262,13 @@ class ValidationManagerImpl implements ValidationManager, Service,
}
private void storeMessageContextAsync(Message m, ClientId c,
MessageContext result) {
dbExecutor.execute(() -> storeMessageContext(m, c, result));
int majorVersion, MessageContext result) {
dbExecutor.execute(() ->
storeMessageContext(m, c, majorVersion, result));
}
@DatabaseExecutor
private void storeMessageContext(Message m, ClientId c,
private void storeMessageContext(Message m, ClientId c, int majorVersion,
MessageContext context) {
try {
MessageId id = m.getId();
@@ -292,7 +299,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
Metadata meta = context.getMetadata();
db.mergeMessageMetadata(txn, id, meta);
if (allDelivered) {
DeliveryResult result = deliverMessage(txn, m, c, meta);
DeliveryResult result =
deliverMessage(txn, m, c, majorVersion, meta);
if (result.valid) {
pending = getPendingDependents(txn, id);
if (result.share) {
@@ -324,10 +332,11 @@ class ValidationManagerImpl implements ValidationManager, Service,
@DatabaseExecutor
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
boolean shareMsg = false;
IncomingMessageHook hook = hooks.get(c);
ClientMajorVersion cv = new ClientMajorVersion(c, majorVersion);
IncomingMessageHook hook = hooks.get(cv);
if (hook != null) {
try {
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 ClientId clientId;
private final int majorVersion;
private final GroupId groupId;
private final Group group;
private final Author author;
@@ -106,7 +107,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
public DatabaseComponentImplTest() {
clientId = getClientId();
group = getGroup(clientId);
majorVersion = 123;
group = getGroup(clientId, majorVersion);
groupId = group.getId();
author = getAuthor();
localAuthor = getLocalAuthor();
@@ -175,7 +177,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).containsGroup(txn, groupId);
will(returnValue(true));
// getGroups()
oneOf(database).getGroups(txn, clientId);
oneOf(database).getGroups(txn, clientId, majorVersion);
will(returnValue(singletonList(group)));
// removeGroup()
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); // Second time - not called
assertEquals(singletonList(group),
db.getGroups(transaction, clientId));
db.getGroups(transaction, clientId, majorVersion));
db.removeGroup(transaction, group);
db.removeContact(transaction, contactId);
db.removeLocalAuthor(transaction, localAuthor.getId());

View File

@@ -267,7 +267,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
String name = "getGroups(T, ClientId)";
benchmark(name, db -> {
Connection txn = db.startTransaction();
db.getGroups(txn, pickRandom(clientIds));
db.getGroups(txn, pickRandom(clientIds), 123);
db.commitTransaction(txn);
});
}
@@ -550,7 +550,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
contacts.add(db.getContact(txn, c));
contactGroups.put(c, new ArrayList<>());
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);
messageMeta.put(g.getId(), new ArrayList<>());
contactGroups.get(c).add(g);
@@ -584,7 +584,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
}
}
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);
messageMeta.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 GroupId groupId;
private final ClientId clientId;
private final int majorVersion;
private final Group group;
private final Author author;
private final LocalAuthor localAuthor;
@@ -96,7 +97,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
JdbcDatabaseTest() throws Exception {
clientId = getClientId();
group = getGroup(clientId);
majorVersion = 123;
group = getGroup(clientId, majorVersion);
groupId = group.getId();
author = getAuthor();
localAuthor = getLocalAuthor();
@@ -1460,7 +1462,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.addMessage(txn, message, PENDING, true, contactId);
// Add a second group
Group group1 = getGroup(clientId);
Group group1 = getGroup(clientId, 123);
GroupId groupId1 = group1.getId();
db.addGroup(txn, group1);
@@ -1828,6 +1830,22 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
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
public void testExceptionHandling() throws Exception {
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.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
@@ -29,8 +30,9 @@ import java.util.LinkedHashMap;
import java.util.List;
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_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.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
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 ClientHelper clientHelper = context.mock(ClientHelper.class);
private final ClientVersioningManager clientVersioningManager =
context.mock(ClientVersioningManager.class);
private final MetadataParser metadataParser =
context.mock(MetadataParser.class);
private final ContactGroupFactory contactGroupFactory =
context.mock(ContactGroupFactory.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 BdfDictionary fooPropertiesDict = BdfDictionary.of(
new BdfEntry("fooKey1", "fooValue1"),
@@ -77,49 +81,41 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
private TransportPropertyManagerImpl createInstance() {
context.checking(new Expectations() {{
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID,
CLIENT_VERSION);
MAJOR_VERSION);
will(returnValue(localGroup));
}});
return new TransportPropertyManagerImpl(db, clientHelper,
metadataParser, contactGroupFactory, clock);
clientVersioningManager, metadataParser, contactGroupFactory,
clock);
}
@Test
public void testCreatesGroupsAtStartup() throws Exception {
Transaction txn = new Transaction(null, false);
Contact contact1 = getContact(true);
Contact contact2 = getContact(true);
List<Contact> contacts = Arrays.asList(contact1, contact2);
Group contactGroup1 = getGroup(CLIENT_ID);
Group contactGroup2 = getGroup(CLIENT_ID);
Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{
oneOf(db).containsGroup(txn, localGroup.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, localGroup);
oneOf(db).getContacts(txn);
will(returnValue(contacts));
// The first contact's group has already been set up
will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact1);
will(returnValue(contactGroup1));
oneOf(db).containsGroup(txn, contactGroup1.getId());
will(returnValue(true));
// The second contact's group hasn't been set up
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact2);
will(returnValue(contactGroup2));
oneOf(db).containsGroup(txn, contactGroup2.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, contactGroup2);
oneOf(db).setGroupVisibility(txn, contact2.getId(),
contactGroup2.getId(), SHARED);
MAJOR_VERSION, contact);
will(returnValue(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(),
contactGroup.getId(), SHARED);
}});
// Copy the latest local properties into the group
expectGetLocalProperties(txn);
expectStoreMessage(txn, contactGroup2.getId(), "foo", fooPropertiesDict,
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
1, true, true);
expectStoreMessage(txn, contactGroup2.getId(), "bar", barPropertiesDict,
expectStoreMessage(txn, contactGroup.getId(), "bar", barPropertiesDict,
1, true, true);
TransportPropertyManagerImpl t = createInstance();
@@ -144,16 +140,17 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
public void testCreatesContactGroupWhenAddingContact() throws Exception {
Transaction txn = new Transaction(null, false);
Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID);
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{
// Create the group and share it with the contact
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact);
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(db).containsGroup(txn, contactGroup.getId());
will(returnValue(false));
oneOf(db).addGroup(txn, contactGroup);
oneOf(clientVersioningManager).getClientVisibility(txn,
contact.getId(), CLIENT_ID, MAJOR_VERSION);
will(returnValue(SHARED));
oneOf(db).setGroupVisibility(txn, contact.getId(),
contactGroup.getId(), SHARED);
}});
@@ -172,11 +169,11 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
public void testRemovesGroupWhenRemovingContact() throws Exception {
Transaction txn = new Transaction(null, false);
Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID);
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact);
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(db).removeGroup(txn, contactGroup);
}});
@@ -307,7 +304,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
@Test
public void testStoresRemotePropertiesWithVersion0() throws Exception {
Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID);
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
Transaction txn = new Transaction(null, false);
Map<TransportId, TransportProperties> properties =
new LinkedHashMap<>();
@@ -318,7 +315,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
oneOf(db).getContact(txn, contact.getId());
will(returnValue(contact));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact);
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
}});
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
@@ -421,8 +418,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Contact contact3 = getContact(true);
List<Contact> contacts =
Arrays.asList(contact1, contact2, contact3);
Group contactGroup2 = getGroup(CLIENT_ID);
Group contactGroup3 = getGroup(CLIENT_ID);
Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION);
Group contactGroup3 = getGroup(CLIENT_ID, MAJOR_VERSION);
Map<MessageId, BdfDictionary> messageMetadata3 =
new LinkedHashMap<>();
// A remote update for another transport should be ignored
@@ -456,14 +453,14 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// First contact: skipped because not active
// Second contact: no updates
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact2);
MAJOR_VERSION, contact2);
will(returnValue(contactGroup2));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup2.getId());
will(returnValue(Collections.emptyMap()));
// Third contact: returns an update
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact3);
MAJOR_VERSION, contact3);
will(returnValue(contactGroup3));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup3.getId());
@@ -524,7 +521,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
public void testMergingNewPropertiesCreatesUpdate() throws Exception {
Transaction txn = new Transaction(null, false);
Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID);
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
@@ -538,9 +535,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
fooPropertiesDict, 1, true, false);
// Store the new properties in each contact's group, version 1
oneOf(db).getContacts(txn);
will(returnValue(Collections.singletonList(contact)));
will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact);
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());
@@ -559,7 +556,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
public void testMergingUpdatedPropertiesCreatesUpdate() throws Exception {
Transaction txn = new Transaction(null, false);
Contact contact = getContact(true);
Group contactGroup = getGroup(CLIENT_ID);
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
BdfDictionary oldMetadata = BdfDictionary.of(
new BdfEntry("transportId", "foo"),
new BdfEntry("version", 1),
@@ -597,9 +594,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
oneOf(db).removeMessage(txn, localGroupUpdateId);
// Store the merged properties in each contact's group, version 2
oneOf(db).getContacts(txn);
will(returnValue(Collections.singletonList(contact)));
will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
CLIENT_VERSION, contact);
MAJOR_VERSION, contact);
will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId());

View File

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

View File

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

View File

@@ -53,10 +53,11 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
private final Executor dbExecutor = new ImmediateExecutor();
private final Executor validationExecutor = new ImmediateExecutor();
private final ClientId clientId = getClientId();
private final int majorVersion = 123;
private final MessageId messageId = new MessageId(getRandomId());
private final MessageId messageId1 = 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 long timestamp = System.currentTimeMillis();
private final byte[] raw = new byte[123];
@@ -85,8 +86,8 @@ public class ValidationManagerImplTest extends BrambleMockTestCase {
public void setUp() {
vm = new ValidationManagerImpl(db, dbExecutor, validationExecutor,
messageFactory);
vm.registerMessageValidator(clientId, validator);
vm.registerIncomingMessageHook(clientId, hook);
vm.registerMessageValidator(clientId, majorVersion, validator);
vm.registerIncomingMessageHook(clientId, majorVersion, hook);
}
@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);
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 byte[] descriptor = group.getDescriptor();
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");
/**
* 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).}

View File

@@ -11,7 +11,12 @@ public interface BlogSharingManager extends SharingManager<Blog> {
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");
/**
* 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.

View File

@@ -23,9 +23,14 @@ public interface ForumManager {
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.

View File

@@ -11,7 +11,12 @@ public interface ForumSharingManager extends SharingManager<Forum> {
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");
/**
* 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.
*/
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.
*/

View File

@@ -19,9 +19,14 @@ public interface MessagingManager extends ConversationClient {
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.

View File

@@ -22,9 +22,14 @@ public interface PrivateGroupManager {
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.

View File

@@ -26,9 +26,14 @@ public interface GroupInvitationManager extends ConversationClient {
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

View File

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

View File

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

View File

@@ -18,7 +18,8 @@ import javax.inject.Singleton;
import dagger.Module;
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
public class BlogModule {
@@ -35,10 +36,10 @@ public class BlogModule {
BlogManager provideBlogManager(BlogManagerImpl blogManager,
LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager) {
lifecycleManager.registerClient(blogManager);
contactManager.registerContactHook(blogManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, blogManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
blogManager);
return blogManager;
}
@@ -60,12 +61,11 @@ public class BlogModule {
MessageFactory messageFactory, BlogFactory blogFactory,
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock) {
BlogPostValidator validator = new BlogPostValidator(groupFactory,
messageFactory, blogFactory, clientHelper, metadataEncoder,
clock);
validationManager.registerMessageValidator(CLIENT_ID, validator);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
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_POST_BODY_LENGTH;
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_POST;
import static org.briarproject.briar.api.blog.MessageType.COMMENT;
@@ -195,7 +195,7 @@ class BlogPostValidator extends BdfMessageValidator {
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
// Get and Validate the Wrapped Message
Group wGroup = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
Group wGroup = groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor);
Blog wBlog = blogFactory.parseBlog(wGroup);
BdfList wBodyList = BdfList.of(POST.getInt(), content, signature);
@@ -258,7 +258,7 @@ class BlogPostValidator extends BdfMessageValidator {
MessageId parentId = new MessageId(parentIdBytes);
// Get and Validate the Wrapped Comment
Group wGroup = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
Group wGroup = groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor);
BdfList wBodyList = BdfList.of(COMMENT.getInt(), comment, pOriginalId,
oldId, signature);

View File

@@ -496,7 +496,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener,
}
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.MAX_FORUM_NAME_LENGTH;
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
@NotNullByDefault
@@ -52,7 +52,7 @@ class ForumFactoryImpl implements ForumFactory {
try {
BdfList forum = BdfList.of(name, salt);
byte[] descriptor = clientHelper.toByteArray(forum);
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
Group g = groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor);
return new Forum(g, name, salt);
} catch (FormatException e) {

View File

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

View File

@@ -15,6 +15,7 @@ import dagger.Module;
import dagger.Provides;
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
import static org.briarproject.briar.api.forum.ForumManager.MAJOR_VERSION;
@Module
public class ForumModule {
@@ -30,7 +31,7 @@ public class ForumModule {
@Singleton
ForumManager provideForumManager(ForumManagerImpl forumManager,
ValidationManager validationManager) {
validationManager.registerIncomingMessageHook(CLIENT_ID,
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
forumManager);
return forumManager;
}
@@ -53,7 +54,8 @@ public class ForumModule {
MetadataEncoder metadataEncoder, Clock clock) {
ForumPostValidator validator = new ForumPostValidator(clientHelper,
metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validator);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
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_MASTER_KEY;
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;
@Immutable
@@ -94,7 +94,7 @@ class IntroductionCryptoImpl implements IntroductionCrypto {
LABEL_MASTER_KEY,
remoteEphemeralPublicKey,
keyPair,
new byte[] {CLIENT_VERSION},
new byte[] {MAJOR_VERSION},
alice ? publicKey : remotePublicKey,
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.sync.Client;
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.Message;
import org.briarproject.bramble.api.sync.MessageId;
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.SessionId;
import org.briarproject.briar.api.introduction.IntroductionManager;
@@ -44,7 +47,6 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
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.INTRODUCER;
import static org.briarproject.briar.introduction.IntroducerState.START;
@@ -59,8 +61,10 @@ import static org.briarproject.briar.introduction.MessageType.REQUEST;
@Immutable
@NotNullByDefault
class IntroductionManagerImpl extends ConversationClientImpl
implements IntroductionManager, Client, ContactHook {
implements IntroductionManager, Client, ContactHook,
ClientVersioningHook {
private final ClientVersioningManager clientVersioningManager;
private final ContactGroupFactory contactGroupFactory;
private final ContactManager contactManager;
private final MessageParser messageParser;
@@ -77,6 +81,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
IntroductionManagerImpl(
DatabaseComponent db,
ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser,
MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory,
@@ -89,6 +94,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
IntroductionCrypto crypto,
IdentityManager identityManager) {
super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.contactGroupFactory = contactGroupFactory;
this.contactManager = contactManager;
this.messageParser = messageParser;
@@ -99,7 +105,7 @@ class IntroductionManagerImpl extends ConversationClientImpl
this.crypto = crypto;
this.identityManager = identityManager;
this.localGroup =
contactGroupFactory.createLocalGroup(CLIENT_ID, CLIENT_VERSION);
contactGroupFactory.createLocalGroup(CLIENT_ID, MAJOR_VERSION);
}
@Override
@@ -112,13 +118,14 @@ class IntroductionManagerImpl extends ConversationClientImpl
}
@Override
// TODO adapt to use upcoming ClientVersioning client
public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact
Group g = getContactGroup(c);
// Store the group and share it with the contact
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
BdfDictionary meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
@@ -138,10 +145,18 @@ class IntroductionManagerImpl extends ConversationClientImpl
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
public Group getContactGroup(Contact c) {
return contactGroupFactory
.createContactGroup(CLIENT_ID, CLIENT_VERSION, c);
.createContactGroup(CLIENT_ID, MAJOR_VERSION, c);
}
@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.sync.ValidationManager;
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.messaging.ConversationManager;
@@ -16,6 +17,8 @@ import dagger.Module;
import dagger.Provides;
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
public class IntroductionModule {
@@ -32,13 +35,11 @@ public class IntroductionModule {
IntroductionValidator provideValidator(ValidationManager validationManager,
MessageEncoder messageEncoder, MetadataEncoder metadataEncoder,
ClientHelper clientHelper, Clock clock) {
IntroductionValidator introductionValidator =
new IntroductionValidator(messageEncoder, clientHelper,
metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID,
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
introductionValidator);
return introductionValidator;
}
@@ -48,13 +49,15 @@ public class IntroductionModule {
LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager,
ConversationManager conversationManager,
ClientVersioningManager clientVersioningManager,
IntroductionManagerImpl introductionManager) {
lifecycleManager.registerClient(introductionManager);
contactManager.registerContactHook(introductionManager);
validationManager.registerIncomingMessageHook(CLIENT_ID,
introductionManager);
MAJOR_VERSION, introductionManager);
conversationManager.registerConversationClient(introductionManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, 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.sync.Client;
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.Message;
import org.briarproject.bramble.api.sync.MessageId;
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.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
@@ -33,21 +36,23 @@ import java.util.Map;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
@Immutable
@NotNullByDefault
class MessagingManagerImpl extends ConversationClientImpl
implements MessagingManager, Client, ContactHook {
implements MessagingManager, Client, ContactHook, ClientVersioningHook {
private final ClientVersioningManager clientVersioningManager;
private final ContactGroupFactory contactGroupFactory;
@Inject
MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory) {
super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.contactGroupFactory = contactGroupFactory;
}
@@ -55,36 +60,36 @@ class MessagingManagerImpl extends ConversationClientImpl
public void createLocalState(Transaction txn) throws DbException {
// Create a local group to indicate that we've set this client up
Group localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
CLIENT_VERSION);
MAJOR_VERSION);
if (db.containsGroup(txn, localGroup.getId())) return;
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);
}
@Override
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 {
// 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);
} catch (FormatException e) {
throw new RuntimeException(e);
throw new AssertionError(e);
}
}
@Override
public Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID,
CLIENT_VERSION, c);
MAJOR_VERSION, c);
}
@Override
@@ -92,6 +97,14 @@ class MessagingManagerImpl extends ConversationClientImpl
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
protected boolean incomingMessage(Transaction txn, Message m, BdfList body,
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.sync.ValidationManager;
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.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
@@ -16,7 +17,9 @@ import javax.inject.Singleton;
import dagger.Module;
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
public class MessagingModule {
@@ -43,7 +46,8 @@ public class MessagingModule {
Clock clock) {
PrivateMessageValidator validator = new PrivateMessageValidator(
clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validator);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator;
}
@@ -52,12 +56,15 @@ public class MessagingModule {
MessagingManager getMessagingManager(LifecycleManager lifecycleManager,
ContactManager contactManager, ValidationManager validationManager,
ConversationManager conversationManager,
ClientVersioningManager clientVersioningManager,
MessagingManagerImpl messagingManager) {
lifecycleManager.registerClient(messagingManager);
contactManager.registerContactHook(messagingManager);
validationManager
.registerIncomingMessageHook(CLIENT_ID, messagingManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
messagingManager);
conversationManager.registerConversationClient(messagingManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, 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.MAX_GROUP_NAME_LENGTH;
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
@NotNullByDefault
@@ -57,7 +57,7 @@ class PrivateGroupFactoryImpl implements PrivateGroupFactory {
BdfList creatorList = clientHelper.toList(creator);
BdfList group = BdfList.of(creatorList, name, salt);
byte[] descriptor = clientHelper.toByteArray(group);
Group g = groupFactory.createGroup(CLIENT_ID, CLIENT_VERSION,
Group g = groupFactory.createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor);
return new PrivateGroup(g, name, creator, salt);
} catch (FormatException e) {

View File

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

View File

@@ -16,6 +16,7 @@ import dagger.Module;
import dagger.Provides;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.MAJOR_VERSION;
@Module
public class PrivateGroupModule {
@@ -32,7 +33,8 @@ public class PrivateGroupModule {
PrivateGroupManager provideGroupManager(
PrivateGroupManagerImpl groupManager,
ValidationManager validationManager) {
validationManager.registerIncomingMessageHook(CLIENT_ID, groupManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
groupManager);
return groupManager;
}
@@ -58,7 +60,8 @@ public class PrivateGroupModule {
GroupMessageValidator validator = new GroupMessageValidator(
privateGroupFactory, clientHelper, metadataEncoder, clock,
groupInvitationFactory);
validationManager.registerMessageValidator(CLIENT_ID, validator);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
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.MessageId;
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.privategroup.GroupMessage;
import org.briarproject.briar.api.privategroup.GroupMessageFactory;
@@ -28,6 +29,8 @@ import java.util.Map;
import javax.annotation.Nullable;
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.MessageType.ABORT;
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 MessageTracker messageTracker;
private final ClientVersioningManager clientVersioningManager;
private final GroupMessageFactory groupMessageFactory;
private final IdentityManager identityManager;
private final MessageParser messageParser;
@@ -52,6 +56,7 @@ abstract class AbstractProtocolEngine<S extends Session>
private final Clock clock;
AbstractProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory,
@@ -60,6 +65,7 @@ abstract class AbstractProtocolEngine<S extends Session>
Clock clock) {
this.db = db;
this.clientHelper = clientHelper;
this.clientVersioningManager = clientVersioningManager;
this.privateGroupManager = privateGroupManager;
this.privateGroupFactory = privateGroupFactory;
this.groupMessageFactory = groupMessageFactory;
@@ -90,10 +96,14 @@ abstract class AbstractProtocolEngine<S extends Session>
return expected != null && dependency.equals(expected);
}
void setPrivateGroupVisibility(Transaction txn, S session, Visibility v)
throws DbException, FormatException {
void setPrivateGroupVisibility(Transaction txn, S session,
Visibility preferred) throws DbException, FormatException {
// Apply min of preferred visibility and client's visibility
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,

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.sync.Message;
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.ProtocolStateException;
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> {
CreatorProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory,
IdentityManager identityManager, MessageParser messageParser,
MessageEncoder messageEncoder, MessageTracker messageTracker,
Clock clock) {
super(db, clientHelper, privateGroupManager, privateGroupFactory,
groupMessageFactory, identityManager, messageParser,
messageEncoder, messageTracker, clock);
super(db, clientHelper, clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, clock);
}
@Override

View File

@@ -2,19 +2,30 @@ package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group.Visibility;
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
@NotNullByDefault
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 Visibility visibility;
CreatorState(int value) {
CreatorState(int value, Visibility visibility) {
this.value = value;
this.visibility = visibility;
}
@Override
@@ -22,6 +33,11 @@ enum CreatorState implements State {
return value;
}
@Override
public Visibility getVisibility() {
return visibility;
}
static CreatorState fromValue(int value) throws FormatException {
for (CreatorState s : values()) if (s.value == value) return s;
throw new FormatException();

View File

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

View File

@@ -17,7 +17,7 @@ import javax.annotation.concurrent.Immutable;
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_VERSION;
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.MAJOR_VERSION;
@Immutable
@NotNullByDefault
@@ -53,7 +53,7 @@ class GroupInvitationFactoryImpl implements GroupInvitationFactory {
public BdfList createInviteToken(AuthorId creatorId, AuthorId memberId,
GroupId privateGroupId, long timestamp) {
Group contactGroup = contactGroupFactory.createContactGroup(CLIENT_ID,
CLIENT_VERSION, creatorId, memberId);
MAJOR_VERSION, creatorId, memberId);
return BdfList.of(
timestamp,
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.sync.Client;
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.Message;
import org.briarproject.bramble.api.sync.MessageId;
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.SessionId;
import org.briarproject.briar.api.privategroup.PrivateGroup;
@@ -36,6 +39,7 @@ import org.briarproject.briar.client.ConversationClientImpl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -59,8 +63,9 @@ import static org.briarproject.briar.privategroup.invitation.Role.PEER;
@NotNullByDefault
class GroupInvitationManagerImpl extends ConversationClientImpl
implements GroupInvitationManager, Client, ContactHook,
PrivateGroupHook {
PrivateGroupHook, ClientVersioningHook {
private final ClientVersioningManager clientVersioningManager;
private final ContactGroupFactory contactGroupFactory;
private final PrivateGroupFactory privateGroupFactory;
private final PrivateGroupManager privateGroupManager;
@@ -73,8 +78,9 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
@Inject
GroupInvitationManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, MetadataParser metadataParser,
MessageTracker messageTracker,
ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory,
PrivateGroupFactory privateGroupFactory,
PrivateGroupManager privateGroupManager,
@@ -82,6 +88,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
SessionEncoder sessionEncoder,
ProtocolEngineFactory engineFactory) {
super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.contactGroupFactory = contactGroupFactory;
this.privateGroupFactory = privateGroupFactory;
this.privateGroupManager = privateGroupManager;
@@ -97,10 +104,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
public void createLocalState(Transaction txn) throws DbException {
// Create a local group to indicate that we've set this client up
Group localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
CLIENT_VERSION);
MAJOR_VERSION);
if (db.containsGroup(txn, localGroup.getId())) return;
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);
}
@@ -108,11 +115,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
public void addingContact(Transaction txn, Contact c) throws DbException {
// 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);
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 meta = new BdfDictionary();
meta.put(GROUP_KEY_CONTACT_ID, c.getId().getInt());
@@ -122,7 +128,8 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
throw new AssertionError(e);
}
// 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()))
addingMember(txn, pg.getId(), c);
}
@@ -137,7 +144,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
@Override
public Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID,
CLIENT_VERSION, c);
MAJOR_VERSION, c);
}
@Override
@@ -461,8 +468,13 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
SessionId sessionId = getSessionId(privateGroupId);
Transaction txn = db.startTransaction(true);
try {
Visibility client = clientVersioningManager.getClientVisibility(txn,
c.getId(), PrivateGroupManager.CLIENT_ID,
PrivateGroupManager.MAJOR_VERSION);
StoredSession ss = getSession(txn, contactGroupId, sessionId);
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 (ss == null) return true;
// 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 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.sync.ValidationManager;
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.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
@@ -19,6 +20,8 @@ import dagger.Module;
import dagger.Provides;
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
public class GroupInvitationModule {
@@ -37,13 +40,22 @@ public class GroupInvitationModule {
LifecycleManager lifecycleManager,
ValidationManager validationManager, ContactManager contactManager,
PrivateGroupManager privateGroupManager,
ConversationManager conversationManager) {
ConversationManager conversationManager,
ClientVersioningManager clientVersioningManager) {
lifecycleManager.registerClient(groupInvitationManager);
validationManager.registerIncomingMessageHook(CLIENT_ID,
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
groupInvitationManager);
contactManager.registerContactHook(groupInvitationManager);
privateGroupManager.registerPrivateGroupHook(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;
}
@@ -57,7 +69,8 @@ public class GroupInvitationModule {
GroupInvitationValidator validator = new GroupInvitationValidator(
clientHelper, metadataEncoder, clock, privateGroupFactory,
messageEncoder);
validationManager.registerMessageValidator(CLIENT_ID, validator);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
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.MessageId;
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.ProtocolStateException;
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> {
InviteeProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory,
IdentityManager identityManager, MessageParser messageParser,
MessageEncoder messageEncoder, MessageTracker messageTracker,
Clock clock) {
super(db, clientHelper, privateGroupManager, privateGroupFactory,
groupMessageFactory, identityManager, messageParser,
messageEncoder, messageTracker, clock);
super(db, clientHelper, clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, clock);
}
@Override
@@ -212,6 +214,12 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
throws DbException {
// Send a LEAVE message
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
return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(),
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.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group.Visibility;
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
@NotNullByDefault
enum InviteeState implements State {
START(0), INVITED(1), ACCEPTED(2), JOINED(3), LEFT(4), DISSOLVED(5),
ERROR(6);
START(0, INVISIBLE),
INVITED(1, INVISIBLE),
ACCEPTED(2, VISIBLE),
JOINED(3, SHARED),
LEFT(4, INVISIBLE),
DISSOLVED(5, INVISIBLE),
ERROR(6, INVISIBLE);
private final int value;
private final Visibility visibility;
InviteeState(int value) {
InviteeState(int value, Visibility visibility) {
this.value = value;
this.visibility = visibility;
}
@Override
@@ -23,6 +35,11 @@ enum InviteeState implements State {
return value;
}
@Override
public Visibility getVisibility() {
return visibility;
}
static InviteeState fromValue(int value) throws FormatException {
for (InviteeState s : values()) if (s.value == value) return s;
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.sync.Message;
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.ProtocolStateException;
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> {
PeerProtocolEngine(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory,
IdentityManager identityManager, MessageParser messageParser,
MessageEncoder messageEncoder, MessageTracker messageTracker,
Clock clock) {
super(db, clientHelper, privateGroupManager, privateGroupFactory,
groupMessageFactory, identityManager, messageParser,
messageEncoder, messageTracker, clock);
super(db, clientHelper, clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, clock);
}
@Override

View File

@@ -2,20 +2,32 @@ package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group.Visibility;
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
@NotNullByDefault
enum PeerState implements State {
START(0), AWAIT_MEMBER(1), NEITHER_JOINED(2), LOCAL_JOINED(3),
BOTH_JOINED(4), LOCAL_LEFT(5), ERROR(6);
START(0, INVISIBLE),
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 Visibility visibility;
PeerState(int value) {
PeerState(int value, Visibility visibility) {
this.value = value;
this.visibility = visibility;
}
@Override
@@ -23,6 +35,11 @@ enum PeerState implements State {
return value;
}
@Override
public Visibility getVisibility() {
return visibility;
}
static PeerState fromValue(int value) throws FormatException {
for (PeerState s : values()) if (s.value == value) return s;
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.nullsafety.NotNullByDefault;
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.privategroup.GroupMessageFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
@@ -19,6 +20,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
private final DatabaseComponent db;
private final ClientHelper clientHelper;
private final ClientVersioningManager clientVersioningManager;
private final PrivateGroupManager privateGroupManager;
private final PrivateGroupFactory privateGroupFactory;
private final GroupMessageFactory groupMessageFactory;
@@ -30,6 +32,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
@Inject
ProtocolEngineFactoryImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
PrivateGroupManager privateGroupManager,
PrivateGroupFactory privateGroupFactory,
GroupMessageFactory groupMessageFactory,
@@ -38,6 +41,7 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
Clock clock) {
this.db = db;
this.clientHelper = clientHelper;
this.clientVersioningManager = clientVersioningManager;
this.privateGroupManager = privateGroupManager;
this.privateGroupFactory = privateGroupFactory;
this.groupMessageFactory = groupMessageFactory;
@@ -50,21 +54,24 @@ class ProtocolEngineFactoryImpl implements ProtocolEngineFactory {
@Override
public ProtocolEngine<CreatorSession> createCreatorEngine() {
return new CreatorProtocolEngine(db, clientHelper, privateGroupManager,
return new CreatorProtocolEngine(db, clientHelper,
clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, clock);
}
@Override
public ProtocolEngine<InviteeSession> createInviteeEngine() {
return new InviteeProtocolEngine(db, clientHelper, privateGroupManager,
return new InviteeProtocolEngine(db, clientHelper,
clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager,
messageParser, messageEncoder, messageTracker, clock);
}
@Override
public ProtocolEngine<PeerSession> createPeerEngine() {
return new PeerProtocolEngine(db, clientHelper, privateGroupManager,
return new PeerProtocolEngine(db, clientHelper,
clientVersioningManager, privateGroupManager,
privateGroupFactory, groupMessageFactory, identityManager,
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.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_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.privategroup.invitation.GroupInvitationConstants.SESSION_KEY_LOCAL_TIMESTAMP;
@@ -28,6 +29,7 @@ class SessionEncoderImpl implements SessionEncoder {
@Override
public BdfDictionary encodeSession(Session s) {
BdfDictionary d = new BdfDictionary();
d.put(SESSION_KEY_IS_SESSION, true);
d.put(SESSION_KEY_SESSION_ID, s.getPrivateGroupId());
d.put(SESSION_KEY_PRIVATE_GROUP_ID, s.getPrivateGroupId());
MessageId lastLocalMessageId = s.getLastLocalMessageId();

View File

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

View File

@@ -13,6 +13,7 @@ import javax.annotation.concurrent.Immutable;
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_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_REMOTE_MESSAGE_ID;
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));
}
@Override
public BdfDictionary getAllSessionsQuery() {
return BdfDictionary.of(new BdfEntry(SESSION_KEY_IS_SESSION, true));
}
@Override
public Role getRole(BdfDictionary d) throws FormatException {
return Role.fromValue(d.getLong(SESSION_KEY_ROLE).intValue());

View File

@@ -1,6 +1,10 @@
package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.sync.Group.Visibility;
interface State {
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.MessageId;
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.BlogInvitationResponse;
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.inject.Inject;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
import static org.briarproject.briar.api.blog.BlogManager.MAJOR_VERSION;
@Immutable
@NotNullByDefault
class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
@@ -31,13 +35,14 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
invitationFactory;
@Inject
BlogProtocolEngineImpl(DatabaseComponent db,
ClientHelper clientHelper, MessageEncoder messageEncoder,
MessageParser<Blog> messageParser, MessageTracker messageTracker,
Clock clock, BlogManager blogManager,
BlogProtocolEngineImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MessageEncoder messageEncoder, MessageParser<Blog> messageParser,
MessageTracker messageTracker, Clock clock, BlogManager blogManager,
InvitationFactory<Blog, BlogInvitationResponse> invitationFactory) {
super(db, clientHelper, messageEncoder, messageParser, messageTracker,
clock);
super(db, clientHelper, clientVersioningManager, messageEncoder,
messageParser, messageTracker, clock, CLIENT_ID,
MAJOR_VERSION);
this.blogManager = blogManager;
this.invitationFactory = invitationFactory;
}
@@ -46,8 +51,8 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
Event getInvitationRequestReceivedEvent(InviteMessage<Blog> m,
ContactId contactId, boolean available, boolean canBeOpened) {
InvitationRequest<Blog> request = invitationFactory
.createInvitationRequest(false, false, true, false, m,
contactId, available, canBeOpened);
.createInvitationRequest(false, false, true, false, m,
contactId, available, canBeOpened);
return new BlogInvitationRequestReceivedEvent(m.getShareable(),
contactId, request);
}
@@ -74,7 +79,7 @@ class BlogProtocolEngineImpl extends ProtocolEngineImpl<Blog> {
@Override
protected ClientId getShareableClientId() {
return BlogManager.CLIENT_ID;
return CLIENT_ID;
}
@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.nullsafety.NotNullByDefault;
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.BlogInvitationResponse;
import org.briarproject.briar.api.blog.BlogManager;
@@ -32,6 +33,7 @@ class BlogSharingManagerImpl extends SharingManagerImpl<Blog>
@Inject
BlogSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageParser<Blog> messageParser,
SessionEncoder sessionEncoder, SessionParser sessionParser,
MessageTracker messageTracker,
@@ -39,9 +41,9 @@ class BlogSharingManagerImpl extends SharingManagerImpl<Blog>
ProtocolEngine<Blog> engine,
InvitationFactory<Blog, BlogInvitationResponse> invitationFactory,
IdentityManager identityManager, BlogManager blogManager) {
super(db, clientHelper, metadataParser, messageParser, sessionEncoder,
sessionParser, messageTracker, contactGroupFactory, engine,
invitationFactory);
super(db, clientHelper, clientVersioningManager, metadataParser,
messageParser, sessionEncoder, sessionParser, messageTracker,
contactGroupFactory, engine, invitationFactory);
this.identityManager = identityManager;
this.blogManager = blogManager;
}
@@ -52,27 +54,31 @@ class BlogSharingManagerImpl extends SharingManagerImpl<Blog>
}
@Override
protected int getClientVersion() {
return CLIENT_VERSION;
protected int getMajorVersion() {
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
public void addingContact(Transaction txn, Contact c) throws DbException {
// Return if we've already set things up for this contact
if (db.containsGroup(txn, getContactGroup(c).getId())) return;
// creates a group to share with the contact
// Create a group to share with the contact
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);
Blog ourBlog = blogManager.getPersonalBlog(localAuthor);
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 {
preShareShareable(txn, c, ourBlog);
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.MessageId;
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.forum.Forum;
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.inject.Inject;
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
import static org.briarproject.briar.api.forum.ForumManager.MAJOR_VERSION;
@Immutable
@NotNullByDefault
class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
@@ -32,12 +36,15 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
@Inject
ForumProtocolEngineImpl(DatabaseComponent db,
ClientHelper clientHelper, MessageEncoder messageEncoder,
MessageParser<Forum> messageParser, MessageTracker messageTracker,
Clock clock, ForumManager forumManager,
ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MessageEncoder messageEncoder, MessageParser<Forum> messageParser,
MessageTracker messageTracker, Clock clock,
ForumManager forumManager,
InvitationFactory<Forum, ForumInvitationResponse> invitationFactory) {
super(db, clientHelper, messageEncoder, messageParser, messageTracker,
clock);
super(db, clientHelper, clientVersioningManager, messageEncoder,
messageParser, messageTracker, clock, CLIENT_ID,
MAJOR_VERSION);
this.forumManager = forumManager;
this.invitationFactory = invitationFactory;
}
@@ -46,8 +53,8 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
Event getInvitationRequestReceivedEvent(InviteMessage<Forum> m,
ContactId contactId, boolean available, boolean canBeOpened) {
InvitationRequest<Forum> request = invitationFactory
.createInvitationRequest(false, false, true, false, m,
contactId, available, canBeOpened);
.createInvitationRequest(false, false, true, false, m,
contactId, available, canBeOpened);
return new ForumInvitationRequestReceivedEvent(m.getShareable(),
contactId, request);
}
@@ -74,7 +81,7 @@ class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
@Override
protected ClientId getShareableClientId() {
return ForumManager.CLIENT_ID;
return CLIENT_ID;
}
@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.nullsafety.NotNullByDefault;
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.forum.Forum;
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.ForumSharingManager;
@@ -22,15 +24,16 @@ class ForumSharingManagerImpl extends SharingManagerImpl<Forum>
@Inject
ForumSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageParser<Forum> messageParser,
SessionEncoder sessionEncoder, SessionParser sessionParser,
MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory,
ProtocolEngine<Forum> engine,
InvitationFactory<Forum, ForumInvitationResponse> invitationFactory) {
super(db, clientHelper, metadataParser, messageParser, sessionEncoder,
sessionParser, messageTracker, contactGroupFactory, engine,
invitationFactory);
super(db, clientHelper, clientVersioningManager, metadataParser,
messageParser, sessionEncoder, sessionParser, messageTracker,
contactGroupFactory, engine, invitationFactory);
}
@Override
@@ -39,8 +42,18 @@ class ForumSharingManagerImpl extends SharingManagerImpl<Forum>
}
@Override
protected int getClientVersion() {
return CLIENT_VERSION;
protected int getMajorVersion() {
return MAJOR_VERSION;
}
@Override
protected ClientId getShareableClientId() {
return ForumManager.CLIENT_ID;
}
@Override
protected int getShareableMajorVersion() {
return ForumManager.MAJOR_VERSION;
}
@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.MessageId;
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.ProtocolStateException;
import org.briarproject.briar.api.sharing.Shareable;
@@ -52,19 +53,27 @@ abstract class ProtocolEngineImpl<S extends Shareable>
protected final ClientHelper clientHelper;
protected final MessageParser<S> messageParser;
private final ClientVersioningManager clientVersioningManager;
private final MessageEncoder messageEncoder;
private final MessageTracker messageTracker;
private final Clock clock;
private final ClientId shareableClientId;
private final int shareableClientVersion;
ProtocolEngineImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MessageEncoder messageEncoder, MessageParser<S> messageParser,
MessageTracker messageTracker, Clock clock) {
MessageTracker messageTracker, Clock clock,
ClientId shareableClientId, int shareableClientVersion) {
this.db = db;
this.clientHelper = clientHelper;
this.clientVersioningManager = clientVersioningManager;
this.messageEncoder = messageEncoder;
this.messageParser = messageParser;
this.messageTracker = messageTracker;
this.clock = clock;
this.shareableClientId = shareableClientId;
this.shareableClientVersion = shareableClientVersion;
}
@Override
@@ -598,9 +607,13 @@ abstract class ProtocolEngineImpl<S extends Shareable>
}
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());
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)

View File

@@ -9,6 +9,7 @@ import javax.inject.Inject;
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_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_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LOCAL_TIMESTAMP;
@@ -27,6 +28,7 @@ class SessionEncoderImpl implements SessionEncoder {
@Override
public BdfDictionary encodeSession(Session s) {
BdfDictionary d = new BdfDictionary();
d.put(SESSION_KEY_IS_SESSION, true);
d.put(SESSION_KEY_SESSION_ID, s.getShareableId());
d.put(SESSION_KEY_SHAREABLE_ID, s.getShareableId());
MessageId lastLocalMessageId = s.getLastLocalMessageId();

View File

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

View File

@@ -13,6 +13,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
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_REMOTE_MESSAGE_ID;
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));
}
@Override
public BdfDictionary getAllSessionsQuery() {
return BdfDictionary.of(new BdfEntry(SESSION_KEY_IS_SESSION, true));
}
@Override
public Session parseSession(GroupId contactGroupId,
BdfDictionary d) throws FormatException {

View File

@@ -18,6 +18,7 @@ interface SharingConstants {
String MSG_KEY_INVITATION_ACCEPTED = "invitationAccepted";
// Session keys
String SESSION_KEY_IS_SESSION = "isSession";
String SESSION_KEY_STATE = "state";
String SESSION_KEY_SESSION_ID = "sessionId";
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.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.Message;
import org.briarproject.bramble.api.sync.MessageId;
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.SessionId;
import org.briarproject.briar.api.sharing.InvitationMessage;
@@ -52,8 +55,10 @@ import static org.briarproject.briar.sharing.State.SHARING;
@NotNullByDefault
abstract class SharingManagerImpl<S extends Shareable>
extends ConversationClientImpl
implements SharingManager<S>, Client, ContactHook {
implements SharingManager<S>, Client, ContactHook,
ClientVersioningHook {
private final ClientVersioningManager clientVersioningManager;
private final MessageParser<S> messageParser;
private final SessionEncoder sessionEncoder;
private final SessionParser sessionParser;
@@ -62,12 +67,14 @@ abstract class SharingManagerImpl<S extends Shareable>
private final InvitationFactory<S, ?> invitationFactory;
SharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageParser<S> messageParser,
SessionEncoder sessionEncoder, SessionParser sessionParser,
MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory, ProtocolEngine<S> engine,
InvitationFactory<S, ?> invitationFactory) {
super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.messageParser = messageParser;
this.sessionEncoder = sessionEncoder;
this.sessionParser = sessionParser;
@@ -78,48 +85,51 @@ abstract class SharingManagerImpl<S extends Shareable>
protected abstract ClientId getClientId();
protected abstract int getClientVersion();
protected abstract int getMajorVersion();
protected abstract ClientId getShareableClientId();
protected abstract int getShareableMajorVersion();
@Override
public void createLocalState(Transaction txn) throws DbException {
// Create a local group to indicate that we've set this client up
Group localGroup = contactGroupFactory.createLocalGroup(getClientId(),
getClientVersion());
getMajorVersion());
if (db.containsGroup(txn, localGroup.getId())) return;
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);
}
@Override
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 {
// 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);
} catch (FormatException e) {
throw new DbException(e);
throw new AssertionError(e);
}
}
@Override
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));
}
@Override
public Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(getClientId(),
getClientVersion(), c);
getMajorVersion(), c);
}
@Override
@@ -152,17 +162,21 @@ abstract class SharingManagerImpl<S extends Shareable>
*/
void preShareShareable(Transaction txn, Contact c, S shareable)
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();
StoredSession existingSession = getSession(txn, contactGroupId,
getSessionId(shareable.getId()));
if (existingSession != null) return;
// add and shares the shareable with the Contact
// Add the shareable
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,
shareable.getId(), null, null, 0, 0);
MessageId storageId = createStorageId(txn, contactGroupId);
@@ -446,6 +460,10 @@ abstract class SharingManagerImpl<S extends Shareable>
private boolean canBeShared(Transaction txn, GroupId g, Contact c)
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();
SessionId sessionId = getSessionId(g);
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 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.sync.ValidationManager;
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.BlogFactory;
import org.briarproject.briar.api.blog.BlogInvitationResponse;
@@ -59,11 +60,11 @@ public class SharingModule {
ValidationManager validationManager, MessageEncoder messageEncoder,
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock, BlogFactory blogFactory) {
BlogSharingValidator validator =
new BlogSharingValidator(messageEncoder, clientHelper,
metadataEncoder, clock, blogFactory);
BlogSharingValidator validator = new BlogSharingValidator(
messageEncoder, clientHelper, metadataEncoder, clock,
blogFactory);
validationManager.registerMessageValidator(BlogSharingManager.CLIENT_ID,
validator);
BlogSharingManager.MAJOR_VERSION, validator);
return validator;
}
@@ -73,14 +74,23 @@ public class SharingModule {
LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager,
ConversationManager conversationManager, BlogManager blogManager,
ClientVersioningManager clientVersioningManager,
BlogSharingManagerImpl blogSharingManager) {
lifecycleManager.registerClient(blogSharingManager);
contactManager.registerContactHook(blogSharingManager);
validationManager.registerIncomingMessageHook(
BlogSharingManager.CLIENT_ID, blogSharingManager);
BlogSharingManager.CLIENT_ID, BlogSharingManager.MAJOR_VERSION,
blogSharingManager);
conversationManager.registerConversationClient(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;
}
@@ -108,12 +118,12 @@ public class SharingModule {
ValidationManager validationManager, MessageEncoder messageEncoder,
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock, ForumFactory forumFactory) {
ForumSharingValidator validator =
new ForumSharingValidator(messageEncoder, clientHelper,
metadataEncoder, clock, forumFactory);
validationManager
.registerMessageValidator(ForumSharingManager.CLIENT_ID,
validator);
ForumSharingValidator validator = new ForumSharingValidator(
messageEncoder, clientHelper, metadataEncoder, clock,
forumFactory);
validationManager.registerMessageValidator(
ForumSharingManager.CLIENT_ID,
ForumSharingManager.MAJOR_VERSION, validator);
return validator;
}
@@ -123,15 +133,23 @@ public class SharingModule {
LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager,
ConversationManager conversationManager, ForumManager forumManager,
ClientVersioningManager clientVersioningManager,
ForumSharingManagerImpl forumSharingManager) {
lifecycleManager.registerClient(forumSharingManager);
contactManager.registerContactHook(forumSharingManager);
validationManager.registerIncomingMessageHook(
ForumSharingManager.CLIENT_ID, forumSharingManager);
ForumSharingManager.CLIENT_ID,
ForumSharingManager.MAJOR_VERSION, forumSharingManager);
conversationManager.registerConversationClient(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;
}

View File

@@ -2,26 +2,41 @@ package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group.Visibility;
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
@NotNullByDefault
enum State {
START(0), LOCAL_INVITED(1), REMOTE_INVITED(2), SHARING(3), LOCAL_LEFT(4),
REMOTE_HANGING(5);
START(0, INVISIBLE),
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 Visibility visibility;
State(int value) {
State(int value, Visibility visibility) {
this.value = value;
this.visibility = visibility;
}
public int getValue() {
return value;
}
public Visibility getVisibility() {
return visibility;
}
public boolean canInvite() {
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.IdentityManager;
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.MessageId;
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.MAX_BLOG_COMMENT_LENGTH;
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.POST;
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) {
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) {

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_RSS_FEED;
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_POST;
import static org.briarproject.briar.api.blog.MessageType.COMMENT;
@@ -64,7 +64,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
private final String body = getRandomString(42);
public BlogPostValidatorTest() {
group = getGroup(CLIENT_ID);
group = getGroup(CLIENT_ID, MAJOR_VERSION);
descriptor = group.getDescriptor();
author = getAuthor();
authorList = BdfList.of(
@@ -206,7 +206,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
byte[] originalBody = getRandomBytes(42);
context.checking(new Expectations() {{
oneOf(groupFactory).createGroup(CLIENT_ID, CLIENT_VERSION,
oneOf(groupFactory).createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor);
will(returnValue(b.getGroup()));
oneOf(blogFactory).parseBlog(b.getGroup());
@@ -250,7 +250,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
byte[] originalBody = getRandomBytes(42);
context.checking(new Expectations() {{
oneOf(groupFactory).createGroup(CLIENT_ID, CLIENT_VERSION,
oneOf(groupFactory).createGroup(CLIENT_ID, MAJOR_VERSION,
descriptor);
will(returnValue(blog.getGroup()));
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.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_VERSION;
import static org.briarproject.briar.api.feed.FeedManager.MAJOR_VERSION;
public class FeedManagerImplTest extends BrambleMockTestCase {
@@ -61,9 +61,10 @@ public class FeedManagerImplTest extends BrambleMockTestCase {
private final Clock clock = context.mock(Clock.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 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 LocalAuthor localAuthor = getLocalAuthor();
private final Blog blog = new Blog(blogGroup, localAuthor, true);
@@ -131,7 +132,7 @@ public class FeedManagerImplTest extends BrambleMockTestCase {
private void expectGetLocalGroup() {
context.checking(new Expectations() {{
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID,
CLIENT_VERSION);
MAJOR_VERSION);
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.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
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.TestUtils;
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.BlogManager;
import org.briarproject.briar.api.blog.BlogPostHeader;
@@ -113,15 +114,16 @@ public class FeedManagerIntegrationTest extends BriarTestCase {
protected void injectEagerSingletons(
FeedManagerIntegrationTestComponent component) {
component.inject(new FeedModule.EagerSingletons());
component.inject(new BlogModule.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 LifecycleModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new SystemModule.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.client.ClientModule;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.data.DataModule;
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.TestSocksModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.feed.FeedManager;
import org.briarproject.briar.blog.BlogModule;
@@ -33,32 +35,34 @@ import dagger.Component;
TestSecureRandomModule.class,
TestSocksModule.class,
TestDnsModule.class,
LifecycleModule.class,
BriarClientModule.class,
ClientModule.class,
ContactModule.class,
CryptoModule.class,
CryptoExecutorModule.class,
BlogModule.class,
FeedModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
IdentityModule.class,
LifecycleModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
TransportModule.class,
VersioningModule.class
})
interface FeedManagerIntegrationTestComponent {
void inject(FeedManagerIntegrationTest testCase);
void inject(FeedModule.EagerSingletons init);
void inject(BlogModule.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);
@@ -70,6 +74,8 @@ interface FeedManagerIntegrationTestComponent {
void inject(TransportModule.EagerSingletons init);
void inject(VersioningModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
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.getTransportPropertiesMap;
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.LOCAL_DECLINED;
import static org.briarproject.briar.introduction.IntroducerState.A_DECLINED;
@@ -1284,7 +1284,7 @@ public class IntroductionIntegrationTest
}
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.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.data.DataModule;
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.TestSecureRandomModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import org.briarproject.briar.blog.BlogModule;
import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.forum.ForumModule;
@@ -38,6 +40,7 @@ import dagger.Component;
ClientModule.class,
ContactModule.class,
CryptoModule.class,
CryptoExecutorModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
@@ -52,7 +55,8 @@ import dagger.Component;
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
TransportModule.class,
VersioningModule.class
})
interface IntroductionIntegrationTestComponent
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.MAX_SIGNATURE_BYTES;
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.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap;
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.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.REQUEST;
import static org.briarproject.briar.test.BriarTestUtils.getRealAuthor;
@@ -57,8 +59,8 @@ public class MessageEncoderParserIntegrationTest extends BrambleTestCase {
private final MessageParser messageParser;
private final IntroductionValidator validator;
private final GroupId groupId = new GroupId(getRandomId());
private final Group group = new Group(groupId, CLIENT_ID, getRandomId());
private final Group group = getGroup(CLIENT_ID, MAJOR_VERSION);
private final GroupId groupId = group.getId();
private final long timestamp = 42L;
private final SessionId sessionId = new SessionId(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.sync.GroupId;
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.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import org.briarproject.briar.api.forum.ForumPost;
import org.briarproject.briar.api.forum.ForumPostFactory;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.forum.ForumModule;
import org.briarproject.briar.test.BriarTestCase;
import org.junit.Test;
@@ -85,6 +92,14 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
private static void injectEagerSingletons(
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 TransportModule.EagerSingletons());
component.inject(new VersioningModule.EagerSingletons());
}
}

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