mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-18 05:39:53 +01:00
Merge branch '2181-implement-sync-client-for-mailbox-props' into 'master'
Implement sync client for mailbox properties Closes #2181 See merge request briar/briar!1591
This commit is contained in:
@@ -6,14 +6,14 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public abstract class UniqueId extends Bytes {
|
public class UniqueId extends Bytes {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of a unique identifier in bytes.
|
* The length of a unique identifier in bytes.
|
||||||
*/
|
*/
|
||||||
public static final int LENGTH = 32;
|
public static final int LENGTH = 32;
|
||||||
|
|
||||||
protected UniqueId(byte[] id) {
|
public UniqueId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
if (id.length != LENGTH) throw new IllegalArgumentException();
|
if (id.length != LENGTH) throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.data.BdfList;
|
|||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertiesUpdate;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
@@ -20,6 +21,8 @@ import java.security.GeneralSecurityException;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface ClientHelper {
|
public interface ClientHelper {
|
||||||
|
|
||||||
@@ -123,6 +126,18 @@ public interface ClientHelper {
|
|||||||
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
|
Map<TransportId, TransportProperties> parseAndValidateTransportPropertiesMap(
|
||||||
BdfDictionary properties) throws FormatException;
|
BdfDictionary properties) throws FormatException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse and validate the property dictionary of a Mailbox property update
|
||||||
|
* message.
|
||||||
|
*
|
||||||
|
* @return the properties for using the Mailbox, or null if there is no
|
||||||
|
* Mailbox available
|
||||||
|
* @throws FormatException if the properties are not valid
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
MailboxPropertiesUpdate parseAndValidateMailboxPropertiesUpdate(
|
||||||
|
BdfDictionary properties) throws FormatException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the contact ID from the group metadata of the given contact
|
* Retrieves the contact ID from the group metadata of the given contact
|
||||||
* group.
|
* group.
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.contact;
|
|||||||
import org.briarproject.bramble.api.UniqueId;
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,9 +16,4 @@ public class PendingContactId extends UniqueId {
|
|||||||
public PendingContactId(byte[] id) {
|
public PendingContactId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(@Nullable Object o) {
|
|
||||||
return o instanceof PendingContactId && super.equals(o);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.crypto;
|
package org.briarproject.bramble.api.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
@@ -10,6 +11,8 @@ import javax.annotation.Nullable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface CryptoComponent {
|
public interface CryptoComponent {
|
||||||
|
|
||||||
|
UniqueId generateUniqueId();
|
||||||
|
|
||||||
SecretKey generateSecretKey();
|
SecretKey generateSecretKey();
|
||||||
|
|
||||||
SecureRandom getSecureRandom();
|
SecureRandom getSecureRandom();
|
||||||
@@ -172,9 +175,11 @@ public interface CryptoComponent {
|
|||||||
String asciiArmour(byte[] b, int lineLength);
|
String asciiArmour(byte[] b, int lineLength);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode the onion/hidden service address given its public key. As
|
* Encode the Onion given its public key. Specified here:
|
||||||
* specified here: https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt?id=29245fd5#n2135
|
* https://gitweb.torproject.org/torspec.git/tree/rend-spec-v3.txt?id=29245fd5#n2135
|
||||||
|
*
|
||||||
|
* @return the encoded onion, base32 chars
|
||||||
*/
|
*/
|
||||||
String encodeOnionAddress(byte[] publicKey);
|
String encodeOnion(byte[] publicKey);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,4 @@ public class AuthorId extends UniqueId {
|
|||||||
public AuthorId(byte[] id) {
|
public AuthorId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
return o instanceof AuthorId && super.equals(o);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ public class MailboxProperties {
|
|||||||
return baseUrl;
|
return baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getOnion() {
|
||||||
|
return baseUrl.replaceFirst("^http://", "")
|
||||||
|
.replaceFirst("\\.onion$", "");
|
||||||
|
}
|
||||||
|
|
||||||
public MailboxAuthToken getAuthToken() {
|
public MailboxAuthToken getAuthToken() {
|
||||||
return authToken;
|
return authToken;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class MailboxPropertiesUpdate {
|
||||||
|
|
||||||
|
private final String onion;
|
||||||
|
private final MailboxAuthToken authToken;
|
||||||
|
private final MailboxFolderId inboxId;
|
||||||
|
private final MailboxFolderId outboxId;
|
||||||
|
|
||||||
|
public MailboxPropertiesUpdate(String onion,
|
||||||
|
MailboxAuthToken authToken, MailboxFolderId inboxId,
|
||||||
|
MailboxFolderId outboxId) {
|
||||||
|
this.onion = onion;
|
||||||
|
this.authToken = authToken;
|
||||||
|
this.inboxId = inboxId;
|
||||||
|
this.outboxId = outboxId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOnion() {
|
||||||
|
return onion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailboxAuthToken getAuthToken() {
|
||||||
|
return authToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailboxFolderId getInboxId() {
|
||||||
|
return inboxId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MailboxFolderId getOutboxId() {
|
||||||
|
return outboxId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
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.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.ClientId;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface MailboxPropertyManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The unique ID of the mailbox property client.
|
||||||
|
*/
|
||||||
|
ClientId CLIENT_ID =
|
||||||
|
new ClientId("org.briarproject.bramble.mailbox.properties");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current major version of the mailbox property client.
|
||||||
|
*/
|
||||||
|
int MAJOR_VERSION = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current minor version of the mailbox property client.
|
||||||
|
*/
|
||||||
|
int MINOR_VERSION = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of properties required for a (non-empty) update message.
|
||||||
|
*/
|
||||||
|
int PROP_COUNT = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The required properties of a non-empty update message.
|
||||||
|
*/
|
||||||
|
String PROP_KEY_ONION = "onion";
|
||||||
|
String PROP_KEY_AUTHTOKEN = "authToken";
|
||||||
|
String PROP_KEY_INBOXID = "inboxId";
|
||||||
|
String PROP_KEY_OUTBOXID = "outboxId";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the Onion property.
|
||||||
|
*/
|
||||||
|
int PROP_ONION_LENGTH = 56;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message metadata key for the version number of a local or remote update,
|
||||||
|
* as a BDF long.
|
||||||
|
*/
|
||||||
|
String MSG_KEY_VERSION = "version";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message metadata key for whether an update is local or remote, as a BDF
|
||||||
|
* boolean.
|
||||||
|
*/
|
||||||
|
String MSG_KEY_LOCAL = "local";
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
MailboxPropertiesUpdate getLocalProperties(Transaction txn, ContactId c)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
MailboxPropertiesUpdate getRemoteProperties(Transaction txn, ContactId c)
|
||||||
|
throws DbException;
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -10,6 +12,13 @@ import javax.annotation.Nullable;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface MailboxSettingsManager {
|
public interface MailboxSettingsManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a hook to be called when a mailbox has been paired or unpaired.
|
||||||
|
* This method should be called before
|
||||||
|
* {@link LifecycleManager#startServices(SecretKey)}.
|
||||||
|
*/
|
||||||
|
void registerMailboxHook(MailboxHook hook);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
MailboxProperties getOwnMailboxProperties(Transaction txn)
|
MailboxProperties getOwnMailboxProperties(Transaction txn)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
@@ -30,4 +39,22 @@ public interface MailboxSettingsManager {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
String getPendingUpload(Transaction txn, ContactId id) throws DbException;
|
String getPendingUpload(Transaction txn, ContactId id) throws DbException;
|
||||||
|
|
||||||
|
interface MailboxHook {
|
||||||
|
/**
|
||||||
|
* Called when Briar is paired with a mailbox
|
||||||
|
*
|
||||||
|
* @param txn A read-write transaction
|
||||||
|
* @param ownOnion Our new mailbox's onion (56 base32 chars)
|
||||||
|
*/
|
||||||
|
void mailboxPaired(Transaction txn, String ownOnion)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the mailbox is unpaired
|
||||||
|
*
|
||||||
|
* @param txn A read-write transaction
|
||||||
|
*/
|
||||||
|
void mailboxUnpaired(Transaction txn) throws DbException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,4 @@ public class GroupId extends UniqueId {
|
|||||||
public GroupId(byte[] id) {
|
public GroupId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
return o instanceof GroupId && super.equals(o);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,4 @@ public class MessageId extends UniqueId {
|
|||||||
public MessageId(byte[] id) {
|
public MessageId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
return o instanceof MessageId && super.equals(o);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.briarproject.bramble.api.identity.Author;
|
|||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.Identity;
|
import org.briarproject.bramble.api.identity.Identity;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertiesUpdate;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
import org.briarproject.bramble.api.sync.ClientId;
|
import org.briarproject.bramble.api.sync.ClientId;
|
||||||
@@ -39,6 +40,8 @@ import java.util.Map;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_SIGNATURE_PUBLIC_KEY_BYTES;
|
||||||
@@ -271,4 +274,17 @@ public class TestUtils {
|
|||||||
return optionalTests != null &&
|
return optionalTests != null &&
|
||||||
asList(optionalTests.split(",")).contains(testClass.getName());
|
asList(optionalTests.split(",")).contains(testClass.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean mailboxPropertiesUpdateEqual(
|
||||||
|
@Nullable MailboxPropertiesUpdate a,
|
||||||
|
@Nullable MailboxPropertiesUpdate b) {
|
||||||
|
if (a == null || b == null) {
|
||||||
|
return a == b;
|
||||||
|
}
|
||||||
|
return a.getOnion().equals(b.getOnion()) &&
|
||||||
|
a.getAuthToken().equals(b.getAuthToken()) &&
|
||||||
|
a.getInboxId().equals(b.getInboxId()) &&
|
||||||
|
a.getOutboxId().equals(b.getOutboxId());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.client;
|
package org.briarproject.bramble.client;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
@@ -22,6 +23,9 @@ import org.briarproject.bramble.api.db.Metadata;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertiesUpdate;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||||
@@ -29,6 +33,7 @@ import org.briarproject.bramble.api.sync.GroupId;
|
|||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.bramble.util.Base32;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -39,6 +44,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -46,6 +52,12 @@ import static org.briarproject.bramble.api.client.ContactGroupConstants.GROUP_KE
|
|||||||
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_COUNT;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_AUTHTOKEN;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_INBOXID;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_ONION;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_OUTBOXID;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_ONION_LENGTH;
|
||||||
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
|
||||||
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
||||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||||
@@ -399,6 +411,35 @@ class ClientHelperImpl implements ClientHelper {
|
|||||||
return tpMap;
|
return tpMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public MailboxPropertiesUpdate parseAndValidateMailboxPropertiesUpdate(
|
||||||
|
BdfDictionary properties) throws FormatException {
|
||||||
|
if (properties.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Accepting more props than we need, for forward compatibility
|
||||||
|
if (properties.size() < PROP_COUNT) {
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
String onion = properties.getString(PROP_KEY_ONION);
|
||||||
|
checkLength(onion, PROP_ONION_LENGTH);
|
||||||
|
try {
|
||||||
|
Base32.decode(onion, true);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new FormatException();
|
||||||
|
}
|
||||||
|
byte[] authToken = properties.getRaw(PROP_KEY_AUTHTOKEN);
|
||||||
|
checkLength(authToken, UniqueId.LENGTH);
|
||||||
|
byte[] inboxId = properties.getRaw(PROP_KEY_INBOXID);
|
||||||
|
checkLength(inboxId, UniqueId.LENGTH);
|
||||||
|
byte[] outboxId = properties.getRaw(PROP_KEY_OUTBOXID);
|
||||||
|
checkLength(outboxId, UniqueId.LENGTH);
|
||||||
|
return new MailboxPropertiesUpdate(onion,
|
||||||
|
new MailboxAuthToken(authToken), new MailboxFolderId(inboxId),
|
||||||
|
new MailboxFolderId(outboxId));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
public ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.bouncycastle.crypto.CryptoException;
|
|||||||
import org.bouncycastle.crypto.Digest;
|
import org.bouncycastle.crypto.Digest;
|
||||||
import org.bouncycastle.crypto.digests.Blake2bDigest;
|
import org.bouncycastle.crypto.digests.Blake2bDigest;
|
||||||
import org.bouncycastle.crypto.digests.SHA3Digest;
|
import org.bouncycastle.crypto.digests.SHA3Digest;
|
||||||
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
@@ -41,6 +42,7 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import static java.lang.System.arraycopy;
|
import static java.lang.System.arraycopy;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_AGREEMENT;
|
||||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
import static org.briarproject.bramble.api.crypto.CryptoConstants.KEY_TYPE_SIGNATURE;
|
||||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
||||||
@@ -54,7 +56,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
|||||||
class CryptoComponentImpl implements CryptoComponent {
|
class CryptoComponentImpl implements CryptoComponent {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(CryptoComponentImpl.class.getName());
|
getLogger(CryptoComponentImpl.class.getName());
|
||||||
|
|
||||||
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
private static final int SIGNATURE_KEY_PAIR_BITS = 256;
|
||||||
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
private static final int STORAGE_IV_BYTES = 24; // 196 bits
|
||||||
@@ -128,6 +130,13 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UniqueId generateUniqueId() {
|
||||||
|
byte[] b = new byte[UniqueId.LENGTH];
|
||||||
|
secureRandom.nextBytes(b);
|
||||||
|
return new UniqueId(b);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SecretKey generateSecretKey() {
|
public SecretKey generateSecretKey() {
|
||||||
byte[] b = new byte[SecretKey.LENGTH];
|
byte[] b = new byte[SecretKey.LENGTH];
|
||||||
@@ -449,7 +458,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encodeOnionAddress(byte[] publicKey) {
|
public String encodeOnion(byte[] publicKey) {
|
||||||
Digest digest = new SHA3Digest(256);
|
Digest digest = new SHA3Digest(256);
|
||||||
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
|
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
|
||||||
digest.update(label, 0, label.length);
|
digest.update(label, 0, label.length);
|
||||||
|
|||||||
@@ -1,16 +1,36 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
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.mailbox.MailboxManager;
|
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertyManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||||
|
import org.briarproject.bramble.api.sync.validation.ValidationManager;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.CLIENT_ID;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.MAJOR_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.MINOR_VERSION;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class MailboxModule {
|
public class MailboxModule {
|
||||||
|
|
||||||
|
public static class EagerSingletons {
|
||||||
|
@Inject
|
||||||
|
MailboxPropertyValidator mailboxPropertyValidator;
|
||||||
|
@Inject
|
||||||
|
MailboxPropertyManager mailboxPropertyManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
MailboxManager providesMailboxManager(MailboxManagerImpl mailboxManager) {
|
MailboxManager providesMailboxManager(MailboxManagerImpl mailboxManager) {
|
||||||
@@ -33,4 +53,34 @@ public class MailboxModule {
|
|||||||
MailboxApi providesMailboxApi(MailboxApiImpl mailboxApi) {
|
MailboxApi providesMailboxApi(MailboxApiImpl mailboxApi) {
|
||||||
return mailboxApi;
|
return mailboxApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
MailboxPropertyValidator provideMailboxPropertyValidator(
|
||||||
|
ValidationManager validationManager, ClientHelper clientHelper,
|
||||||
|
MetadataEncoder metadataEncoder, Clock clock) {
|
||||||
|
MailboxPropertyValidator validator = new MailboxPropertyValidator(
|
||||||
|
clientHelper, metadataEncoder, clock);
|
||||||
|
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
|
||||||
|
validator);
|
||||||
|
return validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
MailboxPropertyManager provideMailboxPropertyManager(
|
||||||
|
LifecycleManager lifecycleManager,
|
||||||
|
ValidationManager validationManager, ContactManager contactManager,
|
||||||
|
ClientVersioningManager clientVersioningManager,
|
||||||
|
MailboxSettingsManager mailboxSettingsManager,
|
||||||
|
MailboxPropertyManagerImpl mailboxPropertyManager) {
|
||||||
|
lifecycleManager.registerOpenDatabaseHook(mailboxPropertyManager);
|
||||||
|
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
|
||||||
|
mailboxPropertyManager);
|
||||||
|
contactManager.registerContactHook(mailboxPropertyManager);
|
||||||
|
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
|
||||||
|
MINOR_VERSION, mailboxPropertyManager);
|
||||||
|
mailboxSettingsManager.registerMailboxHook(mailboxPropertyManager);
|
||||||
|
return mailboxPropertyManager;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,8 +162,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
|||||||
}
|
}
|
||||||
LOG.info("QR code is valid");
|
LOG.info("QR code is valid");
|
||||||
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
||||||
String onionAddress = crypto.encodeOnionAddress(onionPubKey);
|
String onion = crypto.encodeOnion(onionPubKey);
|
||||||
String baseUrl = "http://" + onionAddress + ".onion";
|
String baseUrl = "http://" + onion + ".onion";
|
||||||
byte[] tokenBytes = Arrays.copyOfRange(bytes, 33, 65);
|
byte[] tokenBytes = Arrays.copyOfRange(bytes, 33, 65);
|
||||||
MailboxAuthToken setupToken = new MailboxAuthToken(tokenBytes);
|
MailboxAuthToken setupToken = new MailboxAuthToken(tokenBytes);
|
||||||
return new MailboxProperties(baseUrl, setupToken, true);
|
return new MailboxProperties(baseUrl, setupToken, true);
|
||||||
|
|||||||
@@ -0,0 +1,296 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
|
import org.briarproject.bramble.api.data.MetadataParser;
|
||||||
|
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.LifecycleManager.OpenDatabaseHook;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertiesUpdate;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertyManager;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager.MailboxHook;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
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.validation.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.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
class MailboxPropertyManagerImpl implements MailboxPropertyManager,
|
||||||
|
OpenDatabaseHook, ContactHook, ClientVersioningHook,
|
||||||
|
IncomingMessageHook, MailboxHook {
|
||||||
|
|
||||||
|
private final DatabaseComponent db;
|
||||||
|
private final ClientHelper clientHelper;
|
||||||
|
private final ClientVersioningManager clientVersioningManager;
|
||||||
|
private final MetadataParser metadataParser;
|
||||||
|
private final ContactGroupFactory contactGroupFactory;
|
||||||
|
private final Clock clock;
|
||||||
|
private final MailboxSettingsManager mailboxSettingsManager;
|
||||||
|
private final ContactManager contactManager;
|
||||||
|
private final CryptoComponent crypto;
|
||||||
|
private final Group localGroup;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MailboxPropertyManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||||
|
ClientVersioningManager clientVersioningManager,
|
||||||
|
MetadataParser metadataParser,
|
||||||
|
ContactGroupFactory contactGroupFactory, Clock clock,
|
||||||
|
MailboxSettingsManager mailboxSettingsManager,
|
||||||
|
ContactManager contactManager,
|
||||||
|
CryptoComponent crypto) {
|
||||||
|
this.db = db;
|
||||||
|
this.clientHelper = clientHelper;
|
||||||
|
this.clientVersioningManager = clientVersioningManager;
|
||||||
|
this.metadataParser = metadataParser;
|
||||||
|
this.contactGroupFactory = contactGroupFactory;
|
||||||
|
this.clock = clock;
|
||||||
|
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||||
|
this.contactManager = contactManager;
|
||||||
|
this.crypto = crypto;
|
||||||
|
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDatabaseOpened(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 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);
|
||||||
|
// If we are paired, create and send props to the newly added contact
|
||||||
|
MailboxProperties ownProps =
|
||||||
|
mailboxSettingsManager.getOwnMailboxProperties(txn);
|
||||||
|
if (ownProps != null) {
|
||||||
|
createAndSendProperties(txn, c, ownProps.getOnion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removingContact(Transaction txn, Contact c) throws DbException {
|
||||||
|
db.removeGroup(txn, getContactGroup(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mailboxPaired(Transaction txn, String ownOnion)
|
||||||
|
throws DbException {
|
||||||
|
for (Contact c : contactManager.getContacts()) {
|
||||||
|
createAndSendProperties(txn, c, ownOnion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mailboxUnpaired(Transaction txn) throws DbException {
|
||||||
|
for (Contact c : contactManager.getContacts()) {
|
||||||
|
sendEmptyProperties(txn, 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 DeliveryAction incomingMessage(Transaction txn, Message m,
|
||||||
|
Metadata meta) throws DbException, InvalidMessageException {
|
||||||
|
try {
|
||||||
|
BdfDictionary d = metadataParser.parse(meta);
|
||||||
|
// Get latest non-local update in the same group (from same contact)
|
||||||
|
LatestUpdate latest = findLatest(txn, m.getGroupId(), false);
|
||||||
|
if (latest != null) {
|
||||||
|
if (d.getLong(MSG_KEY_VERSION) > latest.version) {
|
||||||
|
db.deleteMessage(txn, latest.messageId);
|
||||||
|
db.deleteMessageMetadata(txn, latest.messageId);
|
||||||
|
} else {
|
||||||
|
// Delete this update, we already have a newer one
|
||||||
|
db.deleteMessage(txn, m.getId());
|
||||||
|
db.deleteMessageMetadata(txn, m.getId());
|
||||||
|
return ACCEPT_DO_NOT_SHARE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO should probably broadcast an event that a contact's mailbox
|
||||||
|
// properties were updated
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new InvalidMessageException(e);
|
||||||
|
}
|
||||||
|
return ACCEPT_DO_NOT_SHARE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public MailboxPropertiesUpdate getLocalProperties(Transaction txn,
|
||||||
|
ContactId c) throws DbException {
|
||||||
|
return getProperties(txn, db.getContact(txn, c), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public MailboxPropertiesUpdate getRemoteProperties(Transaction txn,
|
||||||
|
ContactId c) throws DbException {
|
||||||
|
return getProperties(txn, db.getContact(txn, c), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and sends an update message to the given contact. The message
|
||||||
|
* holds our own mailbox's onion, and generated unique properties. All of
|
||||||
|
* which the contact needs to communicate with our Mailbox.
|
||||||
|
*/
|
||||||
|
private void createAndSendProperties(Transaction txn,
|
||||||
|
Contact c, String ownOnion) throws DbException {
|
||||||
|
MailboxPropertiesUpdate p = new MailboxPropertiesUpdate(ownOnion,
|
||||||
|
new MailboxAuthToken(crypto.generateUniqueId().getBytes()),
|
||||||
|
new MailboxFolderId(crypto.generateUniqueId().getBytes()),
|
||||||
|
new MailboxFolderId(crypto.generateUniqueId().getBytes()));
|
||||||
|
Group g = getContactGroup(c);
|
||||||
|
storeMessageReplaceLatest(txn, g.getId(), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends an empty update message to the given contact. The empty update
|
||||||
|
* indicates for the receiving contact that we no longer have a Mailbox that
|
||||||
|
* they can use.
|
||||||
|
*/
|
||||||
|
private void sendEmptyProperties(Transaction txn, Contact c)
|
||||||
|
throws DbException {
|
||||||
|
Group g = getContactGroup(c);
|
||||||
|
storeMessageReplaceLatest(txn, g.getId(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private MailboxPropertiesUpdate getProperties(Transaction txn,
|
||||||
|
Contact c, boolean local) throws DbException {
|
||||||
|
MailboxPropertiesUpdate p = null;
|
||||||
|
Group g = getContactGroup(c);
|
||||||
|
try {
|
||||||
|
LatestUpdate latest = findLatest(txn, g.getId(), local);
|
||||||
|
if (latest != null) {
|
||||||
|
BdfList body =
|
||||||
|
clientHelper.getMessageAsList(txn, latest.messageId);
|
||||||
|
p = parseProperties(body);
|
||||||
|
}
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeMessageReplaceLatest(Transaction txn, GroupId g,
|
||||||
|
@Nullable MailboxPropertiesUpdate p) throws DbException {
|
||||||
|
try {
|
||||||
|
LatestUpdate latest = findLatest(txn, g, true);
|
||||||
|
long version = latest == null ? 1 : latest.version + 1;
|
||||||
|
Message m = clientHelper.createMessage(g, clock.currentTimeMillis(),
|
||||||
|
encodeProperties(version, p));
|
||||||
|
BdfDictionary meta = new BdfDictionary();
|
||||||
|
meta.put(MSG_KEY_VERSION, version);
|
||||||
|
meta.put(MSG_KEY_LOCAL, true);
|
||||||
|
clientHelper.addLocalMessage(txn, m, meta, true, false);
|
||||||
|
if (latest != null) {
|
||||||
|
db.removeMessage(txn, latest.messageId);
|
||||||
|
}
|
||||||
|
} catch (FormatException e) {
|
||||||
|
throw new DbException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private LatestUpdate findLatest(Transaction txn, GroupId g, boolean local)
|
||||||
|
throws DbException, FormatException {
|
||||||
|
Map<MessageId, BdfDictionary> metadata =
|
||||||
|
clientHelper.getMessageMetadataAsDictionary(txn, g);
|
||||||
|
// We should have at most 1 local and 1 remote
|
||||||
|
if (metadata.size() > 2) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
|
||||||
|
BdfDictionary meta = e.getValue();
|
||||||
|
if (meta.getBoolean(MSG_KEY_LOCAL) == local) {
|
||||||
|
return new LatestUpdate(e.getKey(),
|
||||||
|
meta.getLong(MSG_KEY_VERSION));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfList encodeProperties(long version,
|
||||||
|
@Nullable MailboxPropertiesUpdate p) {
|
||||||
|
BdfDictionary dict = new BdfDictionary();
|
||||||
|
if (p != null) {
|
||||||
|
dict.put(PROP_KEY_ONION, p.getOnion());
|
||||||
|
dict.put(PROP_KEY_AUTHTOKEN, p.getAuthToken().getBytes());
|
||||||
|
dict.put(PROP_KEY_INBOXID, p.getInboxId().getBytes());
|
||||||
|
dict.put(PROP_KEY_OUTBOXID, p.getOutboxId().getBytes());
|
||||||
|
}
|
||||||
|
return BdfList.of(version, dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private MailboxPropertiesUpdate parseProperties(BdfList body)
|
||||||
|
throws FormatException {
|
||||||
|
BdfDictionary dict = body.getDictionary(1);
|
||||||
|
return clientHelper.parseAndValidateMailboxPropertiesUpdate(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Group getContactGroup(Contact c) {
|
||||||
|
return contactGroupFactory.createContactGroup(CLIENT_ID, MAJOR_VERSION,
|
||||||
|
c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LatestUpdate {
|
||||||
|
|
||||||
|
private final MessageId messageId;
|
||||||
|
private final long version;
|
||||||
|
|
||||||
|
private LatestUpdate(MessageId messageId, long version) {
|
||||||
|
this.messageId = messageId;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
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.InvalidMessageException;
|
||||||
|
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.mailbox.MailboxPropertyManager.MSG_KEY_LOCAL;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.MSG_KEY_VERSION;
|
||||||
|
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class MailboxPropertyValidator extends BdfMessageValidator {
|
||||||
|
|
||||||
|
MailboxPropertyValidator(ClientHelper clientHelper,
|
||||||
|
MetadataEncoder metadataEncoder, Clock clock) {
|
||||||
|
super(clientHelper, metadataEncoder, clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||||
|
BdfList body) throws InvalidMessageException, FormatException {
|
||||||
|
// Version, properties
|
||||||
|
checkSize(body, 2);
|
||||||
|
// Version
|
||||||
|
long version = body.getLong(0);
|
||||||
|
if (version < 0) throw new FormatException();
|
||||||
|
// Properties
|
||||||
|
BdfDictionary dictionary = body.getDictionary(1);
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(dictionary);
|
||||||
|
// Return the metadata
|
||||||
|
BdfDictionary meta = new BdfDictionary();
|
||||||
|
meta.put(MSG_KEY_VERSION, version);
|
||||||
|
meta.put(MSG_KEY_LOCAL, false);
|
||||||
|
return new BdfMessageContext(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,6 +12,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -32,12 +35,18 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
|||||||
static final String SETTINGS_UPLOADS_NAMESPACE = "mailbox-uploads";
|
static final String SETTINGS_UPLOADS_NAMESPACE = "mailbox-uploads";
|
||||||
|
|
||||||
private final SettingsManager settingsManager;
|
private final SettingsManager settingsManager;
|
||||||
|
private final List<MailboxHook> hooks = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MailboxSettingsManagerImpl(SettingsManager settingsManager) {
|
MailboxSettingsManagerImpl(SettingsManager settingsManager) {
|
||||||
this.settingsManager = settingsManager;
|
this.settingsManager = settingsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerMailboxHook(MailboxHook hook) {
|
||||||
|
hooks.add(hook);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MailboxProperties getOwnMailboxProperties(Transaction txn)
|
public MailboxProperties getOwnMailboxProperties(Transaction txn)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -60,6 +69,9 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
|||||||
s.put(SETTINGS_KEY_ONION, p.getBaseUrl());
|
s.put(SETTINGS_KEY_ONION, p.getBaseUrl());
|
||||||
s.put(SETTINGS_KEY_TOKEN, p.getAuthToken().toString());
|
s.put(SETTINGS_KEY_TOKEN, p.getAuthToken().toString());
|
||||||
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
|
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
|
||||||
|
for (MailboxHook hook : hooks) {
|
||||||
|
hook.mailboxPaired(txn, p.getOnion());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -682,8 +682,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
byte[] localSeed = alice ? aliceSeed : bobSeed;
|
byte[] localSeed = alice ? aliceSeed : bobSeed;
|
||||||
byte[] remoteSeed = alice ? bobSeed : aliceSeed;
|
byte[] remoteSeed = alice ? bobSeed : aliceSeed;
|
||||||
String blob = torRendezvousCrypto.getPrivateKeyBlob(localSeed);
|
String blob = torRendezvousCrypto.getPrivateKeyBlob(localSeed);
|
||||||
String localOnion = torRendezvousCrypto.getOnionAddress(localSeed);
|
String localOnion = torRendezvousCrypto.getOnion(localSeed);
|
||||||
String remoteOnion = torRendezvousCrypto.getOnionAddress(remoteSeed);
|
String remoteOnion = torRendezvousCrypto.getOnion(remoteSeed);
|
||||||
TransportProperties remoteProperties = new TransportProperties();
|
TransportProperties remoteProperties = new TransportProperties();
|
||||||
remoteProperties.put(PROP_ONION_V3, remoteOnion);
|
remoteProperties.put(PROP_ONION_V3, remoteOnion);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ interface TorRendezvousCrypto {
|
|||||||
|
|
||||||
static final int SEED_BYTES = 32;
|
static final int SEED_BYTES = 32;
|
||||||
|
|
||||||
String getOnionAddress(byte[] seed);
|
String getOnion(byte[] seed);
|
||||||
|
|
||||||
String getPrivateKeyBlob(byte[] seed);
|
String getPrivateKeyBlob(byte[] seed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getOnionAddress(byte[] seed) {
|
public String getOnion(byte[] seed) {
|
||||||
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
|
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
|
||||||
return crypto.encodeOnionAddress(spec.getA().toByteArray());
|
return crypto.encodeOnion(spec.getA().toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.client;
|
package org.briarproject.bramble.client;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.UniqueId;
|
||||||
import org.briarproject.bramble.api.client.ClientHelper;
|
import org.briarproject.bramble.api.client.ClientHelper;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyParser;
|
import org.briarproject.bramble.api.crypto.KeyParser;
|
||||||
@@ -20,13 +21,15 @@ import org.briarproject.bramble.api.db.Metadata;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertiesUpdate;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.Message;
|
import org.briarproject.bramble.api.sync.Message;
|
||||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -41,15 +44,22 @@ import java.util.Random;
|
|||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_AUTHTOKEN;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_INBOXID;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_ONION;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_OUTBOXID;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSignaturePrivateKey;
|
import static org.briarproject.bramble.test.TestUtils.getSignaturePrivateKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSignaturePublicKey;
|
import static org.briarproject.bramble.test.TestUtils.getSignaturePublicKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.mailboxPropertiesUpdateEqual;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class ClientHelperImplTest extends BrambleMockTestCase {
|
public class ClientHelperImplTest extends BrambleMockTestCase {
|
||||||
@@ -78,13 +88,35 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
|
|||||||
private final long timestamp = message.getTimestamp();
|
private final long timestamp = message.getTimestamp();
|
||||||
private final Metadata metadata = new Metadata();
|
private final Metadata metadata = new Metadata();
|
||||||
private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42));
|
private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42));
|
||||||
private final String label = StringUtils.getRandomString(5);
|
private final String label = getRandomString(5);
|
||||||
private final Author author = getAuthor();
|
private final Author author = getAuthor();
|
||||||
|
|
||||||
private final ClientHelper clientHelper = new ClientHelperImpl(db,
|
private final ClientHelper clientHelper = new ClientHelperImpl(db,
|
||||||
messageFactory, bdfReaderFactory, bdfWriterFactory, metadataParser,
|
messageFactory, bdfReaderFactory, bdfWriterFactory, metadataParser,
|
||||||
metadataEncoder, cryptoComponent, authorFactory);
|
metadataEncoder, cryptoComponent, authorFactory);
|
||||||
|
|
||||||
|
private final MailboxPropertiesUpdate validMailboxPropsUpdate;
|
||||||
|
|
||||||
|
public ClientHelperImplTest() {
|
||||||
|
validMailboxPropsUpdate = new MailboxPropertiesUpdate(
|
||||||
|
"pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd",
|
||||||
|
new MailboxAuthToken(getRandomId()),
|
||||||
|
new MailboxFolderId(getRandomId()),
|
||||||
|
new MailboxFolderId(getRandomId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BdfDictionary getValidMailboxPropsUpdateDict() {
|
||||||
|
BdfDictionary dict = new BdfDictionary();
|
||||||
|
dict.put(PROP_KEY_ONION, validMailboxPropsUpdate.getOnion());
|
||||||
|
dict.put(PROP_KEY_AUTHTOKEN, validMailboxPropsUpdate.getAuthToken()
|
||||||
|
.getBytes());
|
||||||
|
dict.put(PROP_KEY_INBOXID, validMailboxPropsUpdate.getInboxId()
|
||||||
|
.getBytes());
|
||||||
|
dict.put(PROP_KEY_OUTBOXID, validMailboxPropsUpdate.getOutboxId()
|
||||||
|
.getBytes());
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddLocalMessage() throws Exception {
|
public void testAddLocalMessage() throws Exception {
|
||||||
boolean shared = new Random().nextBoolean();
|
boolean shared = new Random().nextBoolean();
|
||||||
@@ -513,4 +545,95 @@ public class ClientHelperImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(eof));
|
will(returnValue(eof));
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseEmptyMailboxPropsUpdate() throws Exception {
|
||||||
|
BdfDictionary emptyPropsDict = new BdfDictionary();
|
||||||
|
MailboxPropertiesUpdate parsedProps = clientHelper
|
||||||
|
.parseAndValidateMailboxPropertiesUpdate(emptyPropsDict);
|
||||||
|
assertNull(parsedProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseValidMailboxPropsUpdate() throws Exception {
|
||||||
|
MailboxPropertiesUpdate parsedProps = clientHelper
|
||||||
|
.parseAndValidateMailboxPropertiesUpdate(
|
||||||
|
getValidMailboxPropsUpdateDict());
|
||||||
|
assertTrue(mailboxPropertiesUpdateEqual(validMailboxPropsUpdate,
|
||||||
|
parsedProps));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsMailboxPropsUpdateOnionNotDecodable()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary propsDict = getValidMailboxPropsUpdateDict();
|
||||||
|
String badOnion = "!" + propsDict.getString(PROP_KEY_ONION)
|
||||||
|
.substring(1);
|
||||||
|
propsDict.put(PROP_KEY_ONION, badOnion);
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(propsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsMailboxPropsUpdateOnionWrongLength()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary propsDict = getValidMailboxPropsUpdateDict();
|
||||||
|
String tooLongOnion = propsDict.getString(PROP_KEY_ONION) + "!";
|
||||||
|
propsDict.put(PROP_KEY_ONION, tooLongOnion);
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(propsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsMailboxPropsUpdateInboxIdWrongLength()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary propsDict = getValidMailboxPropsUpdateDict();
|
||||||
|
propsDict.put(PROP_KEY_INBOXID, getRandomBytes(UniqueId.LENGTH + 1));
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(propsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsMailboxPropsUpdateOutboxIdWrongLength()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary propsDict = getValidMailboxPropsUpdateDict();
|
||||||
|
propsDict.put(PROP_KEY_OUTBOXID, getRandomBytes(UniqueId.LENGTH + 1));
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(propsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsMailboxPropsUpdateAuthTokenWrongLength()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary propsDict = getValidMailboxPropsUpdateDict();
|
||||||
|
propsDict.put(PROP_KEY_AUTHTOKEN, getRandomBytes(UniqueId.LENGTH + 1));
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(propsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsMailboxPropsUpdateMissingOnion() throws Exception {
|
||||||
|
BdfDictionary propsDict = getValidMailboxPropsUpdateDict();
|
||||||
|
propsDict.remove(PROP_KEY_ONION);
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(propsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsMailboxPropsUpdateMissingAuthToken()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary propsDict = getValidMailboxPropsUpdateDict();
|
||||||
|
propsDict.remove(PROP_KEY_AUTHTOKEN);
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(propsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsMailboxPropsUpdateMissingInboxId() throws Exception {
|
||||||
|
BdfDictionary propsDict = getValidMailboxPropsUpdateDict();
|
||||||
|
propsDict.remove(PROP_KEY_INBOXID);
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(propsDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testRejectsMailboxPropsUpdateMissingOutboxId()
|
||||||
|
throws Exception {
|
||||||
|
BdfDictionary propsDict = getValidMailboxPropsUpdateDict();
|
||||||
|
propsDict.remove(PROP_KEY_OUTBOXID);
|
||||||
|
clientHelper.parseAndValidateMailboxPropertiesUpdate(propsDict);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testSuccessfulPairing() throws Exception {
|
public void testSuccessfulPairing() throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).encodeOnionAddress(onionBytes);
|
oneOf(crypto).encodeOnion(onionBytes);
|
||||||
will(returnValue(onion));
|
will(returnValue(onion));
|
||||||
oneOf(api).setup(with(matches(setupProperties)));
|
oneOf(api).setup(with(matches(setupProperties)));
|
||||||
will(returnValue(ownerToken));
|
will(returnValue(ownerToken));
|
||||||
@@ -141,7 +141,7 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
private void testApiException(Exception e,
|
private void testApiException(Exception e,
|
||||||
Class<? extends MailboxPairingState> s) throws Exception {
|
Class<? extends MailboxPairingState> s) throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).encodeOnionAddress(onionBytes);
|
oneOf(crypto).encodeOnion(onionBytes);
|
||||||
will(returnValue(onion));
|
will(returnValue(onion));
|
||||||
oneOf(api).setup(with(matches(setupProperties)));
|
oneOf(api).setup(with(matches(setupProperties)));
|
||||||
will(throwException(e));
|
will(throwException(e));
|
||||||
@@ -155,7 +155,7 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testDbException() throws Exception {
|
public void testDbException() throws Exception {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).encodeOnionAddress(onionBytes);
|
oneOf(crypto).encodeOnion(onionBytes);
|
||||||
will(returnValue(onion));
|
will(returnValue(onion));
|
||||||
oneOf(api).setup(with(matches(setupProperties)));
|
oneOf(api).setup(with(matches(setupProperties)));
|
||||||
will(returnValue(ownerToken));
|
will(returnValue(ownerToken));
|
||||||
|
|||||||
@@ -0,0 +1,671 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
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.ContactManager;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
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.MetadataParser;
|
||||||
|
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.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertiesUpdate;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||||
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.CLIENT_ID;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.MAJOR_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.MSG_KEY_LOCAL;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.MSG_KEY_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_AUTHTOKEN;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_INBOXID;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_ONION;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxPropertyManager.PROP_KEY_OUTBOXID;
|
||||||
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
|
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.mailboxPropertiesUpdateEqual;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class MailboxPropertyManagerImplTest 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 CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||||
|
private final MailboxSettingsManager mailboxSettingsManager =
|
||||||
|
context.mock(MailboxSettingsManager.class);
|
||||||
|
private final ContactManager contactManager =
|
||||||
|
context.mock(ContactManager.class);
|
||||||
|
|
||||||
|
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
private final BdfDictionary propsDict;
|
||||||
|
private final BdfDictionary emptyPropsDict = new BdfDictionary();
|
||||||
|
private final MailboxPropertiesUpdate props;
|
||||||
|
private final MailboxProperties ownProps;
|
||||||
|
|
||||||
|
public MailboxPropertyManagerImplTest() {
|
||||||
|
ownProps = new MailboxProperties("http://bar.onion",
|
||||||
|
new MailboxAuthToken(getRandomId()), true);
|
||||||
|
props = new MailboxPropertiesUpdate(ownProps.getOnion(),
|
||||||
|
new MailboxAuthToken(getRandomId()),
|
||||||
|
new MailboxFolderId(getRandomId()),
|
||||||
|
new MailboxFolderId(getRandomId()));
|
||||||
|
propsDict = new BdfDictionary();
|
||||||
|
propsDict.put(PROP_KEY_ONION, props.getOnion());
|
||||||
|
propsDict.put(PROP_KEY_AUTHTOKEN, props.getAuthToken().getBytes());
|
||||||
|
propsDict.put(PROP_KEY_INBOXID, props.getInboxId().getBytes());
|
||||||
|
propsDict.put(PROP_KEY_OUTBOXID, props.getOutboxId().getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private MailboxPropertyManagerImpl createInstance() {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION);
|
||||||
|
will(returnValue(localGroup));
|
||||||
|
}});
|
||||||
|
return new MailboxPropertyManagerImpl(db, clientHelper,
|
||||||
|
clientVersioningManager, metadataParser, contactGroupFactory,
|
||||||
|
clock, mailboxSettingsManager, contactManager, crypto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatesGroupsAtUnpairedStartup() throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
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(singletonList(contact)));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
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);
|
||||||
|
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
t.onDatabaseOpened(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatesGroupsAndCreatesAndSendsAtPairedStartup()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
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)));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
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);
|
||||||
|
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||||
|
will(returnValue(ownProps));
|
||||||
|
oneOf(crypto).generateUniqueId();
|
||||||
|
will(returnValue(props.getAuthToken()));
|
||||||
|
oneOf(crypto).generateUniqueId();
|
||||||
|
will(returnValue(props.getInboxId()));
|
||||||
|
oneOf(crypto).generateUniqueId();
|
||||||
|
will(returnValue(props.getOutboxId()));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
expectStoreMessage(txn, contactGroup.getId(), propsDict, 1, true);
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
t.onDatabaseOpened(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoesNotCreateGroupsAtStartupIfAlreadyCreated()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).containsGroup(txn, localGroup.getId());
|
||||||
|
will(returnValue(true));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
t.onDatabaseOpened(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatesContactGroupWhenAddingContactUnpaired()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
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,
|
||||||
|
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);
|
||||||
|
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
t.addingContact(txn, contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatesContactGroupAndCreatesAndSendsWhenAddingContactPaired()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
// Create the group and share it with the contact
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
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);
|
||||||
|
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||||
|
will(returnValue(ownProps));
|
||||||
|
oneOf(crypto).generateUniqueId();
|
||||||
|
will(returnValue(props.getAuthToken()));
|
||||||
|
oneOf(crypto).generateUniqueId();
|
||||||
|
will(returnValue(props.getInboxId()));
|
||||||
|
oneOf(crypto).generateUniqueId();
|
||||||
|
will(returnValue(props.getOutboxId()));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
expectStoreMessage(txn, contactGroup.getId(), propsDict, 1, true);
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
t.addingContact(txn, contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemovesGroupWhenRemovingContact() throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(db).removeGroup(txn, contactGroup);
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
t.removingContact(txn, contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoesNotDeleteAnythingWhenFirstUpdateIsDelivered()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
GroupId contactGroupId = new GroupId(getRandomId());
|
||||||
|
Message message = getMessage(contactGroupId);
|
||||||
|
Metadata meta = new Metadata();
|
||||||
|
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||||
|
);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
// A local update should be ignored
|
||||||
|
MessageId localUpdateId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(localUpdateId, BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||||
|
));
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(metadataParser).parse(meta);
|
||||||
|
will(returnValue(metaDictionary));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroupId);
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
assertEquals(ACCEPT_DO_NOT_SHARE,
|
||||||
|
t.incomingMessage(txn, message, meta));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletesOlderUpdateWhenUpdateIsDelivered()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
GroupId contactGroupId = new GroupId(getRandomId());
|
||||||
|
Message message = getMessage(contactGroupId);
|
||||||
|
Metadata meta = new Metadata();
|
||||||
|
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 2),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||||
|
);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
// This older version should be deleted
|
||||||
|
MessageId updateId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(updateId, BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||||
|
));
|
||||||
|
// A local update should be ignored
|
||||||
|
MessageId localUpdateId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(localUpdateId, BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||||
|
));
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(metadataParser).parse(meta);
|
||||||
|
will(returnValue(metaDictionary));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroupId);
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
oneOf(db).deleteMessage(txn, updateId);
|
||||||
|
oneOf(db).deleteMessageMetadata(txn, updateId);
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
assertEquals(ACCEPT_DO_NOT_SHARE,
|
||||||
|
t.incomingMessage(txn, message, meta));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletesObsoleteUpdateWhenDelivered() throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
GroupId contactGroupId = new GroupId(getRandomId());
|
||||||
|
Message message = getMessage(contactGroupId);
|
||||||
|
Metadata meta = new Metadata();
|
||||||
|
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 3),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||||
|
);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
// This newer version should not be deleted
|
||||||
|
MessageId updateId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(updateId, BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 4),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||||
|
));
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(metadataParser).parse(meta);
|
||||||
|
will(returnValue(metaDictionary));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroupId);
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
oneOf(db).deleteMessage(txn, message.getId());
|
||||||
|
oneOf(db).deleteMessageMetadata(txn, message.getId());
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
assertEquals(ACCEPT_DO_NOT_SHARE,
|
||||||
|
t.incomingMessage(txn, message, meta));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatesAndStoresLocalPropertiesWithNewVersionOnPairing()
|
||||||
|
throws Exception {
|
||||||
|
Contact contact = getContact();
|
||||||
|
List<Contact> contacts = singletonList(contact);
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
MessageId latestId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(latestId, BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||||
|
));
|
||||||
|
// Some remote props, ignored
|
||||||
|
messageMetadata.put(new MessageId(getRandomId()), BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 3),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||||
|
));
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(contactManager).getContacts();
|
||||||
|
will(returnValue(contacts));
|
||||||
|
oneOf(crypto).generateUniqueId();
|
||||||
|
will(returnValue(props.getAuthToken()));
|
||||||
|
oneOf(crypto).generateUniqueId();
|
||||||
|
will(returnValue(props.getInboxId()));
|
||||||
|
oneOf(crypto).generateUniqueId();
|
||||||
|
will(returnValue(props.getOutboxId()));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
expectStoreMessage(txn, contactGroup.getId(), propsDict, 2, true);
|
||||||
|
oneOf(db).removeMessage(txn, latestId);
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
t.mailboxPaired(txn, ownProps.getOnion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStoresEmptyLocalPropertiesWithNewVersionOnUnpairing()
|
||||||
|
throws Exception {
|
||||||
|
Contact contact = getContact();
|
||||||
|
List<Contact> contacts = singletonList(contact);
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
MessageId latestId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(latestId, BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||||
|
));
|
||||||
|
// Some remote props, ignored
|
||||||
|
messageMetadata.put(new MessageId(getRandomId()), BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 3),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||||
|
));
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(contactManager).getContacts();
|
||||||
|
will(returnValue(contacts));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
expectStoreMessage(txn, contactGroup.getId(), emptyPropsDict,
|
||||||
|
2, true);
|
||||||
|
oneOf(db).removeMessage(txn, latestId);
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
t.mailboxUnpaired(txn);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetRemoteProperties()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||||
|
);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
MessageId fooUpdateId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(fooUpdateId, metaDictionary);
|
||||||
|
BdfList fooUpdate = BdfList.of(1, propsDict);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
|
will(returnValue(contact));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
||||||
|
will(returnValue(fooUpdate));
|
||||||
|
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||||
|
propsDict);
|
||||||
|
will(returnValue(props));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
MailboxPropertiesUpdate remote =
|
||||||
|
t.getRemoteProperties(txn, contact.getId());
|
||||||
|
assertTrue(mailboxPropertiesUpdateEqual(remote, props));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetRemotePropertiesReturnsNullBecauseNoUpdate()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
Map<MessageId, BdfDictionary> emptyMessageMetadata =
|
||||||
|
new LinkedHashMap<>();
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
|
will(returnValue(contact));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(emptyMessageMetadata));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
assertNull(t.getRemoteProperties(txn, contact.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetRemotePropertiesReturnsNullBecauseEmptyUpdate()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||||
|
);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
MessageId fooUpdateId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(fooUpdateId, metaDictionary);
|
||||||
|
BdfList fooUpdate = BdfList.of(1, emptyPropsDict);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
|
will(returnValue(contact));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
||||||
|
will(returnValue(fooUpdate));
|
||||||
|
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||||
|
emptyPropsDict);
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
assertNull(t.getRemoteProperties(txn, contact.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetLocalProperties()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||||
|
);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
MessageId fooUpdateId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(fooUpdateId, metaDictionary);
|
||||||
|
BdfList fooUpdate = BdfList.of(1, propsDict);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
|
will(returnValue(contact));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
||||||
|
will(returnValue(fooUpdate));
|
||||||
|
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||||
|
propsDict);
|
||||||
|
will(returnValue(props));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
MailboxPropertiesUpdate local =
|
||||||
|
t.getLocalProperties(txn, contact.getId());
|
||||||
|
assertTrue(mailboxPropertiesUpdateEqual(local, props));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetLocalPropertiesReturnsNullBecauseNoUpdate()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
Map<MessageId, BdfDictionary> emptyMessageMetadata =
|
||||||
|
new LinkedHashMap<>();
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
|
will(returnValue(contact));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(emptyMessageMetadata));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
assertNull(t.getLocalProperties(txn, contact.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetLocalPropertiesReturnsNullBecauseEmptyUpdate()
|
||||||
|
throws Exception {
|
||||||
|
Transaction txn = new Transaction(null, false);
|
||||||
|
Contact contact = getContact();
|
||||||
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||||
|
);
|
||||||
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
|
MessageId fooUpdateId = new MessageId(getRandomId());
|
||||||
|
messageMetadata.put(fooUpdateId, metaDictionary);
|
||||||
|
BdfList fooUpdate = BdfList.of(1, emptyPropsDict);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
|
will(returnValue(contact));
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact);
|
||||||
|
will(returnValue(contactGroup));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup.getId());
|
||||||
|
will(returnValue(messageMetadata));
|
||||||
|
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
||||||
|
will(returnValue(fooUpdate));
|
||||||
|
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||||
|
emptyPropsDict);
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
|
||||||
|
MailboxPropertyManagerImpl t = createInstance();
|
||||||
|
assertNull(t.getLocalProperties(txn, contact.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectStoreMessage(Transaction txn, GroupId g,
|
||||||
|
BdfDictionary properties, long version, boolean local)
|
||||||
|
throws Exception {
|
||||||
|
BdfList body = BdfList.of(version, properties);
|
||||||
|
Message message = getMessage(g);
|
||||||
|
long timestamp = message.getTimestamp();
|
||||||
|
BdfDictionary meta = BdfDictionary.of(
|
||||||
|
new BdfEntry(MSG_KEY_VERSION, version),
|
||||||
|
new BdfEntry(MSG_KEY_LOCAL, local)
|
||||||
|
);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(timestamp));
|
||||||
|
oneOf(clientHelper).createMessage(g, timestamp, body);
|
||||||
|
will(returnValue(message));
|
||||||
|
oneOf(clientHelper).addLocalMessage(txn, message, meta, true,
|
||||||
|
false);
|
||||||
|
}});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
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.mailbox.MailboxAuthToken;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertiesUpdate;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPropertyManager;
|
||||||
|
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.jmock.Expectations;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class MailboxPropertyValidatorTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
|
||||||
|
|
||||||
|
private final BdfDictionary bdfDict;
|
||||||
|
private final MailboxPropertiesUpdate mailboxProps;
|
||||||
|
private final Group group;
|
||||||
|
private final Message message;
|
||||||
|
private final MailboxPropertyValidator mpv;
|
||||||
|
|
||||||
|
public MailboxPropertyValidatorTest() {
|
||||||
|
// Just dummies, clientHelper is mocked so our test is a bit shallow;
|
||||||
|
// not testing
|
||||||
|
// {@link ClientHelper#parseAndValidateMailboxPropertiesUpdate(BdfDictionary)}
|
||||||
|
bdfDict = BdfDictionary.of(new BdfEntry("foo", "bar"));
|
||||||
|
mailboxProps = new MailboxPropertiesUpdate("baz",
|
||||||
|
new MailboxAuthToken(getRandomId()),
|
||||||
|
new MailboxFolderId(getRandomId()),
|
||||||
|
new MailboxFolderId(getRandomId()));
|
||||||
|
|
||||||
|
group = getGroup(MailboxPropertyManager.CLIENT_ID,
|
||||||
|
MailboxPropertyManager.MAJOR_VERSION);
|
||||||
|
message = getMessage(group.getId());
|
||||||
|
|
||||||
|
MetadataEncoder metadataEncoder = context.mock(MetadataEncoder.class);
|
||||||
|
Clock clock = context.mock(Clock.class);
|
||||||
|
mpv = new MailboxPropertyValidator(clientHelper, metadataEncoder,
|
||||||
|
clock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateMessageBody() throws IOException {
|
||||||
|
BdfList body = BdfList.of(4, bdfDict);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||||
|
bdfDict);
|
||||||
|
will(returnValue(mailboxProps));
|
||||||
|
}});
|
||||||
|
|
||||||
|
BdfDictionary result =
|
||||||
|
mpv.validateMessage(message, group, body).getDictionary();
|
||||||
|
assertEquals(4, result.getLong("version").longValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateWrongVersionValue() throws IOException {
|
||||||
|
BdfList body = BdfList.of(-1, bdfDict);
|
||||||
|
mpv.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = FormatException.class)
|
||||||
|
public void testValidateWrongVersionType() throws IOException {
|
||||||
|
BdfList body = BdfList.of(bdfDict, bdfDict);
|
||||||
|
mpv.validateMessage(message, group, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptyPropertiesReturnsNull() throws IOException {
|
||||||
|
BdfDictionary emptyBdfDict = new BdfDictionary();
|
||||||
|
BdfList body = BdfList.of(42, emptyBdfDict);
|
||||||
|
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||||
|
emptyBdfDict);
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
|
||||||
|
BdfDictionary result =
|
||||||
|
mpv.validateMessage(message, group, body).getDictionary();
|
||||||
|
assertEquals(42, result.getLong("version").longValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,9 +16,4 @@ public class SessionId extends UniqueId {
|
|||||||
public SessionId(byte[] id) {
|
public SessionId(byte[] id) {
|
||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
return o instanceof SessionId && super.equals(o);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -299,7 +299,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
|
|||||||
private String getRandomTorAddress() {
|
private String getRandomTorAddress() {
|
||||||
byte[] pubkeyBytes =
|
byte[] pubkeyBytes =
|
||||||
crypto.generateSignatureKeyPair().getPublic().getEncoded();
|
crypto.generateSignatureKeyPair().getPublic().getEncoded();
|
||||||
return crypto.encodeOnionAddress(pubkeyBytes);
|
return crypto.encodeOnion(pubkeyBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAvatar(Contact c) throws DbException {
|
private void addAvatar(Contact c) throws DbException {
|
||||||
|
|||||||
Reference in New Issue
Block a user