mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 03:09:04 +01:00
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:
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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<>());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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).}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -11,6 +11,8 @@ interface SessionParser {
|
||||
|
||||
BdfDictionary getSessionQuery(SessionId s);
|
||||
|
||||
BdfDictionary getAllSessionsQuery();
|
||||
|
||||
Role getRole(BdfDictionary d) throws FormatException;
|
||||
|
||||
CreatorSession parseCreatorSession(GroupId contactGroupId, BdfDictionary d)
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package org.briarproject.briar.privategroup.invitation;
|
||||
|
||||
import org.briarproject.bramble.api.sync.Group.Visibility;
|
||||
|
||||
interface State {
|
||||
|
||||
int getValue();
|
||||
|
||||
Visibility getVisibility();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -11,6 +11,8 @@ interface SessionParser {
|
||||
|
||||
BdfDictionary getSessionQuery(SessionId s);
|
||||
|
||||
BdfDictionary getAllSessionsQuery();
|
||||
|
||||
Session parseSession(GroupId contactGroupId, BdfDictionary d)
|
||||
throws FormatException;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}});
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user