From edbf5ff5b41239c97cd0803afd1bcf2730e05765 Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Tue, 8 Nov 2016 15:44:23 +0000
Subject: [PATCH] Preliminaries for private group invitation protocol.

---
 .../res/layout/fragment_create_group.xml      |  2 +
 briar-android/res/values/strings.xml          |  6 +-
 .../android/ActivityComponent.java            | 19 ++++++
 .../api/clients/BdfMessageContext.java        | 22 ++++---
 .../api/clients/ClientHelper.java             | 20 ++++--
 .../briarproject/api/clients/NamedGroup.java  |  3 +-
 .../briarproject/api/data/BdfDictionary.java  |  9 +++
 .../org/briarproject/api/data/BdfList.java    |  8 +++
 .../api/forum/ForumInvitationResponse.java    |  5 +-
 .../api/privategroup/GroupMessageFactory.java |  2 +
 .../api/privategroup/PrivateGroup.java        |  4 +-
 .../api/privategroup/PrivateGroupFactory.java |  5 +-
 .../api/sync/BaseMessageContext.java          | 19 ------
 .../briarproject/api/sync/MessageContext.java | 21 ++++---
 .../briarproject/api/sync/MessageFactory.java |  4 +-
 .../clients/BdfMessageValidator.java          | 23 ++++---
 .../clients/ClientHelperImpl.java             | 61 +++++++++++++++----
 .../briarproject/clients/ClientsModule.java   |  4 +-
 .../privategroup/PrivateGroupFactoryImpl.java |  7 ++-
 .../briarproject/sync/MessageFactoryImpl.java | 16 ++++-
 .../sync/ValidationManagerImpl.java           |  3 +-
 21 files changed, 179 insertions(+), 84 deletions(-)
 delete mode 100644 briar-api/src/org/briarproject/api/sync/BaseMessageContext.java

diff --git a/briar-android/res/layout/fragment_create_group.xml b/briar-android/res/layout/fragment_create_group.xml
index a9601e2abb..67754dfd56 100644
--- a/briar-android/res/layout/fragment_create_group.xml
+++ b/briar-android/res/layout/fragment_create_group.xml
@@ -12,6 +12,8 @@
 		android:layout_height="0dp"
 		android:layout_weight="1"
 		android:gravity="bottom"
+		android:maxLines="1"
+		android:inputType="text|textCapSentences"
 		android:hint="@string/groups_create_group_hint"/>
 
 	<Button
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index 3d9e47c001..368fc5664f 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -181,7 +181,7 @@
 
 	<!-- Private Group Invitations -->
 	<string name="groups_invitations_title">Group Invitations</string>
-	<string name="groups_invitations_invitation_sent">You have invited %1$s to your group "%2$s".</string>
+	<string name="groups_invitations_invitation_sent">You have invited %1$s to join the group "%2$s".</string>
 	<string name="groups_invitations_invitation_received">%1$s has invited you to join the group "%2$s".</string>
 	<string name="groups_invitations_joined">Joined group</string>
 	<string name="groups_invitations_declined">Group invitation declined</string>
@@ -191,8 +191,8 @@
 	</plurals>
 	<string name="groups_invitations_response_accepted_sent">You accepted the group invitation from %s.</string>
 	<string name="groups_invitations_response_declined_sent">You declined the group invitation from %s.</string>
-	<string name="groups_invitations_response_accepted_received">%s accepted your group invitation.</string>
-	<string name="groups_invitations_response_declined_received">%s declined your group invitation.</string>
+	<string name="groups_invitations_response_accepted_received">%s accepted the group invitation.</string>
+	<string name="groups_invitations_response_declined_received">%s declined the group invitation.</string>
 
 	<!-- Forums -->
 	<string name="no_forums">You don\'t have any forums yet.\n\nWhy don\'t you create a new one yourself by tapping the + icon at the top?\n\nYou can also ask your contacts to share forums with you.</string>
diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java
index ee1ee02d59..fa2e845d58 100644
--- a/briar-android/src/org/briarproject/android/ActivityComponent.java
+++ b/briar-android/src/org/briarproject/android/ActivityComponent.java
@@ -80,9 +80,13 @@ public interface ActivityComponent {
 	void inject(BlogInvitationActivity activity);
 
 	void inject(CreateGroupActivity activity);
+
 	void inject(GroupActivity activity);
+
 	void inject(GroupInviteActivity activity);
+
 	void inject(GroupInvitationActivity activity);
+
 	void inject(GroupMemberListActivity activity);
 
 	void inject(CreateForumActivity activity);
@@ -104,9 +108,11 @@ public interface ActivityComponent {
 	void inject(BlogFragment fragment);
 
 	void inject(BlogPostFragment fragment);
+
 	void inject(FeedPostFragment fragment);
 
 	void inject(BlogPostPagerFragment fragment);
+
 	void inject(FeedPostPagerFragment fragment);
 
 	void inject(ReblogFragment fragment);
@@ -124,21 +130,34 @@ public interface ActivityComponent {
 	void inject(RssFeedManageActivity activity);
 
 	void inject(EmojiProvider emojiProvider);
+
 	void inject(RecentEmojiPageModel recentEmojiPageModel);
 
 	// Fragments
 	void inject(ContactListFragment fragment);
+
 	void inject(CreateGroupFragment fragment);
+
 	void inject(CreateGroupMessageFragment fragment);
+
 	void inject(GroupListFragment fragment);
+
 	void inject(ForumListFragment fragment);
+
 	void inject(FeedFragment fragment);
+
 	void inject(IntroFragment fragment);
+
 	void inject(ShowQrCodeFragment fragment);
+
 	void inject(ContactChooserFragment fragment);
+
 	void inject(ContactSelectorFragment fragment);
+
 	void inject(ShareForumMessageFragment fragment);
+
 	void inject(ShareBlogMessageFragment fragment);
+
 	void inject(IntroductionMessageFragment fragment);
 
 }
diff --git a/briar-api/src/org/briarproject/api/clients/BdfMessageContext.java b/briar-api/src/org/briarproject/api/clients/BdfMessageContext.java
index 70f410c039..6eceac96d0 100644
--- a/briar-api/src/org/briarproject/api/clients/BdfMessageContext.java
+++ b/briar-api/src/org/briarproject/api/clients/BdfMessageContext.java
@@ -1,25 +1,28 @@
 package org.briarproject.api.clients;
 
 import org.briarproject.api.data.BdfDictionary;
-import org.briarproject.api.sync.BaseMessageContext;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.MessageId;
-import org.jetbrains.annotations.NotNull;
 
 import java.util.Collection;
 import java.util.Collections;
 
-public class BdfMessageContext extends BaseMessageContext {
+import javax.annotation.concurrent.Immutable;
 
-	private final BdfDictionary dictionary;
+@Immutable
+@NotNullByDefault
+public class BdfMessageContext {
 
-	public BdfMessageContext(@NotNull BdfDictionary dictionary,
-			@NotNull Collection<MessageId> dependencies) {
+	private final BdfDictionary dictionary;
+	private final Collection<MessageId> dependencies;
 
-		super(dependencies);
+	public BdfMessageContext(BdfDictionary dictionary,
+			Collection<MessageId> dependencies) {
 		this.dictionary = dictionary;
+		this.dependencies = dependencies;
 	}
 
-	public BdfMessageContext(@NotNull BdfDictionary dictionary) {
+	public BdfMessageContext(BdfDictionary dictionary) {
 		this(dictionary, Collections.<MessageId>emptyList());
 	}
 
@@ -27,4 +30,7 @@ public class BdfMessageContext extends BaseMessageContext {
 		return dictionary;
 	}
 
+	public Collection<MessageId> getDependencies() {
+		return dependencies;
+	}
 }
diff --git a/briar-api/src/org/briarproject/api/clients/ClientHelper.java b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
index b24c9d2b71..7743f3ba5e 100644
--- a/briar-api/src/org/briarproject/api/clients/ClientHelper.java
+++ b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
@@ -5,14 +5,17 @@ import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.GroupId;
-import org.briarproject.api.sync.InvalidMessageException;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 
 import java.security.GeneralSecurityException;
 import java.util.Map;
 
+import javax.annotation.Nullable;
+
+@NotNullByDefault
 public interface ClientHelper {
 
 	void addLocalMessage(Message m, BdfDictionary metadata, boolean shared)
@@ -21,14 +24,21 @@ public interface ClientHelper {
 	void addLocalMessage(Transaction txn, Message m, BdfDictionary metadata,
 			boolean shared) throws DbException, FormatException;
 
-	Message createMessage(GroupId g, long timestamp, BdfDictionary body)
-			throws FormatException;
-
 	Message createMessage(GroupId g, long timestamp, BdfList body)
 			throws FormatException;
 
+	Message createMessageForStoringMetadata(GroupId g);
+
+	@Nullable
+	Message getMessage(MessageId m) throws DbException;
+
+	@Nullable
+	Message getMessage(Transaction txn, MessageId m) throws DbException;
+
+	@Nullable
 	BdfList getMessageAsList(MessageId m) throws DbException, FormatException;
 
+	@Nullable
 	BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException,
 			FormatException;
 
@@ -80,6 +90,8 @@ public interface ClientHelper {
 
 	BdfList toList(byte[] b) throws FormatException;
 
+	BdfList toList(Message m) throws FormatException;
+
 	byte[] sign(BdfList toSign, byte[] privateKey)
 			throws FormatException, GeneralSecurityException;
 
diff --git a/briar-api/src/org/briarproject/api/clients/NamedGroup.java b/briar-api/src/org/briarproject/api/clients/NamedGroup.java
index ed4589cae6..176a50858d 100644
--- a/briar-api/src/org/briarproject/api/clients/NamedGroup.java
+++ b/briar-api/src/org/briarproject/api/clients/NamedGroup.java
@@ -2,7 +2,6 @@ package org.briarproject.api.clients;
 
 import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.Group;
-import org.jetbrains.annotations.NotNull;
 
 import javax.annotation.concurrent.Immutable;
 
@@ -13,7 +12,7 @@ public abstract class NamedGroup extends BaseGroup {
 	private final String name;
 	private final byte[] salt;
 
-	public NamedGroup(@NotNull Group group, @NotNull String name, byte[] salt) {
+	public NamedGroup(Group group, String name, byte[] salt) {
 		super(group);
 		this.name = name;
 		this.salt = salt;
diff --git a/briar-api/src/org/briarproject/api/data/BdfDictionary.java b/briar-api/src/org/briarproject/api/data/BdfDictionary.java
index 24f133e82b..cae6f728d6 100644
--- a/briar-api/src/org/briarproject/api/data/BdfDictionary.java
+++ b/briar-api/src/org/briarproject/api/data/BdfDictionary.java
@@ -6,6 +6,8 @@ import org.briarproject.api.FormatException;
 import java.util.Map;
 import java.util.concurrent.ConcurrentSkipListMap;
 
+import javax.annotation.Nullable;
+
 public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
 
 	public static final Object NULL_VALUE = new Object();
@@ -39,6 +41,7 @@ public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public Boolean getOptionalBoolean(String key) throws FormatException {
 		Object o = get(key);
 		if (o == null || o == NULL_VALUE) return null;
@@ -61,6 +64,7 @@ public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public Long getOptionalLong(String key) throws FormatException {
 		Object o = get(key);
 		if (o == null || o == NULL_VALUE) return null;
@@ -87,6 +91,7 @@ public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public Double getOptionalDouble(String key) throws FormatException {
 		Object o = get(key);
 		if (o == null || o == NULL_VALUE) return null;
@@ -108,6 +113,7 @@ public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public String getOptionalString(String key) throws FormatException {
 		Object o = get(key);
 		if (o == null || o == NULL_VALUE) return null;
@@ -128,6 +134,7 @@ public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public byte[] getOptionalRaw(String key) throws FormatException {
 		Object o = get(key);
 		if (o == null || o == NULL_VALUE) return null;
@@ -149,6 +156,7 @@ public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public BdfList getOptionalList(String key) throws FormatException {
 		Object o = get(key);
 		if (o == null || o == NULL_VALUE) return null;
@@ -168,6 +176,7 @@ public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public BdfDictionary getOptionalDictionary(String key)
 			throws FormatException {
 		Object o = get(key);
diff --git a/briar-api/src/org/briarproject/api/data/BdfList.java b/briar-api/src/org/briarproject/api/data/BdfList.java
index 59732f4a05..39b7bd89e9 100644
--- a/briar-api/src/org/briarproject/api/data/BdfList.java
+++ b/briar-api/src/org/briarproject/api/data/BdfList.java
@@ -2,6 +2,7 @@ package org.briarproject.api.data;
 
 import org.briarproject.api.Bytes;
 import org.briarproject.api.FormatException;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.Arrays;
 import java.util.List;
@@ -40,6 +41,7 @@ public class BdfList extends Vector<Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public Boolean getOptionalBoolean(int index) throws FormatException {
 		if (!isInRange(index)) throw new FormatException();
 		Object o = get(index);
@@ -65,6 +67,7 @@ public class BdfList extends Vector<Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public Long getOptionalLong(int index) throws FormatException {
 		if (!isInRange(index)) throw new FormatException();
 		Object o = get(index);
@@ -94,6 +97,7 @@ public class BdfList extends Vector<Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public Double getOptionalDouble(int index) throws FormatException {
 		if (!isInRange(index)) throw new FormatException();
 		Object o = get(index);
@@ -118,6 +122,7 @@ public class BdfList extends Vector<Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public String getOptionalString(int index) throws FormatException {
 		if (!isInRange(index)) throw new FormatException();
 		Object o = get(index);
@@ -141,6 +146,7 @@ public class BdfList extends Vector<Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public byte[] getOptionalRaw(int index) throws FormatException {
 		if (!isInRange(index)) throw new FormatException();
 		Object o = get(index);
@@ -165,6 +171,7 @@ public class BdfList extends Vector<Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public BdfList getOptionalList(int index) throws FormatException {
 		if (!isInRange(index)) throw new FormatException();
 		Object o = get(index);
@@ -187,6 +194,7 @@ public class BdfList extends Vector<Object> {
 		throw new FormatException();
 	}
 
+	@Nullable
 	public BdfDictionary getOptionalDictionary(int index)
 			throws FormatException {
 		if (!isInRange(index)) throw new FormatException();
diff --git a/briar-api/src/org/briarproject/api/forum/ForumInvitationResponse.java b/briar-api/src/org/briarproject/api/forum/ForumInvitationResponse.java
index d9c49d1545..54969d97cb 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumInvitationResponse.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumInvitationResponse.java
@@ -6,13 +6,12 @@ import org.briarproject.api.sharing.InvitationResponse;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 public class ForumInvitationResponse extends InvitationResponse {
 
 	public ForumInvitationResponse(@NotNull MessageId id, SessionId sessionId,
-			GroupId groupId, ContactId contactId, boolean accept, long time, boolean local,
-			boolean sent, boolean seen, boolean read) {
+			GroupId groupId, ContactId contactId, boolean accept, long time,
+			boolean local, boolean sent, boolean seen, boolean read) {
 
 		super(id, sessionId, groupId, contactId, accept, time, local, sent,
 				seen, read);
diff --git a/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java b/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java
index 3b50246958..ff1b72e84f 100644
--- a/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java
+++ b/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java
@@ -3,10 +3,12 @@ package org.briarproject.api.privategroup;
 import org.briarproject.api.crypto.CryptoExecutor;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.jetbrains.annotations.Nullable;
 
+@NotNullByDefault
 public interface GroupMessageFactory {
 
 	/**
diff --git a/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java b/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java
index 9b57d99910..ab8d520590 100644
--- a/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java
+++ b/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java
@@ -5,7 +5,6 @@ import org.briarproject.api.identity.Author;
 import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sharing.Shareable;
 import org.briarproject.api.sync.Group;
-import org.jetbrains.annotations.NotNull;
 
 import javax.annotation.concurrent.Immutable;
 
@@ -15,8 +14,7 @@ public class PrivateGroup extends NamedGroup implements Shareable {
 
 	private final Author author;
 
-	public PrivateGroup(@NotNull Group group, @NotNull String name,
-			@NotNull Author author, @NotNull byte[] salt) {
+	public PrivateGroup(Group group, String name, Author author, byte[] salt) {
 		super(group, name, salt);
 		this.author = author;
 	}
diff --git a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupFactory.java b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupFactory.java
index 6a27552fa2..e4a13eeb78 100644
--- a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupFactory.java
+++ b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupFactory.java
@@ -2,21 +2,20 @@ package org.briarproject.api.privategroup;
 
 import org.briarproject.api.FormatException;
 import org.briarproject.api.identity.Author;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.Group;
-import org.jetbrains.annotations.NotNull;
 
+@NotNullByDefault
 public interface PrivateGroupFactory {
 
 	/**
 	 * Creates a private group with the given name and author.
 	 */
-	@NotNull
 	PrivateGroup createPrivateGroup(String name, Author author);
 
 	/**
 	 * Creates a private group with the given name, author and salt.
 	 */
-	@NotNull
 	PrivateGroup createPrivateGroup(String name, Author author, byte[] salt);
 
 	/**
diff --git a/briar-api/src/org/briarproject/api/sync/BaseMessageContext.java b/briar-api/src/org/briarproject/api/sync/BaseMessageContext.java
deleted file mode 100644
index 7eb411a6a0..0000000000
--- a/briar-api/src/org/briarproject/api/sync/BaseMessageContext.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.briarproject.api.sync;
-
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Collection;
-
-public abstract class BaseMessageContext {
-
-	private final Collection<MessageId> dependencies;
-
-	public BaseMessageContext(@NotNull Collection<MessageId> dependencies) {
-		this.dependencies = dependencies;
-	}
-
-	public Collection<MessageId> getDependencies() {
-		return dependencies;
-	}
-
-}
diff --git a/briar-api/src/org/briarproject/api/sync/MessageContext.java b/briar-api/src/org/briarproject/api/sync/MessageContext.java
index d0bef01179..ce78942213 100644
--- a/briar-api/src/org/briarproject/api/sync/MessageContext.java
+++ b/briar-api/src/org/briarproject/api/sync/MessageContext.java
@@ -1,23 +1,27 @@
 package org.briarproject.api.sync;
 
 import org.briarproject.api.db.Metadata;
-import org.jetbrains.annotations.NotNull;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 
 import java.util.Collection;
 import java.util.Collections;
 
-public class MessageContext extends BaseMessageContext {
+import javax.annotation.concurrent.Immutable;
 
-	private final Metadata metadata;
+@Immutable
+@NotNullByDefault
+public class MessageContext {
 
-	public MessageContext(@NotNull Metadata metadata,
-			@NotNull Collection<MessageId> dependencies) {
+	private final Metadata metadata;
+	private final Collection<MessageId> dependencies;
 
-		super(dependencies);
+	public MessageContext(Metadata metadata,
+			Collection<MessageId> dependencies) {
 		this.metadata = metadata;
+		this.dependencies = dependencies;
 	}
 
-	public MessageContext(@NotNull Metadata metadata) {
+	public MessageContext(Metadata metadata) {
 		this(metadata, Collections.<MessageId>emptyList());
 	}
 
@@ -25,4 +29,7 @@ public class MessageContext extends BaseMessageContext {
 		return metadata;
 	}
 
+	public Collection<MessageId> getDependencies() {
+		return dependencies;
+	}
 }
diff --git a/briar-api/src/org/briarproject/api/sync/MessageFactory.java b/briar-api/src/org/briarproject/api/sync/MessageFactory.java
index 35a3746249..6b599a8f1f 100644
--- a/briar-api/src/org/briarproject/api/sync/MessageFactory.java
+++ b/briar-api/src/org/briarproject/api/sync/MessageFactory.java
@@ -2,5 +2,7 @@ package org.briarproject.api.sync;
 
 public interface MessageFactory {
 
-	Message createMessage(GroupId groupId, long timestamp, byte[] body);
+	Message createMessage(GroupId g, long timestamp, byte[] body);
+
+	Message createMessage(MessageId m, byte[] raw);
 }
diff --git a/briar-core/src/org/briarproject/clients/BdfMessageValidator.java b/briar-core/src/org/briarproject/clients/BdfMessageValidator.java
index 0ebcbfb155..2a78e794dd 100644
--- a/briar-core/src/org/briarproject/clients/BdfMessageValidator.java
+++ b/briar-core/src/org/briarproject/clients/BdfMessageValidator.java
@@ -1,10 +1,10 @@
 package org.briarproject.clients;
 
 import org.briarproject.api.FormatException;
+import org.briarproject.api.clients.BdfMessageContext;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.clients.MessageQueueManager.QueueMessageValidator;
 import org.briarproject.api.clients.QueueMessage;
-import org.briarproject.api.clients.BdfMessageContext;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.data.MetadataEncoder;
@@ -12,13 +12,15 @@ import org.briarproject.api.db.Metadata;
 import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.InvalidMessageException;
 import org.briarproject.api.sync.Message;
-import org.briarproject.api.sync.ValidationManager.MessageValidator;
 import org.briarproject.api.sync.MessageContext;
+import org.briarproject.api.sync.ValidationManager.MessageValidator;
 import org.briarproject.api.system.Clock;
 import org.briarproject.util.StringUtils;
 
 import java.util.logging.Logger;
 
+import javax.annotation.Nullable;
+
 import static org.briarproject.api.clients.QueueMessage.QUEUE_MESSAGE_HEADER_LENGTH;
 import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
 import static org.briarproject.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
@@ -78,7 +80,7 @@ public abstract class BdfMessageValidator implements MessageValidator,
 		}
 	}
 
-	protected void checkLength(String s, int minLength, int maxLength)
+	protected void checkLength(@Nullable String s, int minLength, int maxLength)
 			throws FormatException {
 		if (s != null) {
 			int length = StringUtils.toUtf8(s).length;
@@ -87,12 +89,13 @@ public abstract class BdfMessageValidator implements MessageValidator,
 		}
 	}
 
-	protected void checkLength(String s, int length) throws FormatException {
+	protected void checkLength(@Nullable String s, int length)
+			throws FormatException {
 		if (s != null && StringUtils.toUtf8(s).length != length)
 			throw new FormatException();
 	}
 
-	protected void checkLength(byte[] b, int minLength, int maxLength)
+	protected void checkLength(@Nullable byte[] b, int minLength, int maxLength)
 			throws FormatException {
 		if (b != null) {
 			if (b.length < minLength) throw new FormatException();
@@ -100,7 +103,8 @@ public abstract class BdfMessageValidator implements MessageValidator,
 		}
 	}
 
-	protected void checkLength(byte[] b, int length) throws FormatException {
+	protected void checkLength(@Nullable byte[] b, int length)
+			throws FormatException {
 		if (b != null && b.length != length) throw new FormatException();
 	}
 
@@ -112,11 +116,12 @@ public abstract class BdfMessageValidator implements MessageValidator,
 		}
 	}
 
-	protected void checkSize(BdfList list, int size) throws FormatException {
+	protected void checkSize(@Nullable BdfList list, int size)
+			throws FormatException {
 		if (list != null && list.size() != size) throw new FormatException();
 	}
 
-	protected void checkSize(BdfDictionary dictionary, int minSize,
+	protected void checkSize(@Nullable BdfDictionary dictionary, int minSize,
 			int maxSize) throws FormatException {
 		if (dictionary != null) {
 			if (dictionary.size() < minSize) throw new FormatException();
@@ -124,7 +129,7 @@ public abstract class BdfMessageValidator implements MessageValidator,
 		}
 	}
 
-	protected void checkSize(BdfDictionary dictionary, int size)
+	protected void checkSize(@Nullable BdfDictionary dictionary, int size)
 			throws FormatException {
 		if (dictionary != null && dictionary.size() != size)
 			throw new FormatException();
diff --git a/briar-core/src/org/briarproject/clients/ClientHelperImpl.java b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
index d72d78b550..e78e1d36f1 100644
--- a/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
+++ b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
@@ -19,6 +19,7 @@ import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Metadata;
 import org.briarproject.api.db.Transaction;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageFactory;
@@ -37,28 +38,35 @@ import javax.inject.Inject;
 
 import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
 
+@NotNullByDefault
 class ClientHelperImpl implements ClientHelper {
 
+	/**
+	 * Length in bytes of the random salt used for creating local messages for
+	 * storing metadata.
+	 */
+	private static final int SALT_LENGTH = 32;
+
 	private final DatabaseComponent db;
 	private final MessageFactory messageFactory;
 	private final BdfReaderFactory bdfReaderFactory;
 	private final BdfWriterFactory bdfWriterFactory;
 	private final MetadataParser metadataParser;
 	private final MetadataEncoder metadataEncoder;
-	private final CryptoComponent cryptoComponent;
+	private final CryptoComponent crypto;
 
 	@Inject
 	ClientHelperImpl(DatabaseComponent db, MessageFactory messageFactory,
 			BdfReaderFactory bdfReaderFactory,
 			BdfWriterFactory bdfWriterFactory, MetadataParser metadataParser,
-			MetadataEncoder metadataEncoder, CryptoComponent cryptoComponent) {
+			MetadataEncoder metadataEncoder, CryptoComponent crypto) {
 		this.db = db;
 		this.messageFactory = messageFactory;
 		this.bdfReaderFactory = bdfReaderFactory;
 		this.bdfWriterFactory = bdfWriterFactory;
 		this.metadataParser = metadataParser;
 		this.metadataEncoder = metadataEncoder;
-		this.cryptoComponent = cryptoComponent;
+		this.crypto = crypto;
 	}
 
 	@Override
@@ -81,15 +89,36 @@ class ClientHelperImpl implements ClientHelper {
 	}
 
 	@Override
-	public Message createMessage(GroupId g, long timestamp, BdfDictionary body)
+	public Message createMessage(GroupId g, long timestamp, BdfList body)
 			throws FormatException {
 		return messageFactory.createMessage(g, timestamp, toByteArray(body));
 	}
 
 	@Override
-	public Message createMessage(GroupId g, long timestamp, BdfList body)
-			throws FormatException {
-		return messageFactory.createMessage(g, timestamp, toByteArray(body));
+	public Message createMessageForStoringMetadata(GroupId g) {
+		byte[] salt = new byte[SALT_LENGTH];
+		crypto.getSecureRandom().nextBytes(salt);
+		return messageFactory.createMessage(g, 0, salt);
+	}
+
+	@Override
+	public Message getMessage(MessageId m) throws DbException {
+		Message message;
+		Transaction txn = db.startTransaction(true);
+		try {
+			message = getMessage(txn, m);
+			db.commitTransaction(txn);
+		} finally {
+			db.endTransaction(txn);
+		}
+		return message;
+	}
+
+	@Override
+	public Message getMessage(Transaction txn, MessageId m) throws DbException {
+		byte[] raw = db.getRawMessage(txn, m);
+		if (raw == null) return null;
+		return messageFactory.createMessage(m, raw);
 	}
 
 	@Override
@@ -310,13 +339,19 @@ class ClientHelperImpl implements ClientHelper {
 		return toList(b, 0, b.length);
 	}
 
+	@Override
+	public BdfList toList(Message m) throws FormatException {
+		byte[] raw = m.getRaw();
+		return toList(raw, MESSAGE_HEADER_LENGTH,
+				raw.length - MESSAGE_HEADER_LENGTH);
+	}
+
 	@Override
 	public byte[] sign(BdfList toSign, byte[] privateKey)
 			throws FormatException, GeneralSecurityException {
-		Signature signature = cryptoComponent.getSignature();
-		KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
-		PrivateKey key =
-				keyParser.parsePrivateKey(privateKey);
+		Signature signature = crypto.getSignature();
+		KeyParser keyParser = crypto.getSignatureKeyParser();
+		PrivateKey key = keyParser.parsePrivateKey(privateKey);
 		signature.initSign(key);
 		signature.update(toByteArray(toSign));
 		return signature.sign();
@@ -326,10 +361,10 @@ class ClientHelperImpl implements ClientHelper {
 	public void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
 			throws FormatException, GeneralSecurityException {
 		// Parse the public key
-		KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
+		KeyParser keyParser = crypto.getSignatureKeyParser();
 		PublicKey key = keyParser.parsePublicKey(publicKey);
 		// Verify the signature
-		Signature signature = cryptoComponent.getSignature();
+		Signature signature = crypto.getSignature();
 		signature.initVerify(key);
 		signature.update(toByteArray(signed));
 		if (!signature.verify(sig)) {
diff --git a/briar-core/src/org/briarproject/clients/ClientsModule.java b/briar-core/src/org/briarproject/clients/ClientsModule.java
index 9f4160ce3e..480c7f016d 100644
--- a/briar-core/src/org/briarproject/clients/ClientsModule.java
+++ b/briar-core/src/org/briarproject/clients/ClientsModule.java
@@ -1,8 +1,8 @@
 package org.briarproject.clients;
 
 import org.briarproject.api.clients.ClientHelper;
-import org.briarproject.api.clients.MessageQueueManager;
 import org.briarproject.api.clients.ContactGroupFactory;
+import org.briarproject.api.clients.MessageQueueManager;
 import org.briarproject.api.clients.QueueMessageFactory;
 import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.data.BdfReaderFactory;
@@ -33,7 +33,7 @@ public class ClientsModule {
 	}
 
 	@Provides
-	ContactGroupFactory providePrivateGroupFactory(GroupFactory groupFactory,
+	ContactGroupFactory provideContactGroupFactory(GroupFactory groupFactory,
 			ClientHelper clientHelper) {
 		return new ContactGroupFactoryImpl(groupFactory, clientHelper);
 	}
diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupFactoryImpl.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupFactoryImpl.java
index 7389f2fd69..183bcbd7b7 100644
--- a/briar-core/src/org/briarproject/privategroup/PrivateGroupFactoryImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupFactoryImpl.java
@@ -5,20 +5,23 @@ import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.AuthorFactory;
+import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.privategroup.PrivateGroup;
 import org.briarproject.api.privategroup.PrivateGroupFactory;
 import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupFactory;
 import org.briarproject.util.StringUtils;
-import org.jetbrains.annotations.NotNull;
 
 import java.security.SecureRandom;
 
+import javax.annotation.concurrent.Immutable;
 import javax.inject.Inject;
 
 import static org.briarproject.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH;
 import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
 
+@Immutable
+@NotNullByDefault
 class PrivateGroupFactoryImpl implements PrivateGroupFactory {
 
 	private final GroupFactory groupFactory;
@@ -37,7 +40,6 @@ class PrivateGroupFactoryImpl implements PrivateGroupFactory {
 		this.random = random;
 	}
 
-	@NotNull
 	@Override
 	public PrivateGroup createPrivateGroup(String name, Author author) {
 		int length = StringUtils.toUtf8(name).length;
@@ -50,7 +52,6 @@ class PrivateGroupFactoryImpl implements PrivateGroupFactory {
 		return createPrivateGroup(name, author, salt);
 	}
 
-	@NotNull
 	@Override
 	public PrivateGroup createPrivateGroup(String name, Author author,
 			byte[] salt) {
diff --git a/briar-core/src/org/briarproject/sync/MessageFactoryImpl.java b/briar-core/src/org/briarproject/sync/MessageFactoryImpl.java
index 68b5e01b0d..a92f550c64 100644
--- a/briar-core/src/org/briarproject/sync/MessageFactoryImpl.java
+++ b/briar-core/src/org/briarproject/sync/MessageFactoryImpl.java
@@ -24,14 +24,24 @@ class MessageFactoryImpl implements MessageFactory {
 	}
 
 	@Override
-	public Message createMessage(GroupId groupId, long timestamp, byte[] body) {
+	public Message createMessage(GroupId g, long timestamp, byte[] body) {
 		if (body.length > MAX_MESSAGE_BODY_LENGTH)
 			throw new IllegalArgumentException();
 		byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
-		System.arraycopy(groupId.getBytes(), 0, raw, 0, UniqueId.LENGTH);
+		System.arraycopy(g.getBytes(), 0, raw, 0, UniqueId.LENGTH);
 		ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
 		System.arraycopy(body, 0, raw, MESSAGE_HEADER_LENGTH, body.length);
 		MessageId id = new MessageId(crypto.hash(MessageId.LABEL, raw));
-		return new Message(id, groupId, timestamp, raw);
+		return new Message(id, g, timestamp, raw);
+	}
+
+	@Override
+	public Message createMessage(MessageId m, byte[] raw) {
+		if (raw.length < MESSAGE_HEADER_LENGTH)
+			throw new IllegalArgumentException();
+		byte[] groupId = new byte[UniqueId.LENGTH];
+		System.arraycopy(raw, 0, groupId, 0, UniqueId.LENGTH);
+		long timestamp = ByteUtils.readUint64(raw, UniqueId.LENGTH);
+		return new Message(m, new GroupId(groupId), timestamp, raw);
 	}
 }
diff --git a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java
index babfac7576..289bde1228 100644
--- a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java
+++ b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java
@@ -274,7 +274,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
 				MessageContext context = v.validateMessage(m, g);
 				storeMessageContextAsync(m, g.getClientId(), context);
 			} catch (InvalidMessageException e) {
-				if (LOG.isLoggable(INFO)) LOG.info(e.toString());
+				if (LOG.isLoggable(INFO))
+					LOG.log(INFO, e.toString(), e);
 				Queue<MessageId> invalidate = new LinkedList<MessageId>();
 				invalidate.add(m.getId());
 				invalidateNextMessageAsync(invalidate);
-- 
GitLab