From 1731369d7af9f761f95e36c20c69a9a6d169b05c Mon Sep 17 00:00:00 2001
From: Torsten Grote <t@grobox.de>
Date: Wed, 5 Oct 2016 09:20:02 -0300
Subject: [PATCH] Refactor SharingManager so its events provide message header

---
 .../api/blogs/BlogSharingMessage.java         |  8 ++-
 .../event/BlogInvitationReceivedEvent.java    |  6 +-
 .../BlogInvitationResponseReceivedEvent.java  |  5 +-
 .../event/ForumInvitationReceivedEvent.java   |  7 ++-
 .../ForumInvitationResponseReceivedEvent.java |  5 +-
 .../api/event/InvitationReceivedEvent.java    |  9 ++-
 .../InvitationResponseReceivedEvent.java      | 10 +++-
 .../api/forum/ForumInvitationRequest.java     |  3 +-
 .../api/forum/ForumInvitationResponse.java    |  3 +-
 .../api/forum/ForumSharingMessage.java        |  8 ++-
 .../api/sharing/InvitationRequest.java        |  2 +
 .../api/sharing/SharingConstants.java         |  2 +
 .../api/sharing/SharingMessage.java           | 21 +++++--
 .../sharing/BlogInviteeSessionState.java      |  6 +-
 .../sharing/BlogSharerSessionState.java       |  6 +-
 .../sharing/BlogSharingManagerImpl.java       | 49 +++++++++++-----
 .../sharing/ForumInviteeSessionState.java     |  7 ++-
 .../sharing/ForumSharerSessionState.java      |  7 ++-
 .../sharing/ForumSharingManagerImpl.java      | 48 +++++++++++-----
 .../sharing/InvitationFactory.java            |  2 +-
 .../InvitationReceivedEventFactory.java       |  2 +-
 ...nvitationResponseReceivedEventFactory.java |  2 +-
 .../briarproject/sharing/InviteeEngine.java   | 23 ++++++--
 .../sharing/InviteeSessionState.java          | 12 +++-
 .../sharing/InviteeSessionStateFactory.java   |  3 +-
 .../briarproject/sharing/SharerEngine.java    | 25 ++++++---
 .../sharing/SharerSessionState.java           | 18 +++++-
 .../sharing/SharingManagerImpl.java           | 56 +++++++++++--------
 28 files changed, 253 insertions(+), 102 deletions(-)

diff --git a/briar-api/src/org/briarproject/api/blogs/BlogSharingMessage.java b/briar-api/src/org/briarproject/api/blogs/BlogSharingMessage.java
index 105b0d2a3f..1465225e66 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogSharingMessage.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogSharingMessage.java
@@ -13,6 +13,7 @@ import static org.briarproject.api.blogs.BlogConstants.BLOG_PUBLIC_KEY;
 import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE;
 import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG;
 import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
+import static org.briarproject.api.sharing.SharingConstants.TIME;
 
 public interface BlogSharingMessage {
 
@@ -25,9 +26,9 @@ public interface BlogSharingMessage {
 
 		public BlogInvitation(GroupId groupId, SessionId sessionId,
 				String blogTitle, String blogDesc, String blogAuthorName,
-				byte[] blogPublicKey, String message) {
+				byte[] blogPublicKey, long time, String message) {
 
-			super(groupId, sessionId, message);
+			super(groupId, sessionId, time, message);
 
 			this.blogTitle = blogTitle;
 			this.blogDesc = blogDesc;
@@ -65,9 +66,10 @@ public interface BlogSharingMessage {
 			String blogAuthorName = d.getString(BLOG_AUTHOR_NAME);
 			byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY);
 			String message = d.getOptionalString(INVITATION_MSG);
+			long time = d.getLong(TIME);
 
 			return new BlogInvitation(groupId, sessionId, blogTitle,
-					blogDesc, blogAuthorName, blogPublicKey, message);
+					blogDesc, blogAuthorName, blogPublicKey, time, message);
 		}
 
 		public String getBlogTitle() {
diff --git a/briar-api/src/org/briarproject/api/event/BlogInvitationReceivedEvent.java b/briar-api/src/org/briarproject/api/event/BlogInvitationReceivedEvent.java
index 12d8d4cc4b..b92a466678 100644
--- a/briar-api/src/org/briarproject/api/event/BlogInvitationReceivedEvent.java
+++ b/briar-api/src/org/briarproject/api/event/BlogInvitationReceivedEvent.java
@@ -2,13 +2,15 @@ package org.briarproject.api.event;
 
 import org.briarproject.api.blogs.Blog;
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.sharing.InvitationRequest;
 
 public class BlogInvitationReceivedEvent extends InvitationReceivedEvent {
 
 	private final Blog blog;
 
-	public BlogInvitationReceivedEvent(Blog blog, ContactId contactId) {
-		super(contactId);
+	public BlogInvitationReceivedEvent(Blog blog, ContactId contactId,
+			InvitationRequest request) {
+		super(contactId, request);
 		this.blog = blog;
 	}
 
diff --git a/briar-api/src/org/briarproject/api/event/BlogInvitationResponseReceivedEvent.java b/briar-api/src/org/briarproject/api/event/BlogInvitationResponseReceivedEvent.java
index b6344ad928..fc4c04be25 100644
--- a/briar-api/src/org/briarproject/api/event/BlogInvitationResponseReceivedEvent.java
+++ b/briar-api/src/org/briarproject/api/event/BlogInvitationResponseReceivedEvent.java
@@ -1,5 +1,6 @@
 package org.briarproject.api.event;
 
+import org.briarproject.api.blogs.BlogInvitationResponse;
 import org.briarproject.api.contact.ContactId;
 
 public class BlogInvitationResponseReceivedEvent extends InvitationResponseReceivedEvent {
@@ -7,8 +8,8 @@ public class BlogInvitationResponseReceivedEvent extends InvitationResponseRecei
 	private final String blogTitle;
 
 	public BlogInvitationResponseReceivedEvent(String blogTitle,
-			ContactId contactId) {
-		super(contactId);
+			ContactId contactId, BlogInvitationResponse response) {
+		super(contactId, response);
 		this.blogTitle = blogTitle;
 	}
 
diff --git a/briar-api/src/org/briarproject/api/event/ForumInvitationReceivedEvent.java b/briar-api/src/org/briarproject/api/event/ForumInvitationReceivedEvent.java
index d823a2a6ba..bbfab9cfe0 100644
--- a/briar-api/src/org/briarproject/api/event/ForumInvitationReceivedEvent.java
+++ b/briar-api/src/org/briarproject/api/event/ForumInvitationReceivedEvent.java
@@ -2,17 +2,20 @@ package org.briarproject.api.event;
 
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.forum.Forum;
+import org.briarproject.api.forum.ForumInvitationRequest;
 
 public class ForumInvitationReceivedEvent extends InvitationReceivedEvent {
 
 	private final Forum forum;
 
-	public ForumInvitationReceivedEvent(Forum forum, ContactId contactId) {
-		super(contactId);
+	public ForumInvitationReceivedEvent(Forum forum, ContactId contactId,
+			ForumInvitationRequest request) {
+		super(contactId, request);
 		this.forum = forum;
 	}
 
 	public Forum getForum() {
 		return forum;
 	}
+
 }
diff --git a/briar-api/src/org/briarproject/api/event/ForumInvitationResponseReceivedEvent.java b/briar-api/src/org/briarproject/api/event/ForumInvitationResponseReceivedEvent.java
index cb51daf0a2..f4669f9462 100644
--- a/briar-api/src/org/briarproject/api/event/ForumInvitationResponseReceivedEvent.java
+++ b/briar-api/src/org/briarproject/api/event/ForumInvitationResponseReceivedEvent.java
@@ -1,14 +1,15 @@
 package org.briarproject.api.event;
 
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.forum.ForumInvitationResponse;
 
 public class ForumInvitationResponseReceivedEvent extends InvitationResponseReceivedEvent {
 
 	private final String forumName;
 
 	public ForumInvitationResponseReceivedEvent(String forumName,
-			ContactId contactId) {
-		super(contactId);
+			ContactId contactId, ForumInvitationResponse response) {
+		super(contactId, response);
 		this.forumName = forumName;
 	}
 
diff --git a/briar-api/src/org/briarproject/api/event/InvitationReceivedEvent.java b/briar-api/src/org/briarproject/api/event/InvitationReceivedEvent.java
index 834b71eddf..a72478d660 100644
--- a/briar-api/src/org/briarproject/api/event/InvitationReceivedEvent.java
+++ b/briar-api/src/org/briarproject/api/event/InvitationReceivedEvent.java
@@ -1,16 +1,23 @@
 package org.briarproject.api.event;
 
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.sharing.InvitationRequest;
 
 public abstract class InvitationReceivedEvent extends Event {
 
 	private final ContactId contactId;
+	private final InvitationRequest request;
 
-	InvitationReceivedEvent(ContactId contactId) {
+	InvitationReceivedEvent(ContactId contactId, InvitationRequest request) {
 		this.contactId = contactId;
+		this.request = request;
 	}
 
 	public ContactId getContactId() {
 		return contactId;
 	}
+
+	public InvitationRequest getRequest() {
+		return request;
+	}
 }
diff --git a/briar-api/src/org/briarproject/api/event/InvitationResponseReceivedEvent.java b/briar-api/src/org/briarproject/api/event/InvitationResponseReceivedEvent.java
index df84a69656..63f41be02d 100644
--- a/briar-api/src/org/briarproject/api/event/InvitationResponseReceivedEvent.java
+++ b/briar-api/src/org/briarproject/api/event/InvitationResponseReceivedEvent.java
@@ -1,16 +1,24 @@
 package org.briarproject.api.event;
 
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.sharing.InvitationResponse;
 
 public abstract class InvitationResponseReceivedEvent extends Event {
 
 	private final ContactId contactId;
+	private final InvitationResponse response;
 
-	public InvitationResponseReceivedEvent(ContactId contactId) {
+	public InvitationResponseReceivedEvent(ContactId contactId,
+			InvitationResponse response) {
 		this.contactId = contactId;
+		this.response = response;
 	}
 
 	public ContactId getContactId() {
 		return contactId;
 	}
+
+	public InvitationResponse getResponse() {
+		return response;
+	}
 }
diff --git a/briar-api/src/org/briarproject/api/forum/ForumInvitationRequest.java b/briar-api/src/org/briarproject/api/forum/ForumInvitationRequest.java
index f043ca2011..7099bf37b2 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumInvitationRequest.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumInvitationRequest.java
@@ -4,12 +4,13 @@ import org.briarproject.api.clients.SessionId;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.sharing.InvitationRequest;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
 
 public class ForumInvitationRequest extends InvitationRequest {
 
 	private final String forumName;
 
-	public ForumInvitationRequest(MessageId id, SessionId sessionId,
+	public ForumInvitationRequest(@Nullable MessageId id, SessionId sessionId,
 			ContactId contactId, String forumName, String message,
 			boolean available, long time, boolean local, boolean sent,
 			boolean seen, boolean read) {
diff --git a/briar-api/src/org/briarproject/api/forum/ForumInvitationResponse.java b/briar-api/src/org/briarproject/api/forum/ForumInvitationResponse.java
index 00a15eac2e..1a76ccb65f 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumInvitationResponse.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumInvitationResponse.java
@@ -4,10 +4,11 @@ import org.briarproject.api.clients.SessionId;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.sharing.InvitationResponse;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
 
 public class ForumInvitationResponse extends InvitationResponse {
 
-	public ForumInvitationResponse(MessageId id, SessionId sessionId,
+	public ForumInvitationResponse(@Nullable MessageId id, SessionId sessionId,
 			ContactId contactId, boolean accept, long time, boolean local,
 			boolean sent, boolean seen, boolean read) {
 
diff --git a/briar-api/src/org/briarproject/api/forum/ForumSharingMessage.java b/briar-api/src/org/briarproject/api/forum/ForumSharingMessage.java
index 45badea549..e93a39c6b8 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumSharingMessage.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumSharingMessage.java
@@ -11,6 +11,7 @@ import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
 import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
 import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG;
 import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
+import static org.briarproject.api.sharing.SharingConstants.TIME;
 
 public interface ForumSharingMessage {
 
@@ -20,9 +21,9 @@ public interface ForumSharingMessage {
 		private final byte[] forumSalt;
 
 		public ForumInvitation(GroupId groupId, SessionId sessionId,
-				String forumName, byte[] forumSalt, String message) {
+				String forumName, byte[] forumSalt, long time, String message) {
 
-			super(groupId, sessionId, message);
+			super(groupId, sessionId, time, message);
 
 			this.forumName = forumName;
 			this.forumSalt = forumSalt;
@@ -53,9 +54,10 @@ public interface ForumSharingMessage {
 			String forumName = d.getString(FORUM_NAME);
 			byte[] forumSalt = d.getRaw(FORUM_SALT);
 			String message = d.getOptionalString(INVITATION_MSG);
+			long time = d.getLong(TIME);
 
 			return new ForumInvitation(groupId, sessionId, forumName, forumSalt,
-					message);
+					time, message);
 		}
 
 		public String getForumName() {
diff --git a/briar-api/src/org/briarproject/api/sharing/InvitationRequest.java b/briar-api/src/org/briarproject/api/sharing/InvitationRequest.java
index 6d5f07d35e..6fe51d88e9 100644
--- a/briar-api/src/org/briarproject/api/sharing/InvitationRequest.java
+++ b/briar-api/src/org/briarproject/api/sharing/InvitationRequest.java
@@ -3,6 +3,7 @@ package org.briarproject.api.sharing;
 import org.briarproject.api.clients.SessionId;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
 
 public abstract class InvitationRequest extends InvitationMessage {
 
@@ -19,6 +20,7 @@ public abstract class InvitationRequest extends InvitationMessage {
 		this.available = available;
 	}
 
+	@Nullable
 	public String getMessage() {
 		return message;
 	}
diff --git a/briar-api/src/org/briarproject/api/sharing/SharingConstants.java b/briar-api/src/org/briarproject/api/sharing/SharingConstants.java
index e09b9015c5..47a990669d 100644
--- a/briar-api/src/org/briarproject/api/sharing/SharingConstants.java
+++ b/briar-api/src/org/briarproject/api/sharing/SharingConstants.java
@@ -19,6 +19,8 @@ public interface SharingConstants {
 	String IS_SHARER = "isSharer";
 	String SHAREABLE_ID = "shareableId";
 	String INVITATION_MSG = "invitationMsg";
+	String INVITATION_ID = "invitationId";
+	String RESPONSE_ID = "responseId";
 	int SHARE_MSG_TYPE_INVITATION = 1;
 	int SHARE_MSG_TYPE_ACCEPT = 2;
 	int SHARE_MSG_TYPE_DECLINE = 3;
diff --git a/briar-api/src/org/briarproject/api/sharing/SharingMessage.java b/briar-api/src/org/briarproject/api/sharing/SharingMessage.java
index be0cac328d..3078d86b4e 100644
--- a/briar-api/src/org/briarproject/api/sharing/SharingMessage.java
+++ b/briar-api/src/org/briarproject/api/sharing/SharingMessage.java
@@ -14,6 +14,7 @@ import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEP
 import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
 import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
 import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
+import static org.briarproject.api.sharing.SharingConstants.TIME;
 import static org.briarproject.api.sharing.SharingConstants.TYPE;
 
 public interface SharingMessage {
@@ -21,10 +22,12 @@ public interface SharingMessage {
 	abstract class BaseMessage {
 		private final GroupId groupId;
 		private final SessionId sessionId;
+		private final long time;
 
-		BaseMessage(GroupId groupId, SessionId sessionId) {
+		BaseMessage(GroupId groupId, SessionId sessionId, long time) {
 			this.groupId = groupId;
 			this.sessionId = sessionId;
+			this.time = time;
 		}
 
 		public BdfList toBdfList() {
@@ -62,16 +65,20 @@ public interface SharingMessage {
 		public SessionId getSessionId() {
 			return sessionId;
 		}
+
+		public long getTime() {
+			return time;
+		}
 	}
 
 	abstract class Invitation extends BaseMessage {
 
 		protected final String message;
 
-		public Invitation(GroupId groupId, SessionId sessionId,
+		public Invitation(GroupId groupId, SessionId sessionId, long time,
 				String message) {
 
-			super(groupId, sessionId);
+			super(groupId, sessionId, time);
 
 			this.message = message;
 		}
@@ -90,8 +97,9 @@ public interface SharingMessage {
 
 		private final long type;
 
-		public SimpleMessage(long type, GroupId groupId, SessionId sessionId) {
-			super(groupId, sessionId);
+		public SimpleMessage(long type, GroupId groupId, SessionId sessionId,
+				long time) {
+			super(groupId, sessionId, time);
 			this.type = type;
 		}
 
@@ -114,7 +122,8 @@ public interface SharingMessage {
 					type != SHARE_MSG_TYPE_ABORT) throw new FormatException();
 
 			SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
-			return new SimpleMessage(type, groupId, sessionId);
+			long time = d.getLong(TIME);
+			return new SimpleMessage(type, groupId, sessionId, time);
 		}
 	}
 
diff --git a/briar-core/src/org/briarproject/sharing/BlogInviteeSessionState.java b/briar-core/src/org/briarproject/sharing/BlogInviteeSessionState.java
index 7bb3295b7b..2ffc4d8d1f 100644
--- a/briar-core/src/org/briarproject/sharing/BlogInviteeSessionState.java
+++ b/briar-core/src/org/briarproject/sharing/BlogInviteeSessionState.java
@@ -5,6 +5,7 @@ import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
 
 import static org.briarproject.api.blogs.BlogConstants.BLOG_AUTHOR_NAME;
 import static org.briarproject.api.blogs.BlogConstants.BLOG_DESC;
@@ -21,8 +22,9 @@ public class BlogInviteeSessionState extends InviteeSessionState {
 	public BlogInviteeSessionState(SessionId sessionId, MessageId storageId,
 			GroupId groupId, State state, ContactId contactId, GroupId blogId,
 			String blogTitle, String blogDesc, String blogAuthorName,
-			byte[] blogPublicKey) {
-		super(sessionId, storageId, groupId, state, contactId, blogId);
+			byte[] blogPublicKey, @Nullable MessageId invitationId) {
+		super(sessionId, storageId, groupId, state, contactId, blogId,
+				invitationId);
 
 		this.blogTitle = blogTitle;
 		this.blogDesc = blogDesc;
diff --git a/briar-core/src/org/briarproject/sharing/BlogSharerSessionState.java b/briar-core/src/org/briarproject/sharing/BlogSharerSessionState.java
index 16ef1355b2..5dd365cae7 100644
--- a/briar-core/src/org/briarproject/sharing/BlogSharerSessionState.java
+++ b/briar-core/src/org/briarproject/sharing/BlogSharerSessionState.java
@@ -5,6 +5,7 @@ import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
 
 import static org.briarproject.api.blogs.BlogConstants.BLOG_AUTHOR_NAME;
 import static org.briarproject.api.blogs.BlogConstants.BLOG_DESC;
@@ -21,8 +22,9 @@ public class BlogSharerSessionState extends SharerSessionState {
 	public BlogSharerSessionState(SessionId sessionId, MessageId storageId,
 			GroupId groupId, State state, ContactId contactId, GroupId blogId,
 			String blogTitle, String blogDesc, String blogAuthorName,
-			byte[] blogPublicKey) {
-		super(sessionId, storageId, groupId, state, contactId, blogId);
+			byte[] blogPublicKey, @Nullable MessageId responseId) {
+		super(sessionId, storageId, groupId, state, contactId, blogId,
+				responseId);
 
 		this.blogTitle = blogTitle;
 		this.blogDesc = blogDesc;
diff --git a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java
index 32314ec730..a596bcb2c3 100644
--- a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java
+++ b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java
@@ -10,8 +10,8 @@ import org.briarproject.api.blogs.BlogManager.RemoveBlogHook;
 import org.briarproject.api.blogs.BlogSharingManager;
 import org.briarproject.api.blogs.BlogSharingMessage.BlogInvitation;
 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.SessionId;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
@@ -43,6 +43,7 @@ import static org.briarproject.api.blogs.BlogConstants.BLOG_AUTHOR_NAME;
 import static org.briarproject.api.blogs.BlogConstants.BLOG_DESC;
 import static org.briarproject.api.blogs.BlogConstants.BLOG_PUBLIC_KEY;
 import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE;
+import static org.briarproject.api.sharing.SharingConstants.INVITATION_ID;
 
 class BlogSharingManagerImpl extends
 		SharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent>
@@ -163,7 +164,7 @@ class BlogSharingManagerImpl extends
 		private final BlogFactory blogFactory;
 		private final BlogManager blogManager;
 
-		SFactory(AuthorFactory authorFactory, BlogFactory BlogFactory,
+		private SFactory(AuthorFactory authorFactory, BlogFactory BlogFactory,
 				BlogManager BlogManager) {
 			this.authorFactory = authorFactory;
 			this.blogFactory = BlogFactory;
@@ -230,11 +231,13 @@ class BlogSharingManagerImpl extends
 		}
 
 		@Override
-		public BlogInvitation build(BlogSharerSessionState localState) {
+		public BlogInvitation build(BlogSharerSessionState localState,
+				long time) {
 			return new BlogInvitation(localState.getGroupId(),
 					localState.getSessionId(), localState.getBlogTitle(),
 					localState.getBlogDesc(), localState.getBlogAuthorName(),
-					localState.getBlogPublicKey(), localState.getMessage());
+					localState.getBlogPublicKey(), time,
+					localState.getMessage());
 		}
 	}
 
@@ -249,20 +252,24 @@ class BlogSharingManagerImpl extends
 			String blogDesc = d.getString(BLOG_DESC);
 			String blogAuthorName = d.getString(BLOG_AUTHOR_NAME);
 			byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY);
+			MessageId invitationId = null;
+			byte[] invitationIdBytes = d.getOptionalRaw(INVITATION_ID);
+			if (invitationIdBytes != null)
+				invitationId = new MessageId(invitationIdBytes);
 			return new BlogInviteeSessionState(sessionId, storageId,
 					groupId, state, contactId, blogId, blogTitle, blogDesc,
-					blogAuthorName, blogPublicKey);
+					blogAuthorName, blogPublicKey, invitationId);
 		}
 
 		@Override
 		public BlogInviteeSessionState build(SessionId sessionId,
 				MessageId storageId, GroupId groupId,
 				InviteeSessionState.State state, ContactId contactId,
-				Blog blog) {
+				Blog blog, MessageId invitationId) {
 			return new BlogInviteeSessionState(sessionId, storageId,
 					groupId, state, contactId, blog.getId(), blog.getName(),
 					blog.getDescription(), blog.getAuthor().getName(),
-					blog.getAuthor().getPublicKey());
+					blog.getAuthor().getPublicKey(), invitationId);
 		}
 	}
 
@@ -277,9 +284,13 @@ class BlogSharingManagerImpl extends
 			String blogDesc = d.getString(BLOG_DESC);
 			String blogAuthorName = d.getString(BLOG_AUTHOR_NAME);
 			byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY);
+			MessageId responseId = null;
+			byte[] responseIdBytes = d.getOptionalRaw(INVITATION_ID);
+			if (responseIdBytes != null)
+				responseId = new MessageId(responseIdBytes);
 			return new BlogSharerSessionState(sessionId, storageId,
 					groupId, state, contactId, blogId, blogTitle, blogDesc,
-					blogAuthorName, blogPublicKey);
+					blogAuthorName, blogPublicKey, responseId);
 		}
 
 		@Override
@@ -290,7 +301,7 @@ class BlogSharingManagerImpl extends
 			return new BlogSharerSessionState(sessionId, storageId,
 					groupId, state, contactId, blog.getId(), blog.getName(),
 					blog.getDescription(), blog.getAuthor().getName(),
-					blog.getAuthor().getPublicKey());
+					blog.getAuthor().getPublicKey(), null);
 		}
 	}
 
@@ -299,16 +310,21 @@ class BlogSharingManagerImpl extends
 
 		private final SFactory sFactory;
 
-		IRFactory(SFactory sFactory) {
+		private IRFactory(SFactory sFactory) {
 			this.sFactory = sFactory;
 		}
 
 		@Override
 		public BlogInvitationReceivedEvent build(
-				BlogInviteeSessionState localState) {
+				BlogInviteeSessionState localState, long time, String msg) {
 			Blog blog = sFactory.parse(localState);
 			ContactId contactId = localState.getContactId();
-			return new BlogInvitationReceivedEvent(blog, contactId);
+			BlogInvitationRequest request =
+					new BlogInvitationRequest(localState.getInvitationId(),
+							localState.getSessionId(), contactId,
+							blog.getAuthor().getName(), msg, true, time, false,
+							false, false, false);
+			return new BlogInvitationReceivedEvent(blog, contactId, request);
 		}
 	}
 
@@ -316,10 +332,15 @@ class BlogSharingManagerImpl extends
 			InvitationResponseReceivedEventFactory<BlogSharerSessionState, BlogInvitationResponseReceivedEvent> {
 		@Override
 		public BlogInvitationResponseReceivedEvent build(
-				BlogSharerSessionState localState) {
+				BlogSharerSessionState localState, boolean accept, long time) {
 			String title = localState.getBlogTitle();
 			ContactId c = localState.getContactId();
-			return new BlogInvitationResponseReceivedEvent(title, c);
+			BlogInvitationResponse response =
+					new BlogInvitationResponse(localState.getResponseId(),
+							localState.getSessionId(),
+							localState.getContactId(), accept, time, false,
+							false, false, false);
+			return new BlogInvitationResponseReceivedEvent(title, c, response);
 		}
 	}
 }
diff --git a/briar-core/src/org/briarproject/sharing/ForumInviteeSessionState.java b/briar-core/src/org/briarproject/sharing/ForumInviteeSessionState.java
index f8ac1629b1..259b38b00b 100644
--- a/briar-core/src/org/briarproject/sharing/ForumInviteeSessionState.java
+++ b/briar-core/src/org/briarproject/sharing/ForumInviteeSessionState.java
@@ -5,6 +5,7 @@ import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
 
 import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
 import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
@@ -16,8 +17,10 @@ public class ForumInviteeSessionState extends InviteeSessionState {
 
 	public ForumInviteeSessionState(SessionId sessionId, MessageId storageId,
 			GroupId groupId, State state, ContactId contactId, GroupId forumId,
-			String forumName, byte[] forumSalt) {
-		super(sessionId, storageId, groupId, state, contactId, forumId);
+			String forumName, byte[] forumSalt,
+			@Nullable MessageId invitationId) {
+		super(sessionId, storageId, groupId, state, contactId, forumId,
+				invitationId);
 
 		this.forumName = forumName;
 		this.forumSalt = forumSalt;
diff --git a/briar-core/src/org/briarproject/sharing/ForumSharerSessionState.java b/briar-core/src/org/briarproject/sharing/ForumSharerSessionState.java
index 4e0fd05049..1403b75a67 100644
--- a/briar-core/src/org/briarproject/sharing/ForumSharerSessionState.java
+++ b/briar-core/src/org/briarproject/sharing/ForumSharerSessionState.java
@@ -5,6 +5,7 @@ import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
 
 import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
 import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
@@ -16,8 +17,10 @@ class ForumSharerSessionState extends SharerSessionState {
 
 	ForumSharerSessionState(SessionId sessionId, MessageId storageId,
 			GroupId groupId, State state, ContactId contactId, GroupId forumId,
-			String forumName, byte[] forumSalt) {
-		super(sessionId, storageId, groupId, state, contactId, forumId);
+			String forumName, byte[] forumSalt,
+			@Nullable MessageId responseId) {
+		super(sessionId, storageId, groupId, state, contactId, forumId,
+				responseId);
 
 		this.forumName = forumName;
 		this.forumSalt = forumSalt;
diff --git a/briar-core/src/org/briarproject/sharing/ForumSharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/ForumSharingManagerImpl.java
index 89bf145cc7..d0d4a9e8a3 100644
--- a/briar-core/src/org/briarproject/sharing/ForumSharingManagerImpl.java
+++ b/briar-core/src/org/briarproject/sharing/ForumSharingManagerImpl.java
@@ -2,8 +2,8 @@ package org.briarproject.sharing;
 
 import org.briarproject.api.FormatException;
 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.SessionId;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.data.BdfDictionary;
@@ -35,6 +35,7 @@ import javax.inject.Inject;
 
 import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
 import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
+import static org.briarproject.api.sharing.SharingConstants.INVITATION_ID;
 
 class ForumSharingManagerImpl extends
 		SharingManagerImpl<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationReceivedEvent, ForumInvitationResponseReceivedEvent>
@@ -136,7 +137,7 @@ class ForumSharingManagerImpl extends
 		private final ForumFactory forumFactory;
 		private final ForumManager forumManager;
 
-		SFactory(ForumFactory forumFactory, ForumManager forumManager) {
+		private SFactory(ForumFactory forumFactory, ForumManager forumManager) {
 			this.forumFactory = forumFactory;
 			this.forumManager = forumManager;
 		}
@@ -185,10 +186,11 @@ class ForumSharingManagerImpl extends
 		}
 
 		@Override
-		public ForumInvitation build(ForumSharerSessionState localState) {
+		public ForumInvitation build(ForumSharerSessionState localState,
+				long time) {
 			return new ForumInvitation(localState.getGroupId(),
 					localState.getSessionId(), localState.getForumName(),
-					localState.getForumSalt(), localState.getMessage());
+					localState.getForumSalt(), time, localState.getMessage());
 		}
 	}
 
@@ -201,18 +203,23 @@ class ForumSharingManagerImpl extends
 				GroupId forumId, BdfDictionary d) throws FormatException {
 			String forumName = d.getString(FORUM_NAME);
 			byte[] forumSalt = d.getRaw(FORUM_SALT);
+			MessageId invitationId = null;
+			byte[] invitationIdBytes = d.getOptionalRaw(INVITATION_ID);
+			if (invitationIdBytes != null)
+				invitationId = new MessageId(invitationIdBytes);
 			return new ForumInviteeSessionState(sessionId, storageId,
-					groupId, state, contactId, forumId, forumName, forumSalt);
+					groupId, state, contactId, forumId, forumName, forumSalt,
+					invitationId);
 		}
 
 		@Override
 		public ForumInviteeSessionState build(SessionId sessionId,
 				MessageId storageId, GroupId groupId,
 				InviteeSessionState.State state, ContactId contactId,
-				Forum forum) {
+				Forum forum, MessageId invitationId) {
 			return new ForumInviteeSessionState(sessionId, storageId,
 					groupId, state, contactId, forum.getId(), forum.getName(),
-					forum.getSalt());
+					forum.getSalt(), invitationId);
 		}
 	}
 
@@ -225,8 +232,13 @@ class ForumSharingManagerImpl extends
 				GroupId forumId, BdfDictionary d) throws FormatException {
 			String forumName = d.getString(FORUM_NAME);
 			byte[] forumSalt = d.getRaw(FORUM_SALT);
+			MessageId responseId = null;
+			byte[] responseIdBytes = d.getOptionalRaw(INVITATION_ID);
+			if (responseIdBytes != null)
+				responseId = new MessageId(responseIdBytes);
 			return new ForumSharerSessionState(sessionId, storageId,
-					groupId, state, contactId, forumId, forumName, forumSalt);
+					groupId, state, contactId, forumId, forumName, forumSalt,
+					responseId);
 		}
 
 		@Override
@@ -236,7 +248,7 @@ class ForumSharingManagerImpl extends
 				Forum forum) {
 			return new ForumSharerSessionState(sessionId, storageId,
 					groupId, state, contactId, forum.getId(), forum.getName(),
-					forum.getSalt());
+					forum.getSalt(), null);
 		}
 	}
 
@@ -245,16 +257,20 @@ class ForumSharingManagerImpl extends
 
 		private final SFactory sFactory;
 
-		IRFactory(SFactory sFactory) {
+		private IRFactory(SFactory sFactory) {
 			this.sFactory = sFactory;
 		}
 
 		@Override
 		public ForumInvitationReceivedEvent build(
-				ForumInviteeSessionState localState) {
+				ForumInviteeSessionState localState, long time, String msg) {
 			Forum forum = sFactory.parse(localState);
 			ContactId contactId = localState.getContactId();
-			return new ForumInvitationReceivedEvent(forum, contactId);
+			ForumInvitationRequest request = new ForumInvitationRequest(
+					localState.getInvitationId(), localState.getSessionId(),
+					contactId, forum.getName(), msg, true, time, false, false,
+					false, false);
+			return new ForumInvitationReceivedEvent(forum, contactId, request);
 		}
 	}
 
@@ -262,10 +278,14 @@ class ForumSharingManagerImpl extends
 			InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> {
 		@Override
 		public ForumInvitationResponseReceivedEvent build(
-				ForumSharerSessionState localState) {
+				ForumSharerSessionState localState, boolean accept, long time) {
 			String name = localState.getForumName();
 			ContactId c = localState.getContactId();
-			return new ForumInvitationResponseReceivedEvent(name, c);
+			ForumInvitationResponse response = new ForumInvitationResponse(
+					localState.getResponseId(),
+					localState.getSessionId(), localState.getContactId(),
+					accept, time, false, false, false, false);
+			return new ForumInvitationResponseReceivedEvent(name, c, response);
 		}
 	}
 }
diff --git a/briar-core/src/org/briarproject/sharing/InvitationFactory.java b/briar-core/src/org/briarproject/sharing/InvitationFactory.java
index be82501e8f..2c2d32c293 100644
--- a/briar-core/src/org/briarproject/sharing/InvitationFactory.java
+++ b/briar-core/src/org/briarproject/sharing/InvitationFactory.java
@@ -5,5 +5,5 @@ import org.briarproject.api.sharing.SharingMessage;
 public interface InvitationFactory<I extends SharingMessage.Invitation, SS extends SharerSessionState> extends
 		org.briarproject.api.sharing.InvitationFactory<I> {
 
-	I build(SS localState);
+	I build(SS localState, long time);
 }
diff --git a/briar-core/src/org/briarproject/sharing/InvitationReceivedEventFactory.java b/briar-core/src/org/briarproject/sharing/InvitationReceivedEventFactory.java
index bdedeee488..7c1e324d0a 100644
--- a/briar-core/src/org/briarproject/sharing/InvitationReceivedEventFactory.java
+++ b/briar-core/src/org/briarproject/sharing/InvitationReceivedEventFactory.java
@@ -4,5 +4,5 @@ import org.briarproject.api.event.InvitationReceivedEvent;
 
 public interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationReceivedEvent> {
 
-	IR build(IS localState);
+	IR build(IS localState, long time, String msg);
 }
diff --git a/briar-core/src/org/briarproject/sharing/InvitationResponseReceivedEventFactory.java b/briar-core/src/org/briarproject/sharing/InvitationResponseReceivedEventFactory.java
index 4420f30edd..ea5cdd91ab 100644
--- a/briar-core/src/org/briarproject/sharing/InvitationResponseReceivedEventFactory.java
+++ b/briar-core/src/org/briarproject/sharing/InvitationResponseReceivedEventFactory.java
@@ -4,5 +4,5 @@ import org.briarproject.api.event.InvitationResponseReceivedEvent;
 
 public interface InvitationResponseReceivedEventFactory<SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> {
 
-	IRR build(SS localState);
+	IRR build(SS localState, boolean accept, long time);
 }
diff --git a/briar-core/src/org/briarproject/sharing/InviteeEngine.java b/briar-core/src/org/briarproject/sharing/InviteeEngine.java
index 766a37e414..f845833168 100644
--- a/briar-core/src/org/briarproject/sharing/InviteeEngine.java
+++ b/briar-core/src/org/briarproject/sharing/InviteeEngine.java
@@ -4,6 +4,8 @@ import org.briarproject.api.FormatException;
 import org.briarproject.api.clients.ProtocolEngine;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.InvitationReceivedEvent;
+import org.briarproject.api.sharing.SharingMessage.Invitation;
+import org.briarproject.api.system.Clock;
 
 import java.util.Collections;
 import java.util.List;
@@ -30,9 +32,13 @@ class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceive
 			Logger.getLogger(InviteeEngine.class.getName());
 
 	private final InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory;
+	private final Clock clock;
 
-	InviteeEngine(InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory) {
+	InviteeEngine(
+			InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory,
+			Clock clock) {
 		this.invitationReceivedEventFactory = invitationReceivedEventFactory;
+		this.clock = clock;
 	}
 
 	@Override
@@ -63,19 +69,22 @@ class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceive
 				if (action == InviteeSessionState.Action.LOCAL_ACCEPT) {
 					localState.setTask(TASK_ADD_SHARED_SHAREABLE);
 					msg = new SimpleMessage(SHARE_MSG_TYPE_ACCEPT,
-							localState.getGroupId(), localState.getSessionId());
+							localState.getGroupId(), localState.getSessionId(),
+							clock.currentTimeMillis());
 				} else {
 					localState.setTask(
 							TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US);
 					msg = new SimpleMessage(SHARE_MSG_TYPE_DECLINE,
-							localState.getGroupId(), localState.getSessionId());
+							localState.getGroupId(), localState.getSessionId(),
+							clock.currentTimeMillis());
 				}
 				messages = Collections.singletonList(msg);
 				logLocalAction(currentState, localState, msg);
 			}
 			else if (action == InviteeSessionState.Action.LOCAL_LEAVE) {
 				BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
-						localState.getGroupId(), localState.getSessionId());
+						localState.getGroupId(), localState.getSessionId(),
+						clock.currentTimeMillis());
 				messages = Collections.singletonList(msg);
 				logLocalAction(currentState, localState, msg);
 			}
@@ -133,7 +142,9 @@ class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceive
 			// we have just received our invitation
 			else if (action == InviteeSessionState.Action.REMOTE_INVITATION) {
 				localState.setTask(TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US);
-				Event event = invitationReceivedEventFactory.build(localState);
+				Invitation invitation = (Invitation) msg;
+				Event event = invitationReceivedEventFactory.build(localState,
+						msg.getTime(), invitation.getMessage());
 				events = Collections.singletonList(event);
 			}
 			else {
@@ -203,7 +214,7 @@ class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceive
 		localState.setState(InviteeSessionState.State.ERROR);
 		BaseMessage msg =
 				new SimpleMessage(SHARE_MSG_TYPE_ABORT, localState.getGroupId(),
-						localState.getSessionId());
+						localState.getSessionId(), clock.currentTimeMillis());
 		List<BaseMessage> messages = Collections.singletonList(msg);
 
 		List<Event> events = Collections.emptyList();
diff --git a/briar-core/src/org/briarproject/sharing/InviteeSessionState.java b/briar-core/src/org/briarproject/sharing/InviteeSessionState.java
index 6082c7ba8d..295076fd41 100644
--- a/briar-core/src/org/briarproject/sharing/InviteeSessionState.java
+++ b/briar-core/src/org/briarproject/sharing/InviteeSessionState.java
@@ -5,7 +5,9 @@ import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
 
+import static org.briarproject.api.sharing.SharingConstants.INVITATION_ID;
 import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
 import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
 import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
@@ -21,19 +23,22 @@ import static org.briarproject.sharing.InviteeSessionState.Action.REMOTE_LEAVE;
 public abstract class InviteeSessionState extends SharingSessionState {
 
 	private State state;
+	private final MessageId invitationId;
 
 	public InviteeSessionState(SessionId sessionId, MessageId storageId,
 			GroupId groupId, State state, ContactId contactId,
-			GroupId shareableId) {
+			GroupId shareableId, MessageId invitationId) {
 
 		super(sessionId, storageId, groupId, contactId, shareableId);
 		this.state = state;
+		this.invitationId = invitationId;
 	}
 
 	public BdfDictionary toBdfDictionary() {
 		BdfDictionary d = super.toBdfDictionary();
 		d.put(STATE, getState().getValue());
 		d.put(IS_SHARER, false);
+		if (invitationId != null) d.put(INVITATION_ID, invitationId);
 		return d;
 	}
 
@@ -45,6 +50,11 @@ public abstract class InviteeSessionState extends SharingSessionState {
 		return state;
 	}
 
+	@Nullable
+	public MessageId getInvitationId() {
+		return invitationId;
+	}
+
 	public enum State {
 		ERROR(0),
 		AWAIT_INVITATION(1) {
diff --git a/briar-core/src/org/briarproject/sharing/InviteeSessionStateFactory.java b/briar-core/src/org/briarproject/sharing/InviteeSessionStateFactory.java
index 57b8adcfaa..0a514f5192 100644
--- a/briar-core/src/org/briarproject/sharing/InviteeSessionStateFactory.java
+++ b/briar-core/src/org/briarproject/sharing/InviteeSessionStateFactory.java
@@ -15,5 +15,6 @@ public interface InviteeSessionStateFactory<S extends Shareable, IS extends Invi
 			GroupId shareableId, BdfDictionary d) throws FormatException;
 
 	IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
-			InviteeSessionState.State state, ContactId contactId, S shareable);
+			InviteeSessionState.State state, ContactId contactId, S shareable,
+			MessageId invitationId);
 }
diff --git a/briar-core/src/org/briarproject/sharing/SharerEngine.java b/briar-core/src/org/briarproject/sharing/SharerEngine.java
index 4f7d23f83b..3ddb177606 100644
--- a/briar-core/src/org/briarproject/sharing/SharerEngine.java
+++ b/briar-core/src/org/briarproject/sharing/SharerEngine.java
@@ -4,6 +4,7 @@ import org.briarproject.api.FormatException;
 import org.briarproject.api.clients.ProtocolEngine;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.InvitationResponseReceivedEvent;
+import org.briarproject.api.system.Clock;
 
 import java.util.Collections;
 import java.util.List;
@@ -22,6 +23,8 @@ import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREAB
 import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
 import static org.briarproject.api.sharing.SharingMessage.Invitation;
 import static org.briarproject.api.sharing.SharingMessage.SimpleMessage;
+import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_ACCEPT;
+import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_DECLINE;
 
 class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent>
 		implements ProtocolEngine<SharerSessionState.Action, SS, BaseMessage> {
@@ -32,12 +35,15 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
 	private final InvitationFactory<I, SS> invitationFactory;
 	private final InvitationResponseReceivedEventFactory<SS, IRR>
 			invitationResponseReceivedEventFactory;
+	private final Clock clock;
 
 	SharerEngine(InvitationFactory<I, SS> invitationFactory,
-			InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory) {
+			InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory,
+			Clock clock) {
 		this.invitationFactory = invitationFactory;
 		this.invitationResponseReceivedEventFactory =
 				invitationResponseReceivedEventFactory;
+		this.clock = clock;
 	}
 
 	@Override
@@ -65,7 +71,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
 			List<Event> events = Collections.emptyList();
 
 			if (action == SharerSessionState.Action.LOCAL_INVITATION) {
-				BaseMessage msg = invitationFactory.build(localState);
+				BaseMessage msg = invitationFactory.build(localState,
+						clock.currentTimeMillis());
 				messages = Collections.singletonList(msg);
 				logLocalAction(currentState, nextState, msg);
 
@@ -74,7 +81,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
 						.setTask(TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US);
 			} else if (action == SharerSessionState.Action.LOCAL_LEAVE) {
 				BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
-						localState.getGroupId(), localState.getSessionId());
+						localState.getGroupId(), localState.getSessionId(),
+						clock.currentTimeMillis());
 				messages = Collections.singletonList(msg);
 				logLocalAction(currentState, nextState, msg);
 			} else {
@@ -122,9 +130,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
 				deleteMsg = true;
 			}
 			// we have sent our invitation and just got a response
-			else if (action == SharerSessionState.Action.REMOTE_ACCEPT ||
-					action == SharerSessionState.Action.REMOTE_DECLINE) {
-				if (action == SharerSessionState.Action.REMOTE_ACCEPT) {
+			else if (action == REMOTE_ACCEPT || action == REMOTE_DECLINE) {
+				if (action == REMOTE_ACCEPT) {
 					localState.setTask(TASK_SHARE_SHAREABLE);
 				} else {
 					// this ensures that the forum can be shared again
@@ -132,7 +139,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
 							TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US);
 				}
 				Event event = invitationResponseReceivedEventFactory
-						.build(localState);
+						.build(localState, action == REMOTE_ACCEPT,
+								msg.getTime());
 				events = Collections.singletonList(event);
 			} else {
 				throw new IllegalArgumentException("Bad state");
@@ -204,7 +212,8 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
 
 		localState.setState(SharerSessionState.State.ERROR);
 		BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_ABORT,
-				localState.getGroupId(), localState.getSessionId());
+				localState.getGroupId(), localState.getSessionId(),
+				clock.currentTimeMillis());
 		List<BaseMessage> messages = Collections.singletonList(msg);
 
 		List<Event> events = Collections.emptyList();
diff --git a/briar-core/src/org/briarproject/sharing/SharerSessionState.java b/briar-core/src/org/briarproject/sharing/SharerSessionState.java
index 6cbb271561..a56be90678 100644
--- a/briar-core/src/org/briarproject/sharing/SharerSessionState.java
+++ b/briar-core/src/org/briarproject/sharing/SharerSessionState.java
@@ -5,8 +5,10 @@ import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
 
 import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
+import static org.briarproject.api.sharing.SharingConstants.RESPONSE_ID;
 import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
 import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
 import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
@@ -22,20 +24,25 @@ import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_LEAVE;
 public abstract class SharerSessionState extends SharingSessionState {
 
 	private State state;
+	@Nullable
 	private String msg = null;
+	@Nullable
+	private MessageId responseId;
 
 	public SharerSessionState(SessionId sessionId, MessageId storageId,
 			GroupId groupId, State state, ContactId contactId,
-			GroupId shareableId) {
+			GroupId shareableId, @Nullable MessageId responseId) {
 
 		super(sessionId, storageId, groupId, contactId, shareableId);
 		this.state = state;
+		this.responseId = responseId;
 	}
 
 	public BdfDictionary toBdfDictionary() {
 		BdfDictionary d = super.toBdfDictionary();
 		d.put(STATE, getState().getValue());
 		d.put(IS_SHARER, true);
+		if (responseId != null) d.put(RESPONSE_ID, responseId);
 		return d;
 	}
 
@@ -55,6 +62,15 @@ public abstract class SharerSessionState extends SharingSessionState {
 		return this.msg;
 	}
 
+	public void setResponseId(@Nullable MessageId responseId) {
+		this.responseId = responseId;
+	}
+
+	@Nullable
+	public MessageId getResponseId() {
+		return responseId;
+	}
+
 	public enum State {
 		ERROR(0),
 		PREPARE_INVITATION(1) {
diff --git a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java
index 805b9d0b4b..f0a88ff5fa 100644
--- a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java
+++ b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java
@@ -211,6 +211,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 				if (stateExists) throw new FormatException();
 
 				// check if shareable can be shared
+				@SuppressWarnings("unchecked")
 				I invitation = (I) msg;
 				S f = getSFactory().parse(invitation);
 				ContactId contactId = getContactId(txn, m.getGroupId());
@@ -219,9 +220,10 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 					checkForRaceCondition(txn, f, contact);
 
 				// initialize state and process invitation
-				IS state = initializeInviteeState(txn, contactId, invitation);
+				IS state = initializeInviteeState(txn, contactId, invitation,
+						m.getId());
 				InviteeEngine<IS, IR> engine =
-						new InviteeEngine<IS, IR>(getIRFactory());
+						new InviteeEngine<IS, IR>(getIRFactory(), clock);
 				processInviteeStateUpdate(txn, m.getId(),
 						engine.onMessageReceived(state, msg));
 				trackIncomingMessage(txn, m);
@@ -233,9 +235,10 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 				msg.getType() == SHARE_MSG_TYPE_DECLINE) {
 			// we are a sharer who just received a response
 			SS state = getSessionStateForSharer(txn, sessionId);
+			state.setResponseId(m.getId());
 			SharerEngine<I, SS, IRR> engine =
 					new SharerEngine<I, SS, IRR>(getIFactory(),
-							getIRRFactory());
+							getIRRFactory(), clock);
 			processSharerStateUpdate(txn, m.getId(),
 					engine.onMessageReceived(state, msg));
 			trackIncomingMessage(txn, m);
@@ -245,17 +248,19 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 			SharingSessionState s = getSessionState(txn, sessionId, true);
 			if (s instanceof SharerSessionState) {
 				// we are a sharer and the invitee wants to leave or abort
+				@SuppressWarnings("unchecked")
 				SS state = (SS) s;
 				SharerEngine<I, SS, IRR> engine =
 						new SharerEngine<I, SS, IRR>(getIFactory(),
-								getIRRFactory());
+								getIRRFactory(), clock);
 				processSharerStateUpdate(txn, m.getId(),
 						engine.onMessageReceived(state, msg));
 			} else {
 				// we are an invitee and the sharer wants to leave or abort
+				@SuppressWarnings("unchecked")
 				IS state = (IS) s;
 				InviteeEngine<IS, IR> engine =
-						new InviteeEngine<IS, IR>(getIRFactory());
+						new InviteeEngine<IS, IR>(getIRFactory(), clock);
 				processInviteeStateUpdate(txn, m.getId(),
 						engine.onMessageReceived(state, msg));
 			}
@@ -285,13 +290,14 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 			// start engine and process its state update
 			SharerEngine<I, SS, IRR> engine =
 					new SharerEngine<I, SS, IRR>(getIFactory(),
-							getIRRFactory());
-			processSharerStateUpdate(txn, null,
+							getIRRFactory(), clock);
+			StateUpdate<SS, BaseMessage> update =
 					engine.onLocalAction(localState,
-							SharerSessionState.Action.LOCAL_INVITATION));
+							SharerSessionState.Action.LOCAL_INVITATION);
+			processSharerStateUpdate(txn, null, update);
 
 			// track message
-			long time = clock.currentTimeMillis();
+			long time = update.toSend.get(0).getTime();
 			trackMessage(txn, localState.getGroupId(), time, true);
 
 			txn.setComplete();
@@ -321,12 +327,13 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 
 			// start engine and process its state update
 			InviteeEngine<IS, IR> engine =
-					new InviteeEngine<IS, IR>(getIRFactory());
-			processInviteeStateUpdate(txn, null,
-					engine.onLocalAction(localState, localAction));
+					new InviteeEngine<IS, IR>(getIRFactory(), clock);
+			StateUpdate<IS, BaseMessage> update =
+					engine.onLocalAction(localState, localAction);
+			processInviteeStateUpdate(txn, null, update);
 
 			// track message
-			long time = clock.currentTimeMillis();
+			long time = update.toSend.get(0).getTime();
 			trackMessage(txn, localState.getGroupId(), time, true);
 
 			txn.setComplete();
@@ -466,6 +473,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 			BdfDictionary d = m.getValue();
 			try {
 				I msg = getIFactory().build(group.getId(), d);
+				@SuppressWarnings("unchecked")
 				IS iss = (IS) getSessionState(txn, msg.getSessionId(), true);
 				// get and add the shareable if the invitation is unanswered
 				if (iss.getState().equals(AWAIT_LOCAL_RESPONSE)) {
@@ -642,7 +650,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 	}
 
 	private IS initializeInviteeState(Transaction txn,
-			ContactId contactId, I msg)
+			ContactId contactId, I msg, MessageId id)
 			throws FormatException, DbException {
 
 		Contact c = db.getContact(txn, contactId);
@@ -656,9 +664,10 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 		Message m = clientHelper.createMessage(localGroup.getId(), now,
 				BdfList.of(mSalt));
 
-		IS s = getISFactory().build(msg.getSessionId(),
-				m.getId(), group.getId(),
-				InviteeSessionState.State.AWAIT_INVITATION, contactId, f);
+		IS s = getISFactory()
+				.build(msg.getSessionId(), m.getId(), group.getId(),
+						InviteeSessionState.State.AWAIT_INVITATION, contactId,
+						f, id);
 
 		// save local state to database
 		BdfDictionary d = s.toBdfDictionary();
@@ -712,6 +721,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 
 		if (!d.getBoolean(IS_SHARER)) throw new FormatException();
 
+		//noinspection unchecked
 		return (SS) SharingSessionState
 				.fromBdfDictionary(getISFactory(), getSSFactory(), d);
 	}
@@ -746,6 +756,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 			}
 			throw new DbException();
 		}
+		//noinspection unchecked
 		return (IS) SharingSessionState
 				.fromBdfDictionary(getISFactory(), getSSFactory(),
 						map.values().iterator().next());
@@ -898,16 +909,15 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 
 		byte[] body = clientHelper.toByteArray(m.toBdfList());
 		Group group = db.getGroup(txn, m.getGroupId());
-		long timestamp = clock.currentTimeMillis();
 
 		// add message itself as metadata
 		BdfDictionary d = m.toBdfDictionary();
 		d.put(LOCAL, true);
-		d.put(TIME, timestamp);
+		d.put(TIME, m.getTime());
 		Metadata meta = metadataEncoder.encode(d);
 
 		messageQueueManager
-				.sendMessage(txn, group, timestamp, body, meta);
+				.sendMessage(txn, group, m.getTime(), body, meta);
 	}
 
 	private Group getContactGroup(Contact c) {
@@ -930,14 +940,16 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 					SharerSessionState.Action.LOCAL_LEAVE;
 			SharerEngine<I, SS, IRR> engine =
 					new SharerEngine<I, SS, IRR>(getIFactory(),
-							getIRRFactory());
+							getIRRFactory(), clock);
+			//noinspection unchecked
 			processSharerStateUpdate(txn, null,
 					engine.onLocalAction((SS) state, action));
 		} else {
 			InviteeSessionState.Action action =
 					InviteeSessionState.Action.LOCAL_LEAVE;
 			InviteeEngine<IS, IR> engine =
-					new InviteeEngine<IS, IR>(getIRFactory());
+					new InviteeEngine<IS, IR>(getIRFactory(), clock);
+			//noinspection unchecked
 			processInviteeStateUpdate(txn, null,
 					engine.onLocalAction((IS) state, action));
 		}
-- 
GitLab