Merge branch '475-new-sharing-client' into 'master'

New Forum Sharing Client

This is very similar to how the private group invitations work and I am sure there's still some tiny bugs that I didn't catch.

All existing integration tests either pass or have been modified to pass.

Once this has been merged, the code should be usable for blog sharing as well.

Closes #475

See merge request !467
This commit is contained in:
Torsten Grote
2017-01-03 19:30:48 +00:00
78 changed files with 3809 additions and 1937 deletions

View File

@@ -1020,13 +1020,13 @@ public class ConversationActivity extends BriarActivity
@DatabaseExecutor @DatabaseExecutor
private void respondToForumRequest(SessionId id, boolean accept) private void respondToForumRequest(SessionId id, boolean accept)
throws DbException { throws DbException {
forumSharingManager.respondToInvitation(id, accept); forumSharingManager.respondToInvitation(contactId, id, accept);
} }
@DatabaseExecutor @DatabaseExecutor
private void respondToBlogRequest(SessionId id, boolean accept) private void respondToBlogRequest(SessionId id, boolean accept)
throws DbException { throws DbException {
blogSharingManager.respondToInvitation(id, accept); blogSharingManager.respondToInvitation(contactId, id, accept);
} }
@DatabaseExecutor @DatabaseExecutor

View File

@@ -170,8 +170,7 @@ abstract class ConversationItem {
} else if (ir instanceof GroupInvitationRequest) { } else if (ir instanceof GroupInvitationRequest) {
text = ctx.getString( text = ctx.getString(
R.string.groups_invitations_invitation_sent, R.string.groups_invitations_invitation_sent,
contactName, contactName, ir.getShareable().getName());
((GroupInvitationRequest) ir).getGroupName());
} else { } else {
throw new IllegalArgumentException("Unknown InvitationRequest"); throw new IllegalArgumentException("Unknown InvitationRequest");
} }
@@ -194,8 +193,7 @@ abstract class ConversationItem {
} else if (ir instanceof GroupInvitationRequest) { } else if (ir instanceof GroupInvitationRequest) {
text = ctx.getString( text = ctx.getString(
R.string.groups_invitations_invitation_received, R.string.groups_invitations_invitation_received,
contactName, contactName, ir.getShareable().getName());
((GroupInvitationRequest) ir).getGroupName());
type = GROUP; type = GROUP;
} else { } else {
throw new IllegalArgumentException("Unknown InvitationRequest"); throw new IllegalArgumentException("Unknown InvitationRequest");
@@ -203,7 +201,7 @@ abstract class ConversationItem {
return new ConversationRequestItem(ir.getId(), return new ConversationRequestItem(ir.getId(),
ir.getGroupId(), type, ir.getSessionId(), text, ir.getGroupId(), type, ir.getSessionId(), text,
ir.getMessage(), ir.getTimestamp(), ir.isRead(), ir.getMessage(), ir.getTimestamp(), ir.isRead(),
ir.getInvitedGroupId(), !ir.isAvailable(), ir.getShareable().getId(), !ir.isAvailable(),
ir.canBeOpened()); ir.canBeOpened());
} }
} }

View File

@@ -10,9 +10,11 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl; import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
import org.briarproject.briar.android.controller.handler.ExceptionHandler; import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.messaging.ConversationManager;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -22,6 +24,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -31,14 +34,19 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
private final static Logger LOG = private final static Logger LOG =
Logger.getLogger(ShareBlogControllerImpl.class.getName()); Logger.getLogger(ShareBlogControllerImpl.class.getName());
private final ConversationManager conversationManager;
private final BlogSharingManager blogSharingManager; private final BlogSharingManager blogSharingManager;
private final Clock clock;
@Inject @Inject
ShareBlogControllerImpl(@DatabaseExecutor Executor dbExecutor, ShareBlogControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
BlogSharingManager blogSharingManager) { ConversationManager conversationManager,
BlogSharingManager blogSharingManager, Clock clock) {
super(dbExecutor, lifecycleManager, contactManager); super(dbExecutor, lifecycleManager, contactManager);
this.conversationManager = conversationManager;
this.blogSharingManager = blogSharingManager; this.blogSharingManager = blogSharingManager;
this.clock = clock;
} }
@Override @Override
@@ -48,15 +56,19 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
@Override @Override
public void share(final GroupId g, final Collection<ContactId> contacts, public void share(final GroupId g, final Collection<ContactId> contacts,
final String msg, final String message,
final ExceptionHandler<DbException> handler) { final ExceptionHandler<DbException> handler) {
runOnDbThread(new Runnable() { runOnDbThread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
String msg = isNullOrEmpty(message) ? null : message;
for (ContactId c : contacts) { for (ContactId c : contacts) {
try { try {
blogSharingManager.sendInvitation(g, c, msg); long time = Math.max(clock.currentTimeMillis(),
conversationManager.getGroupCount(c)
.getLatestMsgTime() + 1);
blogSharingManager.sendInvitation(g, c, msg, time);
} catch (NoSuchContactException | NoSuchGroupException e) { } catch (NoSuchContactException | NoSuchGroupException e) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e); LOG.log(WARNING, e.toString(), e);

View File

@@ -10,9 +10,11 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl; import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
import org.briarproject.briar.android.controller.handler.ExceptionHandler; import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.messaging.ConversationManager;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -22,6 +24,7 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -31,14 +34,19 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
private final static Logger LOG = private final static Logger LOG =
Logger.getLogger(ShareForumControllerImpl.class.getName()); Logger.getLogger(ShareForumControllerImpl.class.getName());
private final ConversationManager conversationManager;
private final ForumSharingManager forumSharingManager; private final ForumSharingManager forumSharingManager;
private final Clock clock;
@Inject @Inject
ShareForumControllerImpl(@DatabaseExecutor Executor dbExecutor, ShareForumControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
ForumSharingManager forumSharingManager) { ConversationManager conversationManager,
ForumSharingManager forumSharingManager, Clock clock) {
super(dbExecutor, lifecycleManager, contactManager); super(dbExecutor, lifecycleManager, contactManager);
this.conversationManager = conversationManager;
this.forumSharingManager = forumSharingManager; this.forumSharingManager = forumSharingManager;
this.clock = clock;
} }
@Override @Override
@@ -48,15 +56,19 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
@Override @Override
public void share(final GroupId g, final Collection<ContactId> contacts, public void share(final GroupId g, final Collection<ContactId> contacts,
final String msg, final String message,
final ExceptionHandler<DbException> handler) { final ExceptionHandler<DbException> handler) {
runOnDbThread(new Runnable() { runOnDbThread(new Runnable() {
@Override @Override
public void run() { public void run() {
try { try {
String msg = isNullOrEmpty(message) ? null : message;
for (ContactId c : contacts) { for (ContactId c : contacts) {
try { try {
forumSharingManager.sendInvitation(g, c, msg); long time = Math.max(clock.currentTimeMillis(),
conversationManager.getGroupCount(c)
.getLatestMsgTime() + 1);
forumSharingManager.sendInvitation(g, c, msg, time);
} catch (NoSuchContactException | NoSuchGroupException e) { } catch (NoSuchContactException | NoSuchGroupException e) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e); LOG.log(WARNING, e.toString(), e);

View File

@@ -1,7 +1,10 @@
package org.briarproject.briar.api.blog; package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
@@ -10,7 +13,7 @@ import org.briarproject.briar.api.sharing.InvitationRequest;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public class BlogInvitationRequest extends InvitationRequest { public class BlogInvitationRequest extends InvitationRequest<Blog> {
private final String blogAuthorName; private final String blogAuthorName;
@@ -19,9 +22,12 @@ public class BlogInvitationRequest extends InvitationRequest {
@Nullable String message, GroupId blogId, @Nullable String message, GroupId blogId,
boolean available, boolean canBeOpened, long time, boolean available, boolean canBeOpened, long time,
boolean local, boolean sent, boolean seen, boolean read) { boolean local, boolean sent, boolean seen, boolean read) {
// TODO pass a proper blog here when redoing the BlogSharingManager
super(id, sessionId, groupId, contactId, message, blogId, available, super(id, groupId, time, local, sent, seen, read, sessionId,
canBeOpened, time, local, sent, seen, read); new Blog(new Group(blogId, BlogManager.CLIENT_ID, new byte[0]),
new Author(new AuthorId(new byte[AuthorId.LENGTH]),
blogAuthorName, new byte[0])), contactId,
message, available, canBeOpened);
this.blogAuthorName = blogAuthorName; this.blogAuthorName = blogAuthorName;
} }

View File

@@ -14,9 +14,8 @@ public class BlogInvitationResponse extends InvitationResponse {
GroupId groupId, ContactId contactId, GroupId blogId, GroupId groupId, ContactId contactId, GroupId blogId,
boolean accept, long time, boolean local, boolean sent, boolean accept, long time, boolean local, boolean sent,
boolean seen, boolean read) { boolean seen, boolean read) {
super(id, groupId, time, local, sent, seen, read, sessionId, blogId,
super(id, sessionId, groupId, contactId, blogId, accept, time, local, contactId, accept);
sent, seen, read);
} }
} }

View File

@@ -12,23 +12,18 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class ForumInvitationRequest extends InvitationRequest { public class ForumInvitationRequest extends InvitationRequest<Forum> {
private final String forumName; public ForumInvitationRequest(MessageId id, GroupId groupId, long time,
boolean local, boolean sent, boolean seen, boolean read,
public ForumInvitationRequest(MessageId id, SessionId sessionId, SessionId sessionId, Forum forum, ContactId contactId,
GroupId groupId, ContactId contactId, GroupId forumId, @Nullable String message, boolean available, boolean canBeOpened) {
String forumName, @Nullable String message, boolean available, super(id, groupId, time, local, sent, seen, read, sessionId, forum,
boolean canBeOpened, long time, boolean local, boolean sent, contactId, message, available, canBeOpened);
boolean seen, boolean read) {
super(id, sessionId, groupId, contactId, message, forumId, available,
canBeOpened, time, local, sent, seen, read);
this.forumName = forumName;
} }
public String getForumName() { public String getForumName() {
return forumName; return getShareable().getName();
} }
} }

View File

@@ -13,13 +13,12 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
public class ForumInvitationResponse extends InvitationResponse { public class ForumInvitationResponse extends InvitationResponse {
public ForumInvitationResponse(MessageId id, SessionId sessionId, public ForumInvitationResponse(MessageId id, GroupId groupId, long time,
GroupId groupId, ContactId contactId, GroupId forumId, boolean local, boolean sent, boolean seen, boolean read,
boolean accept, long time, boolean local, boolean sent, SessionId sessionId, GroupId forumId, ContactId contactId,
boolean seen, boolean read) { boolean accept) {
super(id, groupId, time, local, sent, seen, read, sessionId, forumId,
super(id, sessionId, groupId, contactId, forumId, accept, time, local, contactId, accept);
sent, seen, read);
} }
} }

View File

@@ -27,6 +27,11 @@ public interface ForumManager {
*/ */
Forum addForum(String name) throws DbException; Forum addForum(String name) throws DbException;
/**
* Subscribes to a forum within the given {@link Transaction}.
*/
void addForum(Transaction txn, Forum f) throws DbException;
/** /**
* Unsubscribes from a forum. * Unsubscribes from a forum.
*/ */

View File

@@ -1,21 +1,20 @@
package org.briarproject.briar.api.forum.event; package org.briarproject.briar.api.forum.event;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent; import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ForumInvitationResponseReceivedEvent extends public class ForumInvitationResponseReceivedEvent extends
InvitationResponseReceivedEvent { InvitationResponseReceivedEvent {
private final String forumName; public ForumInvitationResponseReceivedEvent(ContactId contactId,
ForumInvitationResponse response) {
public ForumInvitationResponseReceivedEvent(String forumName,
ContactId contactId, ForumInvitationResponse response) {
super(contactId, response); super(contactId, response);
this.forumName = forumName;
} }
public String getForumName() {
return forumName;
}
} }

View File

@@ -1,11 +1,11 @@
package org.briarproject.briar.api.privategroup.invitation; package org.briarproject.briar.api.privategroup.invitation;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.sharing.InvitationRequest; import org.briarproject.briar.api.sharing.InvitationRequest;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -13,28 +13,14 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class GroupInvitationRequest extends InvitationRequest { public class GroupInvitationRequest extends InvitationRequest<PrivateGroup> {
private final String groupName; public GroupInvitationRequest(MessageId id, GroupId groupId, long time,
private final Author creator; boolean local, boolean sent, boolean seen, boolean read,
SessionId sessionId, PrivateGroup shareable, ContactId contactId,
public GroupInvitationRequest(MessageId id, SessionId sessionId, @Nullable String message, boolean available, boolean canBeOpened) {
GroupId groupId, ContactId contactId, @Nullable String message, super(id, groupId, time, local, sent, seen, read, sessionId, shareable,
GroupId privateGroupId, String groupName, Author creator, contactId, message, available, canBeOpened);
boolean available, boolean canBeOpened, long time,
boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, groupId, contactId, message, privateGroupId,
available, canBeOpened, time, local, sent, seen, read);
this.groupName = groupName;
this.creator = creator;
}
public String getGroupName() {
return groupName;
}
public Author getCreator() {
return creator;
} }
} }

View File

@@ -13,11 +13,12 @@ import javax.annotation.concurrent.Immutable;
@NotNullByDefault @NotNullByDefault
public class GroupInvitationResponse extends InvitationResponse { public class GroupInvitationResponse extends InvitationResponse {
public GroupInvitationResponse(MessageId id, SessionId sessionId, public GroupInvitationResponse(MessageId id, GroupId groupId, long time,
GroupId groupId, ContactId contactId, GroupId privateGroupId, boolean local, boolean sent, boolean seen, boolean read,
boolean accept, long time, boolean local, boolean sent, SessionId sessionId, GroupId shareableId, ContactId contactId,
boolean seen, boolean read) { boolean accept) {
super(id, sessionId, groupId, contactId, privateGroupId, accept, time, super(id, groupId, time, local, sent, seen, read, sessionId,
local, sent, seen, read); shareableId, contactId, accept);
} }
} }

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
@Deprecated
public interface InvitationFactory<I extends SharingMessage.Invitation> { public interface InvitationFactory<I extends SharingMessage.Invitation> {
I build(GroupId groupId, BdfDictionary d) throws FormatException; I build(GroupId groupId, BdfDictionary d) throws FormatException;

View File

@@ -7,25 +7,22 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.BaseMessageHeader; import org.briarproject.briar.api.client.BaseMessageHeader;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public abstract class InvitationMessage extends BaseMessageHeader { public class InvitationMessage extends BaseMessageHeader {
private final SessionId sessionId; private final SessionId sessionId;
private final ContactId contactId; private final ContactId contactId;
private final GroupId invitedGroupId;
public InvitationMessage(MessageId id, SessionId sessionId, GroupId groupId, public InvitationMessage(MessageId id, GroupId groupId, long time,
ContactId contactId, GroupId invitedGroupId, long time, boolean local, boolean sent, boolean seen, boolean read,
boolean local, boolean sent, boolean seen, boolean read) { SessionId sessionId, ContactId contactId) {
super(id, groupId, time, local, read, sent, seen); super(id, groupId, time, local, read, sent, seen);
this.sessionId = sessionId; this.sessionId = sessionId;
this.contactId = contactId; this.contactId = contactId;
this.invitedGroupId = invitedGroupId;
} }
public SessionId getSessionId() { public SessionId getSessionId() {
@@ -36,9 +33,4 @@ public abstract class InvitationMessage extends BaseMessageHeader {
return contactId; return contactId;
} }
@Nullable
public GroupId getInvitedGroupId() {
return invitedGroupId;
}
} }

View File

@@ -11,20 +11,19 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public abstract class InvitationRequest extends InvitationMessage { public class InvitationRequest<S extends Shareable> extends InvitationMessage {
private final S shareable;
@Nullable @Nullable
private final String message; private final String message;
private final boolean available, canBeOpened; private final boolean available, canBeOpened;
public InvitationRequest(MessageId id, SessionId sessionId, GroupId groupId, public InvitationRequest(MessageId id, GroupId groupId, long time,
ContactId contactId, @Nullable String message, boolean local, boolean sent, boolean seen, boolean read,
GroupId invitedGroupId, boolean available, SessionId sessionId, S shareable, ContactId contactId,
boolean canBeOpened, long time, boolean local, boolean sent, @Nullable String message, boolean available, boolean canBeOpened) {
boolean seen, boolean read) { super(id, groupId, time, local, sent, seen, read, sessionId, contactId);
super(id, sessionId, groupId, contactId, invitedGroupId, time, local, this.shareable = shareable;
sent, seen, read);
if (available && canBeOpened) throw new IllegalArgumentException();
this.message = message; this.message = message;
this.available = available; this.available = available;
this.canBeOpened = canBeOpened; this.canBeOpened = canBeOpened;
@@ -43,4 +42,8 @@ public abstract class InvitationRequest extends InvitationMessage {
return canBeOpened; return canBeOpened;
} }
public S getShareable() {
return shareable;
}
} }

View File

@@ -10,21 +10,26 @@ import javax.annotation.concurrent.Immutable;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public abstract class InvitationResponse extends InvitationMessage { public class InvitationResponse extends InvitationMessage {
private final GroupId shareableId;
private final boolean accept; private final boolean accept;
public InvitationResponse(MessageId id, SessionId sessionId, public InvitationResponse(MessageId id, GroupId groupId,
GroupId groupId, ContactId contactId, long time, boolean local, boolean sent, boolean seen,
GroupId invitedGroupId, boolean accept, long time, boolean read, SessionId sessionId, GroupId shareableId,
boolean local, boolean sent, boolean seen, boolean read) { ContactId contactId, boolean accept) {
super(id, groupId, time, local, sent, seen, read, sessionId, contactId);
super(id, sessionId, groupId, contactId, invitedGroupId, time, local, this.shareableId = shareableId;
sent, seen, read);
this.accept = accept; this.accept = accept;
} }
public boolean wasAccepted() { public boolean wasAccepted() {
return accept; return accept;
} }
public GroupId getShareableId() {
return shareableId;
}
} }

View File

@@ -15,34 +15,63 @@ public interface SharingConstants {
*/ */
int MAX_INVITATION_MESSAGE_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024; int MAX_INVITATION_MESSAGE_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
@Deprecated
String CONTACT_ID = "contactId"; String CONTACT_ID = "contactId";
@Deprecated
String GROUP_ID = "groupId"; String GROUP_ID = "groupId";
@Deprecated
String TO_BE_SHARED_BY_US = "toBeSharedByUs"; String TO_BE_SHARED_BY_US = "toBeSharedByUs";
@Deprecated
String SHARED_BY_US = "sharedByUs"; String SHARED_BY_US = "sharedByUs";
@Deprecated
String SHARED_WITH_US = "sharedWithUs"; String SHARED_WITH_US = "sharedWithUs";
@Deprecated
String TYPE = "type"; String TYPE = "type";
@Deprecated
String SESSION_ID = "sessionId"; String SESSION_ID = "sessionId";
@Deprecated
String STORAGE_ID = "storageId"; String STORAGE_ID = "storageId";
@Deprecated
String STATE = "state"; String STATE = "state";
@Deprecated
String LOCAL = "local"; String LOCAL = "local";
@Deprecated
String TIME = "time"; String TIME = "time";
@Deprecated
String IS_SHARER = "isSharer"; String IS_SHARER = "isSharer";
@Deprecated
String SHAREABLE_ID = "shareableId"; String SHAREABLE_ID = "shareableId";
@Deprecated
String INVITATION_MSG = "invitationMsg"; String INVITATION_MSG = "invitationMsg";
@Deprecated
String INVITATION_ID = "invitationId"; String INVITATION_ID = "invitationId";
@Deprecated
String RESPONSE_ID = "responseId"; String RESPONSE_ID = "responseId";
@Deprecated
int SHARE_MSG_TYPE_INVITATION = 1; int SHARE_MSG_TYPE_INVITATION = 1;
@Deprecated
int SHARE_MSG_TYPE_ACCEPT = 2; int SHARE_MSG_TYPE_ACCEPT = 2;
@Deprecated
int SHARE_MSG_TYPE_DECLINE = 3; int SHARE_MSG_TYPE_DECLINE = 3;
@Deprecated
int SHARE_MSG_TYPE_LEAVE = 4; int SHARE_MSG_TYPE_LEAVE = 4;
@Deprecated
int SHARE_MSG_TYPE_ABORT = 5; int SHARE_MSG_TYPE_ABORT = 5;
@Deprecated
int TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US = 0; int TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US = 0;
@Deprecated
int TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US = 1; int TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US = 1;
@Deprecated
int TASK_ADD_SHARED_SHAREABLE = 2; int TASK_ADD_SHARED_SHAREABLE = 2;
@Deprecated
int TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US = 3; int TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US = 3;
@Deprecated
int TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US = 4; int TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US = 4;
@Deprecated
int TASK_SHARE_SHAREABLE = 5; int TASK_SHARE_SHAREABLE = 5;
@Deprecated
int TASK_UNSHARE_SHAREABLE_SHARED_BY_US = 6; int TASK_UNSHARE_SHAREABLE_SHARED_BY_US = 6;
@Deprecated
int TASK_UNSHARE_SHAREABLE_SHARED_WITH_US = 7; int TASK_UNSHARE_SHAREABLE_SHARED_WITH_US = 7;
} }

View File

@@ -20,8 +20,8 @@ public interface SharingManager<S extends Shareable>
* Sends an invitation to share the given group with the given contact * Sends an invitation to share the given group with the given contact
* and sends an optional message along with it. * and sends an optional message along with it.
*/ */
void sendInvitation(GroupId groupId, ContactId contactId, void sendInvitation(GroupId shareableId, ContactId contactId,
@Nullable String message) throws DbException; @Nullable String message, long timestamp) throws DbException;
/** /**
* Responds to a pending group invitation * Responds to a pending group invitation
@@ -32,7 +32,7 @@ public interface SharingManager<S extends Shareable>
/** /**
* Responds to a pending group invitation * Responds to a pending group invitation
*/ */
void respondToInvitation(SessionId id, boolean accept) void respondToInvitation(ContactId c, SessionId id, boolean accept)
throws DbException; throws DbException;
/** /**

View File

@@ -21,6 +21,7 @@ import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE
import static org.briarproject.briar.api.sharing.SharingConstants.TIME; import static org.briarproject.briar.api.sharing.SharingConstants.TIME;
import static org.briarproject.briar.api.sharing.SharingConstants.TYPE; import static org.briarproject.briar.api.sharing.SharingConstants.TYPE;
@Deprecated
@NotNullByDefault @NotNullByDefault
public interface SharingMessage { public interface SharingMessage {

View File

@@ -108,6 +108,11 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
return f; return f;
} }
@Override
public void addForum(Transaction txn, Forum f) throws DbException {
db.addGroup(txn, f.getGroup());
}
@Override @Override
public void removeForum(Forum f) throws DbException { public void removeForum(Forum f) throws DbException {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
@@ -177,7 +176,7 @@ abstract class AbstractProtocolEngine<S extends Session>
void subscribeToPrivateGroup(Transaction txn, MessageId inviteId) void subscribeToPrivateGroup(Transaction txn, MessageId inviteId)
throws DbException, FormatException { throws DbException, FormatException {
InviteMessage invite = getInviteMessage(txn, inviteId); InviteMessage invite = messageParser.getInviteMessage(txn, inviteId);
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup( PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
invite.getGroupName(), invite.getCreator(), invite.getSalt()); invite.getGroupName(), invite.getCreator(), invite.getSalt());
long timestamp = long timestamp =
@@ -197,14 +196,6 @@ abstract class AbstractProtocolEngine<S extends Session>
session.getInviteTimestamp()) + 1); session.getInviteTimestamp()) + 1);
} }
private InviteMessage getInviteMessage(Transaction txn, MessageId m)
throws DbException, FormatException {
Message message = clientHelper.getMessage(txn, m);
if (message == null) throw new DbException();
BdfList body = clientHelper.toList(message);
return messageParser.parseInviteMessage(message, body);
}
private void sendMessage(Transaction txn, Message m, MessageType type, private void sendMessage(Transaction txn, Message m, MessageType type,
GroupId privateGroupId, boolean visibleInConversation) GroupId privateGroupId, boolean visibleInConversation)
throws DbException { throws DbException {

View File

@@ -254,8 +254,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
private GroupInvitationResponse createInvitationResponse( private GroupInvitationResponse createInvitationResponse(
GroupInvitationMessage m, ContactId c, boolean accept) { GroupInvitationMessage m, ContactId c, boolean accept) {
SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes()); SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes());
return new GroupInvitationResponse(m.getId(), sessionId, return new GroupInvitationResponse(m.getId(), m.getContactGroupId(),
m.getContactGroupId(), c, m.getPrivateGroupId(), accept, m.getTimestamp(), false, false, true, false, sessionId,
m.getTimestamp(), false, false, true, false); m.getPrivateGroupId(), c, accept);
} }
} }

View File

@@ -71,7 +71,6 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
private final ProtocolEngine<CreatorSession> creatorEngine; private final ProtocolEngine<CreatorSession> creatorEngine;
private final ProtocolEngine<InviteeSession> inviteeEngine; private final ProtocolEngine<InviteeSession> inviteeEngine;
private final ProtocolEngine<PeerSession> peerEngine; private final ProtocolEngine<PeerSession> peerEngine;
private final Group localGroup;
@Inject @Inject
GroupInvitationManagerImpl(DatabaseComponent db, GroupInvitationManagerImpl(DatabaseComponent db,
@@ -93,12 +92,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
creatorEngine = engineFactory.createCreatorEngine(); creatorEngine = engineFactory.createCreatorEngine();
inviteeEngine = engineFactory.createInviteeEngine(); inviteeEngine = engineFactory.createInviteeEngine();
peerEngine = engineFactory.createPeerEngine(); peerEngine = engineFactory.createPeerEngine();
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
} }
@Override @Override
public void createLocalState(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
db.addGroup(txn, localGroup);
// Ensure we've set things up for any pre-existing contacts // Ensure we've set things up for any pre-existing contacts
for (Contact c : db.getContacts(txn)) addingContact(txn, c); for (Contact c : db.getContacts(txn)) addingContact(txn, c);
} }
@@ -404,22 +401,15 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
throws DbException, FormatException { throws DbException, FormatException {
SessionId sessionId = getSessionId(meta.getPrivateGroupId()); SessionId sessionId = getSessionId(meta.getPrivateGroupId());
// Look up the invite message to get the details of the private group // Look up the invite message to get the details of the private group
InviteMessage invite = getInviteMessage(txn, m); InviteMessage invite = messageParser.getInviteMessage(txn, m);
PrivateGroup pg = privateGroupFactory
.createPrivateGroup(invite.getGroupName(), invite.getCreator(),
invite.getSalt());
boolean canBeOpened = db.containsGroup(txn, invite.getPrivateGroupId()); boolean canBeOpened = db.containsGroup(txn, invite.getPrivateGroupId());
return new GroupInvitationRequest(m, sessionId, contactGroupId, c, return new GroupInvitationRequest(m, contactGroupId,
invite.getMessage(), invite.getPrivateGroupId(), meta.getTimestamp(), meta.isLocal(), status.isSent(),
invite.getGroupName(), invite.getCreator(), status.isSeen(), meta.isRead(), sessionId, pg, c,
meta.isAvailableToAnswer(), canBeOpened, meta.getTimestamp(), invite.getMessage(), meta.isAvailableToAnswer(), canBeOpened);
meta.isLocal(), status.isSent(), status.isSeen(),
meta.isRead());
}
private InviteMessage getInviteMessage(Transaction txn, MessageId m)
throws DbException, FormatException {
Message message = clientHelper.getMessage(txn, m);
if (message == null) throw new DbException();
BdfList body = clientHelper.toList(message);
return messageParser.parseInviteMessage(message, body);
} }
private GroupInvitationResponse parseInvitationResponse(ContactId c, private GroupInvitationResponse parseInvitationResponse(ContactId c,
@@ -427,10 +417,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
MessageStatus status, boolean accept) MessageStatus status, boolean accept)
throws DbException, FormatException { throws DbException, FormatException {
SessionId sessionId = getSessionId(meta.getPrivateGroupId()); SessionId sessionId = getSessionId(meta.getPrivateGroupId());
return new GroupInvitationResponse(m, sessionId, contactGroupId, c, return new GroupInvitationResponse(m, contactGroupId,
meta.getPrivateGroupId(), accept, meta.getTimestamp(), meta.getTimestamp(), meta.isLocal(), status.isSent(),
meta.isLocal(), status.isSent(), status.isSeen(), status.isSeen(), meta.isRead(), sessionId,
meta.isRead()); meta.getPrivateGroupId(), c, accept);
} }
@Override @Override
@@ -481,7 +471,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
private GroupInvitationItem parseGroupInvitationItem(Transaction txn, private GroupInvitationItem parseGroupInvitationItem(Transaction txn,
Contact c, MessageId m) throws DbException, FormatException { Contact c, MessageId m) throws DbException, FormatException {
InviteMessage invite = getInviteMessage(txn, m); InviteMessage invite = messageParser.getInviteMessage(txn, m);
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup( PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
invite.getGroupName(), invite.getCreator(), invite.getSalt()); invite.getGroupName(), invite.getCreator(), invite.getSalt());
return new GroupInvitationItem(privateGroup, c); return new GroupInvitationItem(privateGroup, c);

View File

@@ -234,8 +234,9 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
// Broadcast an event // Broadcast an event
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup( PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
m.getGroupName(), m.getCreator(), m.getSalt()); m.getGroupName(), m.getCreator(), m.getSalt());
txn.attach(new GroupInvitationRequestReceivedEvent(privateGroup, txn.attach(
contactId, createInvitationRequest(m, contactId))); new GroupInvitationRequestReceivedEvent(privateGroup, contactId,
createInvitationRequest(m, privateGroup, contactId)));
// Move to the INVITED state // Move to the INVITED state
return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(), return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(), s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
@@ -317,11 +318,11 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
} }
private GroupInvitationRequest createInvitationRequest(InviteMessage m, private GroupInvitationRequest createInvitationRequest(InviteMessage m,
ContactId c) { PrivateGroup pg, ContactId c) {
SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes()); SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes());
return new GroupInvitationRequest(m.getId(), sessionId, return new GroupInvitationRequest(m.getId(), m.getContactGroupId(),
m.getContactGroupId(), c, m.getMessage(), m.getPrivateGroupId(), m.getTimestamp(), false, false, true, false, sessionId, pg, c,
m.getGroupName(), m.getCreator(), true, false, m.getTimestamp(), m.getMessage(), true, false);
false, false, true, false);
} }
} }

View File

@@ -3,9 +3,12 @@ package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
@NotNullByDefault @NotNullByDefault
interface MessageParser { interface MessageParser {
@@ -18,6 +21,9 @@ interface MessageParser {
MessageMetadata parseMetadata(BdfDictionary meta) throws FormatException; MessageMetadata parseMetadata(BdfDictionary meta) throws FormatException;
InviteMessage getInviteMessage(Transaction txn, MessageId m)
throws DbException, FormatException;
InviteMessage parseInviteMessage(Message m, BdfList body) InviteMessage parseInviteMessage(Message m, BdfList body)
throws FormatException; throws FormatException;

View File

@@ -1,9 +1,12 @@
package org.briarproject.briar.privategroup.invitation; package org.briarproject.briar.privategroup.invitation;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry; import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory; import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -31,12 +34,14 @@ class MessageParserImpl implements MessageParser {
private final AuthorFactory authorFactory; private final AuthorFactory authorFactory;
private final PrivateGroupFactory privateGroupFactory; private final PrivateGroupFactory privateGroupFactory;
private final ClientHelper clientHelper;
@Inject @Inject
MessageParserImpl(AuthorFactory authorFactory, MessageParserImpl(AuthorFactory authorFactory,
PrivateGroupFactory privateGroupFactory) { PrivateGroupFactory privateGroupFactory, ClientHelper clientHelper) {
this.authorFactory = authorFactory; this.authorFactory = authorFactory;
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
this.clientHelper = clientHelper;
} }
@Override @Override
@@ -78,6 +83,15 @@ class MessageParserImpl implements MessageParser {
visible, available); visible, available);
} }
@Override
public InviteMessage getInviteMessage(Transaction txn, MessageId m)
throws DbException, FormatException {
Message message = clientHelper.getMessage(txn, m);
if (message == null) throw new DbException();
BdfList body = clientHelper.toList(message);
return parseInviteMessage(message, body);
}
@Override @Override
public InviteMessage parseInviteMessage(Message m, BdfList body) public InviteMessage parseInviteMessage(Message m, BdfList body)
throws FormatException { throws FormatException {

View File

@@ -0,0 +1,19 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class AbortMessage extends SharingMessage {
AbortMessage(MessageId id, GroupId contactGroupId, GroupId shareableId,
long timestamp, @Nullable MessageId previousMessageId) {
super(id, contactGroupId, shareableId, timestamp, previousMessageId);
}
}

View File

@@ -0,0 +1,19 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class AcceptMessage extends SharingMessage {
AcceptMessage(MessageId id, @Nullable MessageId previousMessageId,
GroupId contactGroupId, GroupId shareableId, long timestamp) {
super(id, contactGroupId, shareableId, timestamp, previousMessageId);
}
}

View File

@@ -52,7 +52,7 @@ import static org.briarproject.briar.api.sharing.SharingConstants.RESPONSE_ID;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class BlogSharingManagerImpl extends class BlogSharingManagerImpl extends
SharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationRequestReceivedEvent, BlogInvitationResponseReceivedEvent> OldSharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationRequestReceivedEvent, BlogInvitationResponseReceivedEvent>
implements BlogSharingManager, RemoveBlogHook { implements BlogSharingManager, RemoveBlogHook {
private final ContactManager contactManager; private final ContactManager contactManager;
@@ -159,7 +159,7 @@ class BlogSharingManagerImpl extends
} }
@Override @Override
protected InvitationFactory<BlogInvitation, BlogSharerSessionState> getIFactory() { protected OldInvitationFactory<BlogInvitation, BlogSharerSessionState> getIFactory() {
return iFactory; return iFactory;
} }
@@ -251,7 +251,7 @@ class BlogSharingManagerImpl extends
} }
private static class IFactory implements private static class IFactory implements
InvitationFactory<BlogInvitation, BlogSharerSessionState> { OldInvitationFactory<BlogInvitation, BlogSharerSessionState> {
@Override @Override
public BlogInvitation build(GroupId groupId, BdfDictionary d) public BlogInvitation build(GroupId groupId, BdfDictionary d)
throws FormatException { throws FormatException {

View File

@@ -0,0 +1,20 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class DeclineMessage extends SharingMessage {
DeclineMessage(MessageId id, GroupId contactGroupId,
GroupId shareableId, long timestamp,
@Nullable MessageId previousMessageId) {
super(id, contactGroupId, shareableId, timestamp, previousMessageId);
}
}

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumInvitationRequest;
import org.briarproject.briar.api.forum.ForumInvitationResponse;
import javax.inject.Inject;
public class ForumInvitationFactoryImpl implements InvitationFactory<Forum> {
@Inject
ForumInvitationFactoryImpl() {
}
@Override
public ForumInvitationRequest createInvitationRequest(boolean local,
boolean sent, boolean seen, boolean read, InviteMessage<Forum> m,
ContactId c, boolean available, boolean canBeOpened) {
SessionId sessionId = new SessionId(m.getShareableId().getBytes());
return new ForumInvitationRequest(m.getId(), m.getContactGroupId(),
m.getTimestamp(), local, sent, seen, read, sessionId,
m.getShareable(), c, m.getMessage(), available, canBeOpened);
}
@Override
public ForumInvitationResponse createInvitationResponse(MessageId id,
GroupId contactGroupId, long time, boolean local, boolean sent,
boolean seen, boolean read, GroupId shareableId,
ContactId contactId, boolean accept) {
SessionId sessionId = new SessionId(shareableId.getBytes());
return new ForumInvitationResponse(id, contactGroupId, time, local,
sent, seen, read, sessionId, shareableId, contactId, accept);
}
}

View File

@@ -1,47 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT;
@NotThreadSafe
@NotNullByDefault
class ForumInviteeSessionState extends InviteeSessionState {
private final String forumName;
private final byte[] forumSalt;
ForumInviteeSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt, MessageId invitationId) {
super(sessionId, storageId, groupId, state, contactId, forumId,
invitationId);
this.forumName = forumName;
this.forumSalt = forumSalt;
}
@Override
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(FORUM_NAME, getForumName());
d.put(FORUM_SALT, getForumSalt());
return d;
}
String getForumName() {
return forumName;
}
byte[] getForumSalt() {
return forumSalt;
}
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumFactory;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class ForumMessageParserImpl extends MessageParserImpl<Forum> {
private final ForumFactory forumFactory;
@Inject
ForumMessageParserImpl(ClientHelper clientHelper,
ForumFactory forumFactory) {
super(clientHelper);
this.forumFactory = forumFactory;
}
@Override
protected Forum createShareable(BdfList descriptor)
throws FormatException {
String name = descriptor.getString(0);
byte[] salt = descriptor.getRaw(1);
return forumFactory.createForum(name, salt);
}
}

View File

@@ -0,0 +1,93 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumInvitationRequest;
import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.forum.event.ForumInvitationRequestReceivedEvent;
import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class ForumProtocolEngineImpl extends ProtocolEngineImpl<Forum> {
private final ForumManager forumManager;
private final InvitationFactory<Forum> invitationFactory;
@Inject
ForumProtocolEngineImpl(DatabaseComponent db,
ClientHelper clientHelper, MessageEncoder messageEncoder,
MessageParser<Forum> messageParser, MessageTracker messageTracker,
Clock clock, ForumManager forumManager,
InvitationFactory<Forum> invitationFactory) {
super(db, clientHelper, messageEncoder, messageParser, messageTracker,
clock);
this.forumManager = forumManager;
this.invitationFactory = invitationFactory;
}
@Override
Event getInvitationRequestReceivedEvent(InviteMessage<Forum> m,
ContactId contactId, boolean available, boolean canBeOpened) {
ForumInvitationRequest request =
(ForumInvitationRequest) invitationFactory
.createInvitationRequest(false, false, true, false, m,
contactId, available, canBeOpened);
return new ForumInvitationRequestReceivedEvent(m.getShareable(),
contactId, request);
}
@Override
Event getInvitationResponseReceivedEvent(AcceptMessage m,
ContactId contactId) {
ForumInvitationResponse response =
(ForumInvitationResponse) invitationFactory
.createInvitationResponse(m.getId(),
m.getContactGroupId(), m.getTimestamp(), false,
false, true, false, m.getShareableId(),
contactId, true);
return new ForumInvitationResponseReceivedEvent(contactId, response);
}
@Override
Event getInvitationResponseReceivedEvent(DeclineMessage m,
ContactId contactId) {
ForumInvitationResponse response =
(ForumInvitationResponse) invitationFactory
.createInvitationResponse(m.getId(),
m.getContactGroupId(), m.getTimestamp(), false,
false, true, false, m.getShareableId(),
contactId, true);
return new ForumInvitationResponseReceivedEvent(contactId, response);
}
@Override
protected ClientId getClientId() {
return ForumSharingManager.CLIENT_ID;
}
@Override
protected void addShareable(Transaction txn, MessageId inviteId)
throws DbException, FormatException {
InviteMessage<Forum> invite =
messageParser.getInviteMessage(txn, inviteId);
forumManager.addForum(txn, invite.getShareable());
}
}

View File

@@ -1,49 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT;
@NotThreadSafe
@NotNullByDefault
class ForumSharerSessionState extends SharerSessionState {
private final String forumName;
private final byte[] forumSalt;
ForumSharerSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt,
@Nullable MessageId responseId) {
super(sessionId, storageId, groupId, state, contactId, forumId,
responseId);
this.forumName = forumName;
this.forumSalt = forumSalt;
}
@Override
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(FORUM_NAME, getForumName());
d.put(FORUM_SALT, getForumSalt());
return d;
}
String getForumName() {
return forumName;
}
byte[] getForumSalt() {
return forumSalt;
}
}

View File

@@ -1,78 +1,35 @@
package org.briarproject.briar.sharing; package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.data.MetadataParser; import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.MessageQueueManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumFactory;
import org.briarproject.briar.api.forum.ForumInvitationRequest;
import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumManager.RemoveForumHook; import org.briarproject.briar.api.forum.ForumManager.RemoveForumHook;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.forum.ForumSharingMessage.ForumInvitation;
import org.briarproject.briar.api.forum.event.ForumInvitationRequestReceivedEvent;
import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent;
import org.briarproject.briar.api.sharing.InvitationMessage;
import java.security.SecureRandom;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.briar.api.sharing.SharingConstants.INVITATION_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.RESPONSE_ID;
@NotNullByDefault @NotNullByDefault
class ForumSharingManagerImpl extends class ForumSharingManagerImpl extends SharingManagerImpl<Forum>
SharingManagerImpl<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationRequestReceivedEvent, ForumInvitationResponseReceivedEvent>
implements ForumSharingManager, RemoveForumHook { implements ForumSharingManager, RemoveForumHook {
private final SFactory sFactory;
private final IFactory iFactory;
private final ISFactory isFactory;
private final SSFactory ssFactory;
private final IRFactory irFactory;
private final IRRFactory irrFactory;
@Inject @Inject
ForumSharingManagerImpl(ClientHelper clientHelper, ForumSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
Clock clock, DatabaseComponent db, MetadataParser metadataParser, MessageParser<Forum> messageParser,
ForumFactory forumFactory, SessionEncoder sessionEncoder, SessionParser sessionParser,
ForumManager forumManager, MessageTracker messageTracker,
MessageQueueManager messageQueueManager,
MetadataEncoder metadataEncoder,
MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory, ContactGroupFactory contactGroupFactory,
SecureRandom random, MessageTracker messageTracker) { ProtocolEngine<Forum> engine,
super(db, messageQueueManager, clientHelper, metadataParser, InvitationFactory<Forum> invitationFactory) {
metadataEncoder, random, contactGroupFactory, messageTracker, super(db, clientHelper, metadataParser, messageParser, sessionEncoder,
clock); sessionParser, messageTracker, contactGroupFactory, engine,
invitationFactory);
sFactory = new SFactory(forumFactory, forumManager);
iFactory = new IFactory();
isFactory = new ISFactory();
ssFactory = new SSFactory();
irFactory = new IRFactory(sFactory);
irrFactory = new IRRFactory();
} }
@Override @Override
@@ -80,221 +37,9 @@ class ForumSharingManagerImpl extends
return CLIENT_ID; return CLIENT_ID;
} }
@Override
protected InvitationMessage createInvitationRequest(MessageId id,
ForumInvitation msg, ContactId contactId, GroupId forumId,
boolean available, boolean canBeOpened, long time,
boolean local, boolean sent, boolean seen, boolean read) {
return new ForumInvitationRequest(id, msg.getSessionId(),
msg.getGroupId(), contactId, forumId, msg.getForumName(),
msg.getMessage(), available, canBeOpened, time, local, sent,
seen, read);
}
@Override
protected InvitationMessage createInvitationResponse(MessageId id,
SessionId sessionId, GroupId groupId, ContactId contactId,
GroupId forumId, boolean accept, long time, boolean local,
boolean sent, boolean seen, boolean read) {
return new ForumInvitationResponse(id, sessionId, groupId, contactId,
forumId, accept, time, local, sent, seen, read);
}
@Override
protected ShareableFactory<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState> getSFactory() {
return sFactory;
}
@Override
protected InvitationFactory<ForumInvitation, ForumSharerSessionState> getIFactory() {
return iFactory;
}
@Override
protected InviteeSessionStateFactory<Forum, ForumInviteeSessionState> getISFactory() {
return isFactory;
}
@Override
protected SharerSessionStateFactory<Forum, ForumSharerSessionState> getSSFactory() {
return ssFactory;
}
@Override
protected InvitationReceivedEventFactory<ForumInviteeSessionState, ForumInvitationRequestReceivedEvent> getIRFactory() {
return irFactory;
}
@Override
protected InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> getIRRFactory() {
return irrFactory;
}
@Override @Override
public void removingForum(Transaction txn, Forum f) throws DbException { public void removingForum(Transaction txn, Forum f) throws DbException {
removingShareable(txn, f); removingShareable(txn, f);
} }
private static class SFactory implements
ShareableFactory<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState> {
private final ForumFactory forumFactory;
private final ForumManager forumManager;
private SFactory(ForumFactory forumFactory, ForumManager forumManager) {
this.forumFactory = forumFactory;
this.forumManager = forumManager;
}
@Override
public BdfList encode(Forum f) {
return BdfList.of(f.getName(), f.getSalt());
}
@Override
public Forum get(Transaction txn, GroupId groupId)
throws DbException {
return forumManager.getForum(txn, groupId);
}
@Override
public Forum parse(BdfList shareable) throws FormatException {
return forumFactory
.createForum(shareable.getString(0), shareable.getRaw(1));
}
@Override
public Forum parse(ForumInvitation msg) {
return forumFactory
.createForum(msg.getForumName(), msg.getForumSalt());
}
@Override
public Forum parse(ForumInviteeSessionState state) {
return forumFactory
.createForum(state.getForumName(), state.getForumSalt());
}
@Override
public Forum parse(ForumSharerSessionState state) {
return forumFactory
.createForum(state.getForumName(), state.getForumSalt());
}
}
private static class IFactory implements
InvitationFactory<ForumInvitation, ForumSharerSessionState> {
@Override
public ForumInvitation build(GroupId groupId, BdfDictionary d)
throws FormatException {
return ForumInvitation.from(groupId, d);
}
@Override
public ForumInvitation build(ForumSharerSessionState localState,
long time) {
return new ForumInvitation(localState.getContactGroupId(),
localState.getSessionId(), localState.getForumName(),
localState.getForumSalt(), time, localState.getMessage());
}
}
private static class ISFactory implements
InviteeSessionStateFactory<Forum, ForumInviteeSessionState> {
@Override
public ForumInviteeSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId,
GroupId forumId, BdfDictionary d) throws FormatException {
String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT);
MessageId invitationId = new MessageId(d.getRaw(INVITATION_ID));
return new ForumInviteeSessionState(sessionId, storageId,
groupId, state, contactId, forumId, forumName, forumSalt,
invitationId);
}
@Override
public ForumInviteeSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId,
Forum forum, MessageId invitationId) {
return new ForumInviteeSessionState(sessionId, storageId,
groupId, state, contactId, forum.getId(), forum.getName(),
forum.getSalt(), invitationId);
}
}
private static class SSFactory implements
SharerSessionStateFactory<Forum, ForumSharerSessionState> {
@Override
public ForumSharerSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId,
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(RESPONSE_ID);
if (responseIdBytes != null)
responseId = new MessageId(responseIdBytes);
return new ForumSharerSessionState(sessionId, storageId,
groupId, state, contactId, forumId, forumName, forumSalt,
responseId);
}
@Override
public ForumSharerSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId,
Forum forum) {
return new ForumSharerSessionState(sessionId, storageId,
groupId, state, contactId, forum.getId(), forum.getName(),
forum.getSalt(), null);
}
}
private static class IRFactory implements
InvitationReceivedEventFactory<ForumInviteeSessionState, ForumInvitationRequestReceivedEvent> {
private final SFactory sFactory;
private IRFactory(SFactory sFactory) {
this.sFactory = sFactory;
}
@Override
public ForumInvitationRequestReceivedEvent build(
ForumInviteeSessionState localState, long time,
@Nullable String msg) {
Forum forum = sFactory.parse(localState);
ContactId contactId = localState.getContactId();
ForumInvitationRequest request = new ForumInvitationRequest(
localState.getInvitationId(), localState.getSessionId(),
localState.getContactGroupId(), contactId,
localState.getShareableId(), forum.getName(), msg, true,
false, time, false, false, false, false);
return new ForumInvitationRequestReceivedEvent(forum, contactId,
request);
}
}
private static class IRRFactory implements
InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> {
@Override
public ForumInvitationResponseReceivedEvent build(
ForumSharerSessionState localState, boolean accept, long time) {
String name = localState.getForumName();
ContactId c = localState.getContactId();
MessageId responseId = localState.getResponseId();
if (responseId == null)
throw new IllegalStateException("No responseId");
ForumInvitationResponse response = new ForumInvitationResponse(
responseId, localState.getSessionId(),
localState.getContactGroupId(), localState.getContactId(),
localState.getShareableId(), accept, time, false, false,
false, false);
return new ForumInvitationResponseReceivedEvent(name, c, response);
}
}
} }

View File

@@ -1,89 +1,47 @@
package org.briarproject.briar.sharing; package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder; import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.client.BdfQueueMessageValidator; import org.briarproject.briar.api.forum.ForumFactory;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.util.ValidationUtils.checkLength; import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize; import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH; import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH; import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
import static org.briarproject.briar.api.sharing.SharingConstants.INVITATION_MSG;
import static org.briarproject.briar.api.sharing.SharingConstants.LOCAL;
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
import static org.briarproject.briar.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.briar.api.sharing.SharingConstants.TIME;
import static org.briarproject.briar.api.sharing.SharingConstants.TYPE;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class ForumSharingValidator extends BdfQueueMessageValidator { class ForumSharingValidator extends SharingValidator {
private final ForumFactory forumFactory;
@Inject @Inject
ForumSharingValidator(ClientHelper clientHelper, ForumSharingValidator(MessageEncoder messageEncoder,
MetadataEncoder metadataEncoder, Clock clock) { ClientHelper clientHelper, MetadataEncoder metadataEncoder,
super(clientHelper, metadataEncoder, clock); Clock clock, ForumFactory forumFactory) {
super(messageEncoder, clientHelper, metadataEncoder, clock);
this.forumFactory = forumFactory;
} }
@Override @Override
protected BdfMessageContext validateMessage(Message m, Group g, protected GroupId validateDescriptor(BdfList descriptor)
BdfList body) throws FormatException { throws FormatException {
checkSize(descriptor, 2);
BdfDictionary d = new BdfDictionary(); String name = descriptor.getString(0);
long type = body.getLong(0); checkLength(name, 1, MAX_FORUM_NAME_LENGTH);
byte[] id = body.getRaw(1); byte[] salt = descriptor.getRaw(1);
checkLength(id, SessionId.LENGTH); checkLength(salt, FORUM_SALT_LENGTH);
Forum forum = forumFactory.createForum(name, salt);
if (type == SHARE_MSG_TYPE_INVITATION) { return forum.getId();
checkSize(body, 4, 5);
String name = body.getString(2);
checkLength(name, 1, MAX_FORUM_NAME_LENGTH);
byte[] salt = body.getRaw(3);
checkLength(salt, FORUM_SALT_LENGTH);
d.put(FORUM_NAME, name);
d.put(FORUM_SALT, salt);
if (body.size() > 4) {
String msg = body.getString(4);
checkLength(msg, 0, MAX_INVITATION_MESSAGE_LENGTH);
d.put(INVITATION_MSG, msg);
}
} else {
checkSize(body, 2);
if (type != SHARE_MSG_TYPE_ACCEPT &&
type != SHARE_MSG_TYPE_DECLINE &&
type != SHARE_MSG_TYPE_LEAVE &&
type != SHARE_MSG_TYPE_ABORT) {
throw new FormatException();
}
}
// Return the metadata
d.put(TYPE, type);
d.put(SESSION_ID, id);
d.put(LOCAL, false);
d.put(TIME, m.getTimestamp());
return new BdfMessageContext(d);
} }
} }

View File

@@ -1,11 +1,21 @@
package org.briarproject.briar.sharing; package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.briar.api.sharing.SharingMessage; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.sharing.InvitationRequest;
import org.briarproject.briar.api.sharing.InvitationResponse;
import org.briarproject.briar.api.sharing.Shareable;
@NotNullByDefault public interface InvitationFactory<S extends Shareable> {
interface InvitationFactory<I extends SharingMessage.Invitation, SS extends SharerSessionState>
extends org.briarproject.briar.api.sharing.InvitationFactory<I> { InvitationRequest<S> createInvitationRequest(boolean local, boolean sent,
boolean seen, boolean read, InviteMessage<S> m, ContactId c,
boolean available, boolean canBeOpened);
InvitationResponse createInvitationResponse(MessageId id,
GroupId contactGroupId, long time, boolean local, boolean sent,
boolean seen, boolean read, GroupId shareableId,
ContactId contactId, boolean accept);
I build(SS localState, long time);
} }

View File

@@ -5,6 +5,7 @@ import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@Deprecated
@NotNullByDefault @NotNullByDefault
interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent> { interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent> {

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent; import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
@Deprecated
@NotNullByDefault @NotNullByDefault
interface InvitationResponseReceivedEventFactory<SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> { interface InvitationResponseReceivedEventFactory<SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> {

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.sharing.Shareable;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class InviteMessage<S extends Shareable> extends SharingMessage {
private final S shareable;
@Nullable
private final String message;
InviteMessage(MessageId id, @Nullable MessageId previousMessageId,
GroupId contactGroupId, S shareable, @Nullable String message,
long timestamp) {
super(id, contactGroupId, shareable.getId(), timestamp,
previousMessageId);
if (message != null && message.equals(""))
throw new IllegalArgumentException();
this.shareable = shareable;
this.message = message;
}
public S getShareable() {
return shareable;
}
@Nullable
public String getMessage() {
return message;
}
}

View File

@@ -28,6 +28,7 @@ import static org.briarproject.briar.api.sharing.SharingConstants.TASK_UNSHARE_S
import static org.briarproject.briar.api.sharing.SharingMessage.BaseMessage; import static org.briarproject.briar.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.briar.api.sharing.SharingMessage.SimpleMessage; import static org.briarproject.briar.api.sharing.SharingMessage.SimpleMessage;
@Deprecated
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent> class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent>

View File

@@ -23,6 +23,7 @@ import static org.briarproject.briar.sharing.InviteeSessionState.Action.LOCAL_LE
import static org.briarproject.briar.sharing.InviteeSessionState.Action.REMOTE_INVITATION; import static org.briarproject.briar.sharing.InviteeSessionState.Action.REMOTE_INVITATION;
import static org.briarproject.briar.sharing.InviteeSessionState.Action.REMOTE_LEAVE; import static org.briarproject.briar.sharing.InviteeSessionState.Action.REMOTE_LEAVE;
@Deprecated
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
public abstract class InviteeSessionState extends SharingSessionState { public abstract class InviteeSessionState extends SharingSessionState {

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.sharing.Shareable; import org.briarproject.briar.api.sharing.Shareable;
@Deprecated
interface InviteeSessionStateFactory<S extends Shareable, IS extends InviteeSessionState> { interface InviteeSessionStateFactory<S extends Shareable, IS extends InviteeSessionState> {
IS build(SessionId sessionId, MessageId storageId, GroupId groupId, IS build(SessionId sessionId, MessageId storageId, GroupId groupId,

View File

@@ -0,0 +1,20 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class LeaveMessage extends SharingMessage {
LeaveMessage(MessageId id, GroupId contactGroupId,
GroupId shareableId, long timestamp,
@Nullable MessageId previousMessageId) {
super(id, contactGroupId, shareableId, timestamp, previousMessageId);
}
}

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
@NotNullByDefault
interface MessageEncoder {
BdfDictionary encodeMetadata(MessageType type, GroupId shareableId,
long timestamp, boolean local, boolean read, boolean visible,
boolean available);
void setVisibleInUi(BdfDictionary meta, boolean visible);
void setAvailableToAnswer(BdfDictionary meta, boolean available);
Message encodeInviteMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, BdfList descriptor,
@Nullable String message);
Message encodeAcceptMessage(GroupId contactGroupId, GroupId shareableId,
long timestamp, @Nullable MessageId previousMessageId);
Message encodeDeclineMessage(GroupId contactGroupId, GroupId shareableId,
long timestamp, @Nullable MessageId previousMessageId);
Message encodeLeaveMessage(GroupId contactGroupId, GroupId shareableId,
long timestamp, @Nullable MessageId previousMessageId);
Message encodeAbortMessage(GroupId contactGroupId, GroupId shareableId,
long timestamp, @Nullable MessageId previousMessageId);
}

View File

@@ -0,0 +1,137 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.briar.sharing.MessageType.ABORT;
import static org.briarproject.briar.sharing.MessageType.ACCEPT;
import static org.briarproject.briar.sharing.MessageType.DECLINE;
import static org.briarproject.briar.sharing.MessageType.INVITE;
import static org.briarproject.briar.sharing.MessageType.LEAVE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_SHAREABLE_ID;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_READ;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_TIMESTAMP;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_VISIBLE_IN_UI;
@Immutable
@NotNullByDefault
class MessageEncoderImpl implements MessageEncoder {
private final ClientHelper clientHelper;
private final MessageFactory messageFactory;
@Inject
MessageEncoderImpl(ClientHelper clientHelper,
MessageFactory messageFactory) {
this.clientHelper = clientHelper;
this.messageFactory = messageFactory;
}
@Override
public BdfDictionary encodeMetadata(MessageType type,
GroupId shareableId, long timestamp, boolean local, boolean read,
boolean visible, boolean available) {
BdfDictionary meta = new BdfDictionary();
meta.put(MSG_KEY_MESSAGE_TYPE, type.getValue());
meta.put(MSG_KEY_SHAREABLE_ID, shareableId);
meta.put(MSG_KEY_TIMESTAMP, timestamp);
meta.put(MSG_KEY_LOCAL, local);
meta.put(MSG_KEY_READ, read);
meta.put(MSG_KEY_VISIBLE_IN_UI, visible);
meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available);
return meta;
}
@Override
public void setVisibleInUi(BdfDictionary meta, boolean visible) {
meta.put(MSG_KEY_VISIBLE_IN_UI, visible);
}
@Override
public void setAvailableToAnswer(BdfDictionary meta, boolean available) {
meta.put(MSG_KEY_AVAILABLE_TO_ANSWER, available);
}
@Override
public Message encodeInviteMessage(GroupId contactGroupId, long timestamp,
@Nullable MessageId previousMessageId, BdfList descriptor,
@Nullable String message) {
if (message != null && message.equals(""))
throw new IllegalArgumentException();
BdfList body = BdfList.of(
INVITE.getValue(),
previousMessageId,
descriptor,
message
);
try {
return messageFactory.createMessage(contactGroupId, timestamp,
clientHelper.toByteArray(body));
} catch (FormatException e) {
throw new AssertionError(e);
}
}
@Override
public Message encodeAcceptMessage(GroupId contactGroupId,
GroupId shareableId, long timestamp,
@Nullable MessageId previousMessageId) {
return encodeMessage(ACCEPT, contactGroupId, shareableId, timestamp,
previousMessageId);
}
@Override
public Message encodeDeclineMessage(GroupId contactGroupId,
GroupId shareableId, long timestamp,
@Nullable MessageId previousMessageId) {
return encodeMessage(DECLINE, contactGroupId, shareableId, timestamp,
previousMessageId);
}
@Override
public Message encodeLeaveMessage(GroupId contactGroupId,
GroupId shareableId, long timestamp,
@Nullable MessageId previousMessageId) {
return encodeMessage(LEAVE, contactGroupId, shareableId, timestamp,
previousMessageId);
}
@Override
public Message encodeAbortMessage(GroupId contactGroupId,
GroupId shareableId, long timestamp,
@Nullable MessageId previousMessageId) {
return encodeMessage(ABORT, contactGroupId, shareableId, timestamp,
previousMessageId);
}
private Message encodeMessage(MessageType type, GroupId contactGroupId,
GroupId shareableId, long timestamp,
@Nullable MessageId previousMessageId) {
BdfList body = BdfList.of(
type.getValue(),
shareableId,
previousMessageId
);
try {
return messageFactory.createMessage(contactGroupId, timestamp,
clientHelper.toByteArray(body));
} catch (FormatException e) {
throw new AssertionError(e);
}
}
}

View File

@@ -0,0 +1,56 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
class MessageMetadata {
private final MessageType type;
private final GroupId shareableId;
private final long timestamp;
private final boolean local, read, visible, available;
MessageMetadata(MessageType type, GroupId shareableId, long timestamp,
boolean local, boolean read, boolean visible, boolean available) {
this.shareableId = shareableId;
this.type = type;
this.timestamp = timestamp;
this.local = local;
this.read = read;
this.visible = visible;
this.available = available;
}
MessageType getMessageType() {
return type;
}
GroupId getShareableId() {
return shareableId;
}
long getTimestamp() {
return timestamp;
}
boolean isLocal() {
return local;
}
boolean isRead() {
return read;
}
boolean isVisibleInConversation() {
return visible;
}
boolean isAvailableToAnswer() {
return available;
}
}

View File

@@ -0,0 +1,43 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.sharing.Shareable;
@NotNullByDefault
interface MessageParser<S extends Shareable> {
BdfDictionary getMessagesVisibleInUiQuery();
BdfDictionary getInvitesAvailableToAnswerQuery();
BdfDictionary getInvitesAvailableToAnswerQuery(GroupId shareableId);
MessageMetadata parseMetadata(BdfDictionary meta) throws FormatException;
InviteMessage<S> getInviteMessage(Transaction txn, MessageId m)
throws DbException, FormatException;
InviteMessage<S> parseInviteMessage(Message m, BdfList body)
throws FormatException;
AcceptMessage parseAcceptMessage(Message m, BdfList body)
throws FormatException;
DeclineMessage parseDeclineMessage(Message m, BdfList body)
throws FormatException;
LeaveMessage parseLeaveMessage(Message m, BdfList body)
throws FormatException;
AbortMessage parseAbortMessage(Message m, BdfList body)
throws FormatException;
}

View File

@@ -0,0 +1,139 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.sharing.Shareable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.sharing.MessageType.INVITE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_AVAILABLE_TO_ANSWER;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_LOCAL;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_MESSAGE_TYPE;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_READ;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_SHAREABLE_ID;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_TIMESTAMP;
import static org.briarproject.briar.sharing.SharingConstants.MSG_KEY_VISIBLE_IN_UI;
@Immutable
@NotNullByDefault
abstract class MessageParserImpl<S extends Shareable>
implements MessageParser<S> {
private final ClientHelper clientHelper;
MessageParserImpl(ClientHelper clientHelper) {
this.clientHelper = clientHelper;
}
@Override
public BdfDictionary getMessagesVisibleInUiQuery() {
return BdfDictionary.of(new BdfEntry(MSG_KEY_VISIBLE_IN_UI, true));
}
@Override
public BdfDictionary getInvitesAvailableToAnswerQuery() {
return BdfDictionary.of(
new BdfEntry(MSG_KEY_AVAILABLE_TO_ANSWER, true),
new BdfEntry(MSG_KEY_MESSAGE_TYPE, INVITE.getValue())
);
}
@Override
public BdfDictionary getInvitesAvailableToAnswerQuery(GroupId shareableId) {
return BdfDictionary.of(
new BdfEntry(MSG_KEY_AVAILABLE_TO_ANSWER, true),
new BdfEntry(MSG_KEY_MESSAGE_TYPE, INVITE.getValue()),
new BdfEntry(MSG_KEY_SHAREABLE_ID, shareableId)
);
}
@Override
public MessageMetadata parseMetadata(BdfDictionary meta)
throws FormatException {
MessageType type = MessageType
.fromValue(meta.getLong(MSG_KEY_MESSAGE_TYPE).intValue());
GroupId shareableId = new GroupId(meta.getRaw(MSG_KEY_SHAREABLE_ID));
long timestamp = meta.getLong(MSG_KEY_TIMESTAMP);
boolean local = meta.getBoolean(MSG_KEY_LOCAL);
boolean read = meta.getBoolean(MSG_KEY_READ, false);
boolean visible = meta.getBoolean(MSG_KEY_VISIBLE_IN_UI, false);
boolean available = meta.getBoolean(MSG_KEY_AVAILABLE_TO_ANSWER, false);
return new MessageMetadata(type, shareableId, timestamp, local, read,
visible, available);
}
@Override
public InviteMessage<S> getInviteMessage(Transaction txn, MessageId m)
throws DbException, FormatException {
Message message = clientHelper.getMessage(txn, m);
if (message == null) throw new DbException();
BdfList body = clientHelper.toList(message);
return parseInviteMessage(message, body);
}
@Override
public InviteMessage<S> parseInviteMessage(Message m, BdfList body)
throws FormatException {
byte[] b = body.getOptionalRaw(1);
MessageId previousMessageId = (b == null ? null : new MessageId(b));
BdfList descriptor = body.getList(2);
S shareable = createShareable(descriptor);
String message = body.getOptionalString(3);
return new InviteMessage<S>(m.getId(), previousMessageId,
m.getGroupId(), shareable, message, m.getTimestamp());
}
protected abstract S createShareable(BdfList descriptor)
throws FormatException;
@Override
public AcceptMessage parseAcceptMessage(Message m, BdfList body)
throws FormatException {
GroupId shareableId = new GroupId(body.getRaw(1));
byte[] b = body.getOptionalRaw(2);
MessageId previousMessageId = (b == null ? null : new MessageId(b));
return new AcceptMessage(m.getId(), previousMessageId, m.getGroupId(),
shareableId, m.getTimestamp());
}
@Override
public DeclineMessage parseDeclineMessage(Message m, BdfList body)
throws FormatException {
GroupId shareableId = new GroupId(body.getRaw(1));
byte[] b = body.getOptionalRaw(2);
MessageId previousMessageId = (b == null ? null : new MessageId(b));
return new DeclineMessage(m.getId(), m.getGroupId(), shareableId,
m.getTimestamp(), previousMessageId);
}
@Override
public LeaveMessage parseLeaveMessage(Message m, BdfList body)
throws FormatException {
GroupId shareableId = new GroupId(body.getRaw(1));
byte[] b = body.getOptionalRaw(2);
MessageId previousMessageId = (b == null ? null : new MessageId(b));
return new LeaveMessage(m.getId(), m.getGroupId(), shareableId,
m.getTimestamp(), previousMessageId);
}
@Override
public AbortMessage parseAbortMessage(Message m, BdfList body)
throws FormatException {
GroupId shareableId = new GroupId(body.getRaw(1));
byte[] b = body.getOptionalRaw(2);
MessageId previousMessageId = (b == null ? null : new MessageId(b));
return new AbortMessage(m.getId(), m.getGroupId(), shareableId,
m.getTimestamp(), previousMessageId);
}
}

View File

@@ -0,0 +1,29 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
enum MessageType {
INVITE(0), ACCEPT(1), DECLINE(2), LEAVE(3), ABORT(4);
private final int value;
MessageType(int value) {
this.value = value;
}
int getValue() {
return value;
}
static MessageType fromValue(int value) throws FormatException {
for (MessageType m : values()) if (m.value == value) return m;
throw new FormatException();
}
}

View File

@@ -0,0 +1,12 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.sharing.SharingMessage;
@Deprecated
@NotNullByDefault
interface OldInvitationFactory<I extends SharingMessage.Invitation, SS extends SharerSessionState>
extends org.briarproject.briar.api.sharing.InvitationFactory<I> {
I build(SS localState, long time);
}

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.sharing.Shareable;
import javax.annotation.Nullable;
@NotNullByDefault
interface ProtocolEngine<S extends Shareable> {
Session onInviteAction(Transaction txn, Session session,
@Nullable String message, long timestamp) throws DbException;
Session onAcceptAction(Transaction txn, Session session) throws DbException;
Session onDeclineAction(Transaction txn, Session session)
throws DbException;
Session onLeaveAction(Transaction txn, Session session) throws DbException;
Session onInviteMessage(Transaction txn, Session session,
InviteMessage<S> m) throws DbException, FormatException;
Session onAcceptMessage(Transaction txn, Session session, AcceptMessage m)
throws DbException, FormatException;
Session onDeclineMessage(Transaction txn, Session session, DeclineMessage m)
throws DbException, FormatException;
Session onLeaveMessage(Transaction txn, Session session, LeaveMessage m)
throws DbException, FormatException;
Session onAbortMessage(Transaction txn, Session session, AbortMessage m)
throws DbException, FormatException;
}

View File

@@ -0,0 +1,619 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.sharing.Shareable;
import java.util.Map;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
import static org.briarproject.briar.sharing.MessageType.ABORT;
import static org.briarproject.briar.sharing.MessageType.ACCEPT;
import static org.briarproject.briar.sharing.MessageType.DECLINE;
import static org.briarproject.briar.sharing.MessageType.INVITE;
import static org.briarproject.briar.sharing.MessageType.LEAVE;
import static org.briarproject.briar.sharing.SharingConstants.GROUP_KEY_CONTACT_ID;
import static org.briarproject.briar.sharing.State.ERROR;
import static org.briarproject.briar.sharing.State.LOCAL_INVITED;
import static org.briarproject.briar.sharing.State.LOCAL_LEFT;
import static org.briarproject.briar.sharing.State.REMOTE_HANGING;
import static org.briarproject.briar.sharing.State.REMOTE_INVITED;
import static org.briarproject.briar.sharing.State.REMOTE_LEFT;
import static org.briarproject.briar.sharing.State.SHARING;
import static org.briarproject.briar.sharing.State.START;
@Immutable
@NotNullByDefault
abstract class ProtocolEngineImpl<S extends Shareable>
implements ProtocolEngine<S> {
protected final DatabaseComponent db;
protected final ClientHelper clientHelper;
protected final MessageParser<S> messageParser;
private final MessageEncoder messageEncoder;
private final MessageTracker messageTracker;
private final Clock clock;
ProtocolEngineImpl(DatabaseComponent db, ClientHelper clientHelper,
MessageEncoder messageEncoder, MessageParser<S> messageParser,
MessageTracker messageTracker, Clock clock) {
this.db = db;
this.clientHelper = clientHelper;
this.messageEncoder = messageEncoder;
this.messageParser = messageParser;
this.messageTracker = messageTracker;
this.clock = clock;
}
@Override
public Session onInviteAction(Transaction txn, Session s,
@Nullable String message, long timestamp) throws DbException {
switch (s.getState()) {
case START:
case REMOTE_LEFT:
return onLocalInvite(txn, s, message, timestamp);
case LOCAL_INVITED:
case REMOTE_INVITED:
case SHARING:
case LOCAL_LEFT:
case REMOTE_HANGING:
case ERROR:
throw new ProtocolStateException(); // Invalid in these states
default:
throw new AssertionError();
}
}
private Session onLocalInvite(Transaction txn, Session s,
@Nullable String message, long timestamp) throws DbException {
// Send an INVITE message
Message sent = sendInviteMessage(txn, s, message, timestamp);
// Track the message
messageTracker.trackOutgoingMessage(txn, sent);
// Make the shareable visible to the contact
try {
setShareableVisibility(txn, s, VISIBLE);
} catch (FormatException e) {
throw new DbException(e); // Invalid group metadata
}
// Move to the REMOTE_INVITED state
return new Session(REMOTE_INVITED, s.getContactGroupId(),
s.getShareableId(), sent.getId(), s.getLastRemoteMessageId(),
sent.getTimestamp(), s.getInviteTimestamp());
}
private Message sendInviteMessage(Transaction txn, Session s,
@Nullable String message, long timestamp) throws DbException {
Group g = db.getGroup(txn, s.getShareableId());
BdfList descriptor;
try {
descriptor = clientHelper.toList(g.getDescriptor());
} catch (FormatException e) {
throw new DbException(e); // Invalid group descriptor
}
long localTimestamp = Math.max(timestamp, getLocalTimestamp(s));
Message m = messageEncoder
.encodeInviteMessage(s.getContactGroupId(), localTimestamp,
s.getLastLocalMessageId(), descriptor, message);
sendMessage(txn, m, INVITE, s.getShareableId(), true);
return m;
}
@Override
public Session onAcceptAction(Transaction txn, Session s)
throws DbException {
switch (s.getState()) {
case LOCAL_INVITED:
return onLocalAccept(txn, s);
case START:
case REMOTE_INVITED:
case SHARING:
case LOCAL_LEFT:
case REMOTE_LEFT:
case REMOTE_HANGING:
case ERROR:
throw new ProtocolStateException(); // Invalid in these states
default:
throw new AssertionError();
}
}
private Session onLocalAccept(Transaction txn, Session s)
throws DbException {
// Mark the invite message unavailable to answer
MessageId inviteId = s.getLastRemoteMessageId();
if (inviteId == null) throw new IllegalStateException();
markMessageAvailableToAnswer(txn, inviteId, false);
// Send a ACCEPT message
Message sent = sendAcceptMessage(txn, s);
// Track the message
messageTracker.trackOutgoingMessage(txn, sent);
try {
// Add and subscribe to the shareable
addShareable(txn, inviteId);
// Share the shareable with the contact
setShareableVisibility(txn, s, SHARED);
} catch (FormatException e) {
throw new DbException(e); // Invalid group metadata
}
// Move to the SHARING state
return new Session(SHARING, s.getContactGroupId(), s.getShareableId(),
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
s.getInviteTimestamp());
}
protected abstract void addShareable(Transaction txn, MessageId inviteId)
throws DbException, FormatException;
private Message sendAcceptMessage(Transaction txn, Session session)
throws DbException {
Message m = messageEncoder.encodeAcceptMessage(
session.getContactGroupId(), session.getShareableId(),
getLocalTimestamp(session), session.getLastLocalMessageId());
sendMessage(txn, m, ACCEPT, session.getShareableId(), true);
return m;
}
@Override
public Session onDeclineAction(Transaction txn, Session s)
throws DbException {
switch (s.getState()) {
case LOCAL_INVITED:
return onLocalDecline(txn, s);
case START:
case REMOTE_INVITED:
case SHARING:
case LOCAL_LEFT:
case REMOTE_LEFT:
case REMOTE_HANGING:
case ERROR:
throw new ProtocolStateException(); // Invalid in these states
default:
throw new AssertionError();
}
}
private Session onLocalDecline(Transaction txn, Session s)
throws DbException {
// Mark the invite message unavailable to answer
MessageId inviteId = s.getLastRemoteMessageId();
if (inviteId == null) throw new IllegalStateException();
markMessageAvailableToAnswer(txn, inviteId, false);
// Send a DECLINE message
Message sent = sendDeclineMessage(txn, s);
// Track the message
messageTracker.trackOutgoingMessage(txn, sent);
// Move to the START state
return new Session(START, s.getContactGroupId(), s.getShareableId(),
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
s.getInviteTimestamp());
}
private Message sendDeclineMessage(Transaction txn, Session session)
throws DbException {
Message m = messageEncoder.encodeDeclineMessage(
session.getContactGroupId(), session.getShareableId(),
getLocalTimestamp(session), session.getLastLocalMessageId());
sendMessage(txn, m, DECLINE, session.getShareableId(), true);
return m;
}
@Override
public Session onLeaveAction(Transaction txn, Session s)
throws DbException {
switch (s.getState()) {
case REMOTE_INVITED:
return onLocalLeave(txn, s, REMOTE_HANGING);
case SHARING:
return onLocalLeave(txn, s, LOCAL_LEFT);
case REMOTE_LEFT:
return onLocalLeave(txn, s, START);
case START:
case LOCAL_INVITED:
case LOCAL_LEFT:
case REMOTE_HANGING:
case ERROR:
return s; // Ignored in this state
default:
throw new AssertionError();
}
}
private Session onLocalLeave(Transaction txn, Session s, State nextState)
throws DbException {
try {
// Stop sharing the shareable (not actually needed in REMOTE_LEFT)
setShareableVisibility(txn, s, INVISIBLE);
} catch (FormatException e) {
throw new DbException(e); // Invalid group metadata
}
// Send a LEAVE message
Message sent = sendLeaveMessage(txn, s);
// Move to the next state
return new Session(nextState, s.getContactGroupId(), s.getShareableId(),
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
s.getInviteTimestamp());
}
private Message sendLeaveMessage(Transaction txn, Session session)
throws DbException {
Message m = messageEncoder.encodeLeaveMessage(
session.getContactGroupId(), session.getShareableId(),
getLocalTimestamp(session), session.getLastLocalMessageId());
sendMessage(txn, m, LEAVE, session.getShareableId(), false);
return m;
}
@Override
public Session onInviteMessage(Transaction txn, Session s,
InviteMessage<S> m) throws DbException, FormatException {
switch (s.getState()) {
case START:
case LOCAL_LEFT:
return onRemoteInvite(txn, s, m, true, LOCAL_INVITED);
case REMOTE_INVITED:
return onRemoteInviteWhenInvited(txn, s, m);
case REMOTE_HANGING:
return onRemoteInvite(txn, s, m, false, LOCAL_LEFT);
case LOCAL_INVITED:
case SHARING:
case REMOTE_LEFT:
return abort(txn, s); // Invalid in these states
case ERROR:
return s; // Ignored in this state
default:
throw new AssertionError();
}
}
private Session onRemoteInvite(Transaction txn, Session s,
InviteMessage<S> m, boolean available, State nextState)
throws DbException, FormatException {
// The timestamp must be higher than the last invite message, if any
if (m.getTimestamp() <= s.getInviteTimestamp()) return abort(txn, s);
// The dependency, if any, must be the last remote message
if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s);
// Mark the invite message visible in the UI and (un)available to answer
markMessageVisibleInUi(txn, m.getId(), true);
markMessageAvailableToAnswer(txn, m.getId(), available);
// Track the message
messageTracker.trackMessage(txn, m.getContactGroupId(),
m.getTimestamp(), false);
// Broadcast an event
ContactId contactId = getContactId(txn, s.getContactGroupId());
txn.attach(getInvitationRequestReceivedEvent(m, contactId, available,
false));
// Move to the next state
return new Session(nextState, s.getContactGroupId(), s.getShareableId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
m.getTimestamp());
}
private Session onRemoteInviteWhenInvited(Transaction txn, Session s,
InviteMessage<S> m) throws DbException, FormatException {
// The timestamp must be higher than the last invite message, if any
if (m.getTimestamp() <= s.getInviteTimestamp()) return abort(txn, s);
// The dependency, if any, must be the last remote message
if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s);
// Mark the invite message visible in the UI and unavailable to answer
markMessageVisibleInUi(txn, m.getId(), true);
markMessageAvailableToAnswer(txn, m.getId(), false);
// Track the message
messageTracker.trackMessage(txn, m.getContactGroupId(),
m.getTimestamp(), false);
// Share the shareable with the contact
setShareableVisibility(txn, s, SHARED);
// Broadcast an event
ContactId contactId = getContactId(txn, s.getContactGroupId());
txn.attach(
getInvitationRequestReceivedEvent(m, contactId, false, true));
// Move to the next state
return new Session(SHARING, s.getContactGroupId(), s.getShareableId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
m.getTimestamp());
}
abstract Event getInvitationRequestReceivedEvent(InviteMessage<S> m,
ContactId contactId, boolean available, boolean canBeOpened);
@Override
public Session onAcceptMessage(Transaction txn, Session s,
AcceptMessage m) throws DbException, FormatException {
switch (s.getState()) {
case REMOTE_INVITED:
return onRemoteAcceptWhenInvited(txn, s, m);
case REMOTE_HANGING:
return onRemoteAccept(txn, s, m, LOCAL_LEFT);
case START:
case LOCAL_INVITED:
case SHARING:
case LOCAL_LEFT:
case REMOTE_LEFT:
return abort(txn, s); // Invalid in these states
case ERROR:
return s; // Ignored in this state
default:
throw new AssertionError();
}
}
private Session onRemoteAccept(Transaction txn, Session s, AcceptMessage m,
State nextState) throws DbException, FormatException {
// The timestamp must be higher than the last invite message
if (m.getTimestamp() <= s.getInviteTimestamp()) return abort(txn, s);
// The dependency, if any, must be the last remote message
if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s);
// Mark the response visible in the UI
markMessageVisibleInUi(txn, m.getId(), true);
// Track the message
messageTracker.trackMessage(txn, m.getContactGroupId(),
m.getTimestamp(), false);
// Broadcast an event
ContactId contactId = getContactId(txn, m.getContactGroupId());
txn.attach(getInvitationResponseReceivedEvent(m, contactId));
// Move to the next state
return new Session(nextState, s.getContactGroupId(), s.getShareableId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
s.getInviteTimestamp());
}
private Session onRemoteAcceptWhenInvited(Transaction txn, Session s,
AcceptMessage m) throws DbException, FormatException {
// Perform normal remote accept validation and operation
Session session = onRemoteAccept(txn, s, m, SHARING);
// Share the shareable with the contact
if (session.getState() != ERROR)
setShareableVisibility(txn, s, SHARED);
return session;
}
abstract Event getInvitationResponseReceivedEvent(AcceptMessage m,
ContactId contactId);
@Override
public Session onDeclineMessage(Transaction txn, Session s,
DeclineMessage m) throws DbException, FormatException {
switch (s.getState()) {
case REMOTE_INVITED:
case REMOTE_HANGING:
return onRemoteDecline(txn, s, m);
case START:
case LOCAL_INVITED:
case SHARING:
case LOCAL_LEFT:
case REMOTE_LEFT:
return abort(txn, s); // Invalid in these states
case ERROR:
return s; // Ignored in this state
default:
throw new AssertionError();
}
}
private Session onRemoteDecline(Transaction txn, Session s,
DeclineMessage m) throws DbException, FormatException {
// The timestamp must be higher than the last invite message
if (m.getTimestamp() <= s.getInviteTimestamp()) return abort(txn, s);
// The dependency, if any, must be the last remote message
if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s);
// Mark the response visible in the UI
markMessageVisibleInUi(txn, m.getId(), true);
// Track the message
messageTracker.trackMessage(txn, m.getContactGroupId(),
m.getTimestamp(), false);
// Make the shareable invisible (not actually needed in REMOTE_HANGING)
try {
setShareableVisibility(txn, s, INVISIBLE);
} catch (FormatException e) {
throw new DbException(e); // Invalid group metadata
}
// Broadcast an event
ContactId contactId = getContactId(txn, m.getContactGroupId());
txn.attach(getInvitationResponseReceivedEvent(m, contactId));
// Move to the next state
return new Session(START, s.getContactGroupId(), s.getShareableId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
s.getInviteTimestamp());
}
abstract Event getInvitationResponseReceivedEvent(DeclineMessage m,
ContactId contactId);
@Override
public Session onLeaveMessage(Transaction txn, Session s,
LeaveMessage m) throws DbException, FormatException {
switch (s.getState()) {
case LOCAL_INVITED:
return onRemoteLeaveWhenInvited(txn, s, m, START);
case LOCAL_LEFT:
return onRemoteLeave(txn, s, m, START);
case SHARING:
return onRemoteLeaveWhenSharing(txn, s, m);
case START:
case REMOTE_INVITED:
case REMOTE_LEFT:
case REMOTE_HANGING:
return abort(txn, s); // Invalid in these states
case ERROR:
return s; // Ignored in this state
default:
throw new AssertionError();
}
}
private Session onRemoteLeaveWhenInvited(Transaction txn, Session s,
LeaveMessage m, State nextState)
throws DbException, FormatException {
// Carry out normal leave validation and operation
Session session = onRemoteLeave(txn, s, m, nextState);
// Mark any invite messages in the session unavailable to answer
if (session.getState() != ERROR)
markInvitesUnavailableToAnswer(txn, s);
// Move to the next state
return session;
}
private Session onRemoteLeave(Transaction txn, Session s,
LeaveMessage m, State nextState)
throws DbException, FormatException {
// The dependency, if any, must be the last remote message
if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s);
// Move to the next state
return new Session(nextState, s.getContactGroupId(), s.getShareableId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
s.getInviteTimestamp());
}
private Session onRemoteLeaveWhenSharing(Transaction txn, Session s,
LeaveMessage m) throws DbException, FormatException {
// Carry out normal leave validation and operation
Session session = onRemoteLeave(txn, s, m, REMOTE_LEFT);
// Stop sharing the shareable with the contact
if (session.getState() != ERROR)
setShareableVisibility(txn, s, INVISIBLE);
// Move to the next state
return session;
}
@Override
public Session onAbortMessage(Transaction txn, Session s, AbortMessage m)
throws DbException, FormatException {
return abort(txn, s);
}
private Session abort(Transaction txn, Session s)
throws DbException, FormatException {
// If the session has already been aborted, do nothing
if (s.getState() == ERROR) return s;
// Mark any invite messages in the session unavailable to answer
markInvitesUnavailableToAnswer(txn, s);
// If we subscribe, make the shareable invisible to the contact
if (isSubscribed(txn, s.getShareableId()))
setShareableVisibility(txn, s, INVISIBLE);
// Send an ABORT message
Message sent = sendAbortMessage(txn, s);
// Move to the ERROR state
return new Session(ERROR, s.getContactGroupId(), s.getShareableId(),
sent.getId(), s.getLastRemoteMessageId(), sent.getTimestamp(),
s.getInviteTimestamp());
}
private void markInvitesUnavailableToAnswer(Transaction txn, Session s)
throws DbException, FormatException {
GroupId shareableId = s.getShareableId();
BdfDictionary query =
messageParser.getInvitesAvailableToAnswerQuery(shareableId);
Map<MessageId, BdfDictionary> results =
clientHelper.getMessageMetadataAsDictionary(txn,
s.getContactGroupId(), query);
for (MessageId m : results.keySet())
markMessageAvailableToAnswer(txn, m, false);
}
private boolean isSubscribed(Transaction txn, GroupId g)
throws DbException {
if (!db.containsGroup(txn, g)) return false;
Group group = db.getGroup(txn, g);
return group.getClientId().equals(getClientId());
}
protected abstract ClientId getClientId();
private Message sendAbortMessage(Transaction txn, Session session)
throws DbException {
Message m = messageEncoder.encodeAbortMessage(
session.getContactGroupId(), session.getShareableId(),
getLocalTimestamp(session), session.getLastLocalMessageId());
sendMessage(txn, m, ABORT, session.getShareableId(), false);
return m;
}
private void sendMessage(Transaction txn, Message m, MessageType type,
GroupId shareableId, boolean visibleInConversation)
throws DbException {
BdfDictionary meta = messageEncoder.encodeMetadata(type, shareableId,
m.getTimestamp(), true, true, visibleInConversation, false);
try {
clientHelper.addLocalMessage(txn, m, meta, true);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
private void markMessageAvailableToAnswer(Transaction txn, MessageId m,
boolean available) throws DbException {
BdfDictionary meta = new BdfDictionary();
messageEncoder.setAvailableToAnswer(meta, available);
try {
clientHelper.mergeMessageMetadata(txn, m, meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
private void markMessageVisibleInUi(Transaction txn, MessageId m,
boolean visible) throws DbException {
BdfDictionary meta = new BdfDictionary();
messageEncoder.setVisibleInUi(meta, visible);
try {
clientHelper.mergeMessageMetadata(txn, m, meta);
} catch (FormatException e) {
throw new AssertionError(e);
}
}
private void setShareableVisibility(Transaction txn, Session session,
Visibility v) throws DbException, FormatException {
ContactId contactId = getContactId(txn, session.getContactGroupId());
db.setGroupVisibility(txn, contactId, session.getShareableId(), v);
}
private ContactId getContactId(Transaction txn, GroupId contactGroupId)
throws DbException, FormatException {
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn,
contactGroupId);
return new ContactId(meta.getLong(GROUP_KEY_CONTACT_ID).intValue());
}
private boolean isValidDependency(Session session,
@Nullable MessageId dependency) {
MessageId expected = session.getLastRemoteMessageId();
if (dependency == null) return expected == null;
return expected != null && dependency.equals(expected);
}
private long getLocalTimestamp(Session session) {
return Math.max(clock.currentTimeMillis(),
Math.max(session.getLocalTimestamp(),
session.getInviteTimestamp()) + 1);
}
}

View File

@@ -0,0 +1,69 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.sharing.State.START;
@Immutable
@NotNullByDefault
class Session {
private final State state;
private final GroupId contactGroupId, shareableId;
@Nullable
private final MessageId lastLocalMessageId, lastRemoteMessageId;
private final long localTimestamp, inviteTimestamp;
Session(State state, GroupId contactGroupId, GroupId shareableId,
@Nullable MessageId lastLocalMessageId,
@Nullable MessageId lastRemoteMessageId, long localTimestamp,
long inviteTimestamp) {
this.state = state;
this.contactGroupId = contactGroupId;
this.shareableId = shareableId;
this.lastLocalMessageId = lastLocalMessageId;
this.lastRemoteMessageId = lastRemoteMessageId;
this.localTimestamp = localTimestamp;
this.inviteTimestamp = inviteTimestamp;
}
Session(GroupId contactGroupId, GroupId shareableId) {
this(START, contactGroupId, shareableId, null, null, 0, 0);
}
public State getState() {
return state;
}
GroupId getContactGroupId() {
return contactGroupId;
}
GroupId getShareableId() {
return shareableId;
}
@Nullable
MessageId getLastLocalMessageId() {
return lastLocalMessageId;
}
@Nullable
MessageId getLastRemoteMessageId() {
return lastRemoteMessageId;
}
long getLocalTimestamp() {
return localTimestamp;
}
long getInviteTimestamp() {
return inviteTimestamp;
}
}

View File

@@ -0,0 +1,11 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
interface SessionEncoder {
BdfDictionary encodeSession(Session s);
}

View File

@@ -0,0 +1,46 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_INVITE_TIMESTAMP;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LOCAL_TIMESTAMP;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_SESSION_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_SHAREABLE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_STATE;
@Immutable
@NotNullByDefault
class SessionEncoderImpl implements SessionEncoder {
@Inject
SessionEncoderImpl() {
}
@Override
public BdfDictionary encodeSession(Session s) {
BdfDictionary d = new BdfDictionary();
d.put(SESSION_KEY_SESSION_ID, s.getShareableId());
d.put(SESSION_KEY_SHAREABLE_ID, s.getShareableId());
MessageId lastLocalMessageId = s.getLastLocalMessageId();
if (lastLocalMessageId == null)
d.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, NULL_VALUE);
else d.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, lastLocalMessageId);
MessageId lastRemoteMessageId = s.getLastRemoteMessageId();
if (lastRemoteMessageId == null)
d.put(SESSION_KEY_LAST_REMOTE_MESSAGE_ID, NULL_VALUE);
else d.put(SESSION_KEY_LAST_REMOTE_MESSAGE_ID, lastRemoteMessageId);
d.put(SESSION_KEY_LOCAL_TIMESTAMP, s.getLocalTimestamp());
d.put(SESSION_KEY_INVITE_TIMESTAMP, s.getInviteTimestamp());
d.put(SESSION_KEY_STATE, s.getState().getValue());
return d;
}
}

View File

@@ -0,0 +1,17 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.client.SessionId;
@NotNullByDefault
interface SessionParser {
BdfDictionary getSessionQuery(SessionId s);
Session parseSession(GroupId contactGroupId, BdfDictionary d)
throws FormatException;
}

View File

@@ -0,0 +1,75 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_INVITE_TIMESTAMP;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LOCAL_TIMESTAMP;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_SESSION_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_SHAREABLE_ID;
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_STATE;
@Immutable
@NotNullByDefault
class SessionParserImpl implements SessionParser {
@Inject
SessionParserImpl() {
}
@Override
public BdfDictionary getSessionQuery(SessionId s) {
return BdfDictionary.of(new BdfEntry(SESSION_KEY_SESSION_ID, s));
}
@Override
public Session parseSession(GroupId contactGroupId,
BdfDictionary d) throws FormatException {
return new Session(State.fromValue(getState(d)), contactGroupId,
getShareableId(d), getLastLocalMessageId(d),
getLastRemoteMessageId(d), getLocalTimestamp(d),
getInviteTimestamp(d));
}
private int getState(BdfDictionary d) throws FormatException {
return d.getLong(SESSION_KEY_STATE).intValue();
}
private GroupId getShareableId(BdfDictionary d) throws FormatException {
return new GroupId(d.getRaw(SESSION_KEY_SHAREABLE_ID));
}
@Nullable
private MessageId getLastLocalMessageId(BdfDictionary d)
throws FormatException {
byte[] b = d.getOptionalRaw(SESSION_KEY_LAST_LOCAL_MESSAGE_ID);
return b == null ? null : new MessageId(b);
}
@Nullable
private MessageId getLastRemoteMessageId(BdfDictionary d)
throws FormatException {
byte[] b = d.getOptionalRaw(SESSION_KEY_LAST_REMOTE_MESSAGE_ID);
return b == null ? null : new MessageId(b);
}
private long getLocalTimestamp(BdfDictionary d) throws FormatException {
return d.getLong(SESSION_KEY_LOCAL_TIMESTAMP);
}
private long getInviteTimestamp(BdfDictionary d) throws FormatException {
return d.getLong(SESSION_KEY_INVITE_TIMESTAMP);
}
}

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.sharing.Shareable; import org.briarproject.briar.api.sharing.Shareable;
import org.briarproject.briar.api.sharing.SharingMessage; import org.briarproject.briar.api.sharing.SharingMessage;
@Deprecated
@NotNullByDefault @NotNullByDefault
interface ShareableFactory<S extends Shareable, I extends SharingMessage.Invitation, IS extends InviteeSessionState, SS extends SharerSessionState> { interface ShareableFactory<S extends Shareable, I extends SharingMessage.Invitation, IS extends InviteeSessionState, SS extends SharerSessionState> {

View File

@@ -29,6 +29,7 @@ import static org.briarproject.briar.api.sharing.SharingMessage.SimpleMessage;
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_ACCEPT; import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_ACCEPT;
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_DECLINE; import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_DECLINE;
@Deprecated
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent>
@@ -37,12 +38,12 @@ class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR exte
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(SharerEngine.class.getName()); Logger.getLogger(SharerEngine.class.getName());
private final InvitationFactory<I, SS> invitationFactory; private final OldInvitationFactory<I, SS> invitationFactory;
private final InvitationResponseReceivedEventFactory<SS, IRR> private final InvitationResponseReceivedEventFactory<SS, IRR>
invitationResponseReceivedEventFactory; invitationResponseReceivedEventFactory;
private final Clock clock; private final Clock clock;
SharerEngine(InvitationFactory<I, SS> invitationFactory, SharerEngine(OldInvitationFactory<I, SS> invitationFactory,
InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory, InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory,
Clock clock) { Clock clock) {
this.invitationFactory = invitationFactory; this.invitationFactory = invitationFactory;

View File

@@ -23,6 +23,7 @@ import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_AC
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_DECLINE; import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_DECLINE;
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_LEAVE; import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_LEAVE;
@Deprecated
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
public abstract class SharerSessionState extends SharingSessionState { public abstract class SharerSessionState extends SharingSessionState {

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.sharing.Shareable; import org.briarproject.briar.api.sharing.Shareable;
@Deprecated
@NotNullByDefault @NotNullByDefault
interface SharerSessionStateFactory<S extends Shareable, SS extends SharerSessionState> { interface SharerSessionStateFactory<S extends Shareable, SS extends SharerSessionState> {

View File

@@ -0,0 +1,28 @@
package org.briarproject.briar.sharing;
import org.briarproject.briar.client.MessageTrackerConstants;
interface SharingConstants {
// Group metadata keys
String GROUP_KEY_CONTACT_ID = "contactId";
// Message metadata keys
String MSG_KEY_MESSAGE_TYPE = "messageType";
String MSG_KEY_SHAREABLE_ID = "shareableId";
String MSG_KEY_TIMESTAMP = "timestamp";
String MSG_KEY_READ = MessageTrackerConstants.MSG_KEY_READ;
String MSG_KEY_LOCAL = "local";
String MSG_KEY_VISIBLE_IN_UI = "visibleInUi";
String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer";
// Session keys
String SESSION_KEY_STATE = "state";
String SESSION_KEY_SESSION_ID = "sessionId";
String SESSION_KEY_SHAREABLE_ID = "shareableId";
String SESSION_KEY_LAST_LOCAL_MESSAGE_ID = "lastLocalMessageId";
String SESSION_KEY_LAST_REMOTE_MESSAGE_ID = "lastRemoteMessageId";
String SESSION_KEY_LOCAL_TIMESTAMP = "localTimestamp";
String SESSION_KEY_INVITE_TIMESTAMP = "inviteTimestamp";
}

View File

@@ -0,0 +1,50 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
abstract class SharingMessage {
private final MessageId id;
private final GroupId contactGroupId, shareableId;
private final long timestamp;
@Nullable
private final MessageId previousMessageId;
SharingMessage(MessageId id, GroupId contactGroupId, GroupId shareableId,
long timestamp, @Nullable MessageId previousMessageId) {
this.id = id;
this.previousMessageId = previousMessageId;
this.contactGroupId = contactGroupId;
this.shareableId = shareableId;
this.timestamp = timestamp;
}
MessageId getId() {
return id;
}
GroupId getContactGroupId() {
return contactGroupId;
}
GroupId getShareableId() {
return shareableId;
}
long getTimestamp() {
return timestamp;
}
@Nullable
public MessageId getPreviousMessageId() {
return previousMessageId;
}
}

View File

@@ -4,10 +4,13 @@ import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.MetadataEncoder; import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.client.MessageQueueManager; import org.briarproject.briar.api.client.MessageQueueManager;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumFactory;
import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.messaging.ConversationManager; import org.briarproject.briar.api.messaging.ConversationManager;
@@ -68,14 +71,15 @@ public class SharingModule {
@Provides @Provides
@Singleton @Singleton
ForumSharingValidator provideForumSharingValidator( ForumSharingValidator provideForumSharingValidator(
MessageQueueManager messageQueueManager, ClientHelper clientHelper, ValidationManager validationManager, MessageEncoder messageEncoder,
MetadataEncoder metadataEncoder, Clock clock) { ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock, ForumFactory forumFactory) {
ForumSharingValidator validator = ForumSharingValidator validator =
new ForumSharingValidator(clientHelper, metadataEncoder, clock); new ForumSharingValidator(messageEncoder, clientHelper,
messageQueueManager.registerMessageValidator( metadataEncoder, clock, forumFactory);
ForumSharingManager.CLIENT_ID, validator); validationManager
.registerMessageValidator(ForumSharingManager.CLIENT_ID,
validator);
return validator; return validator;
} }
@@ -83,14 +87,14 @@ public class SharingModule {
@Singleton @Singleton
ForumSharingManager provideForumSharingManager( ForumSharingManager provideForumSharingManager(
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
MessageQueueManager messageQueueManager, ValidationManager validationManager,
ConversationManager conversationManager, ForumManager forumManager, ConversationManager conversationManager, ForumManager forumManager,
ForumSharingManagerImpl forumSharingManager) { ForumSharingManagerImpl forumSharingManager) {
lifecycleManager.registerClient(forumSharingManager); lifecycleManager.registerClient(forumSharingManager);
contactManager.registerAddContactHook(forumSharingManager); contactManager.registerAddContactHook(forumSharingManager);
contactManager.registerRemoveContactHook(forumSharingManager); contactManager.registerRemoveContactHook(forumSharingManager);
messageQueueManager.registerIncomingMessageHook( validationManager.registerIncomingMessageHook(
ForumSharingManager.CLIENT_ID, forumSharingManager); ForumSharingManager.CLIENT_ID, forumSharingManager);
conversationManager.registerConversationClient(forumSharingManager); conversationManager.registerConversationClient(forumSharingManager);
forumManager.registerRemoveForumHook(forumSharingManager); forumManager.registerRemoveForumHook(forumSharingManager);
@@ -98,4 +102,37 @@ public class SharingModule {
return forumSharingManager; return forumSharingManager;
} }
@Provides
MessageEncoder provideMessageEncoder(MessageEncoderImpl messageEncoder) {
return messageEncoder;
}
@Provides
MessageParser<Forum> provideForumMessageParser(
ForumMessageParserImpl forumMessageParser) {
return forumMessageParser;
}
@Provides
SessionEncoder provideSessionEncoder(SessionEncoderImpl sessionEncoder) {
return sessionEncoder;
}
@Provides
SessionParser provideSessionParser(SessionParserImpl sessionParser) {
return sessionParser;
}
@Provides
ProtocolEngine<Forum> provideForumProtocolEngine(
ForumProtocolEngineImpl forumProtocolEngine) {
return forumProtocolEngine;
}
@Provides
InvitationFactory<Forum> provideForumInvitationFactory(
ForumInvitationFactoryImpl forumInvitationFactory) {
return forumInvitationFactory;
}
} }

View File

@@ -18,6 +18,7 @@ import static org.briarproject.briar.api.sharing.SharingConstants.SHAREABLE_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.STATE; import static org.briarproject.briar.api.sharing.SharingConstants.STATE;
import static org.briarproject.briar.api.sharing.SharingConstants.STORAGE_ID; import static org.briarproject.briar.api.sharing.SharingConstants.STORAGE_ID;
@Deprecated
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
abstract class SharingSessionState { abstract class SharingSessionState {

View File

@@ -0,0 +1,101 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.client.BdfMessageValidator;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import java.util.Collections;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
import static org.briarproject.briar.sharing.MessageType.INVITE;
@Immutable
@NotNullByDefault
abstract class SharingValidator extends BdfMessageValidator {
private final MessageEncoder messageEncoder;
SharingValidator(MessageEncoder messageEncoder, ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock) {
super(clientHelper, metadataEncoder, clock);
this.messageEncoder = messageEncoder;
}
@Override
protected BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws FormatException {
MessageType type = MessageType.fromValue(body.getLong(0).intValue());
switch (type) {
case INVITE:
return validateInviteMessage(m, body);
case ACCEPT:
case DECLINE:
case LEAVE:
case ABORT:
return validateNonInviteMessage(type, m, body);
default:
throw new FormatException();
}
}
private BdfMessageContext validateInviteMessage(Message m, BdfList body)
throws FormatException {
checkSize(body, 4);
byte[] previousMessageId = body.getOptionalRaw(1);
checkLength(previousMessageId, UniqueId.LENGTH);
BdfList descriptor = body.getList(2);
GroupId shareableId = validateDescriptor(descriptor);
String msg = body.getOptionalString(3);
checkLength(msg, 1, MAX_INVITATION_MESSAGE_LENGTH);
BdfDictionary meta = messageEncoder
.encodeMetadata(INVITE, shareableId, m.getTimestamp(), false,
false, false, false);
if (previousMessageId == null) {
return new BdfMessageContext(meta);
} else {
MessageId dependency = new MessageId(previousMessageId);
return new BdfMessageContext(meta,
Collections.singletonList(dependency));
}
}
protected abstract GroupId validateDescriptor(BdfList descriptor)
throws FormatException;
private BdfMessageContext validateNonInviteMessage(MessageType type,
Message m, BdfList body) throws FormatException {
checkSize(body, 3);
byte[] shareableId = body.getRaw(1);
checkLength(shareableId, UniqueId.LENGTH);
byte[] previousMessageId = body.getOptionalRaw(2);
checkLength(previousMessageId, UniqueId.LENGTH);
BdfDictionary meta = messageEncoder
.encodeMetadata(type, new GroupId(shareableId),
m.getTimestamp(), false, false, false, false);
if (previousMessageId == null) {
return new BdfMessageContext(meta);
} else {
MessageId dependency = new MessageId(previousMessageId);
return new BdfMessageContext(meta,
Collections.singletonList(dependency));
}
}
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
enum State {
START(0), LOCAL_INVITED(1), REMOTE_INVITED(2), SHARING(3), LOCAL_LEFT(4),
REMOTE_LEFT(5), REMOTE_HANGING(6), ERROR(7);
private final int value;
State(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public boolean canInvite() {
return this == START || this == REMOTE_LEFT;
}
static State fromValue(int value) throws FormatException {
for (State s : values()) if (s.value == value) return s;
throw new FormatException();
}
}

View File

@@ -45,7 +45,8 @@ public class ForumManagerTest
forum0 = forumManager0.addForum("Test Forum"); forum0 = forumManager0.addForum("Test Forum");
groupId0 = forum0.getId(); groupId0 = forum0.getId();
// share forum // share forum
forumSharingManager0.sendInvitation(groupId0, contactId1From0, null); forumSharingManager0.sendInvitation(groupId0, contactId1From0, null,
clock.currentTimeMillis());
sync0To1(1, true); sync0To1(1, true);
forumSharingManager1.respondToInvitation(forum0, contact0From1, true); forumSharingManager1.respondToInvitation(forum0, contact0From1, true);
sync1To0(1, true); sync1To0(1, true);
@@ -189,7 +190,8 @@ public class ForumManagerTest
// share a second forum // share a second forum
Forum forum1 = forumManager0.addForum("Test Forum1"); Forum forum1 = forumManager0.addForum("Test Forum1");
GroupId g1 = forum1.getId(); GroupId g1 = forum1.getId();
forumSharingManager0.sendInvitation(g1, contactId1From0, null); forumSharingManager0.sendInvitation(g1, contactId1From0, null,
clock.currentTimeMillis());
sync0To1(1, true); sync0To1(1, true);
forumSharingManager1.respondToInvitation(forum1, contact0From1, true); forumSharingManager1.respondToInvitation(forum1, contact0From1, true);
sync1To0(1, true); sync1To0(1, true);

View File

@@ -12,6 +12,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse;
import org.briarproject.briar.api.sharing.InvitationMessage; import org.briarproject.briar.api.sharing.InvitationMessage;
import org.briarproject.briar.api.sharing.InvitationResponse;
import org.briarproject.briar.test.BriarIntegrationTest; import org.briarproject.briar.test.BriarIntegrationTest;
import org.briarproject.briar.test.BriarIntegrationTestComponent; import org.briarproject.briar.test.BriarIntegrationTestComponent;
import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent; import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent;
@@ -97,10 +98,10 @@ public class GroupInvitationIntegrationTest
GroupInvitationRequest request = GroupInvitationRequest request =
(GroupInvitationRequest) messages.iterator().next(); (GroupInvitationRequest) messages.iterator().next();
assertEquals(msg, request.getMessage()); assertEquals(msg, request.getMessage());
assertEquals(author0, request.getCreator()); assertEquals(author0, request.getShareable().getCreator());
assertEquals(timestamp, request.getTimestamp()); assertEquals(timestamp, request.getTimestamp());
assertEquals(contactId0From1, request.getContactId()); assertEquals(contactId0From1, request.getContactId());
assertEquals(privateGroup0.getName(), request.getGroupName()); assertEquals(privateGroup0.getName(), request.getShareable().getName());
assertFalse(request.isLocal()); assertFalse(request.isLocal());
assertFalse(request.isRead()); assertFalse(request.isRead());
} }
@@ -123,7 +124,7 @@ public class GroupInvitationIntegrationTest
for (InvitationMessage m : messages) { for (InvitationMessage m : messages) {
if (m instanceof GroupInvitationResponse) { if (m instanceof GroupInvitationResponse) {
foundResponse = true; foundResponse = true;
GroupInvitationResponse response = (GroupInvitationResponse) m; InvitationResponse response = (GroupInvitationResponse) m;
assertEquals(contactId0From1, response.getContactId()); assertEquals(contactId0From1, response.getContactId());
assertTrue(response.isLocal()); assertTrue(response.isLocal());
assertFalse(response.wasAccepted()); assertFalse(response.wasAccepted());
@@ -140,7 +141,7 @@ public class GroupInvitationIntegrationTest
for (InvitationMessage m : messages) { for (InvitationMessage m : messages) {
if (m instanceof GroupInvitationResponse) { if (m instanceof GroupInvitationResponse) {
foundResponse = true; foundResponse = true;
GroupInvitationResponse response = (GroupInvitationResponse) m; InvitationResponse response = (GroupInvitationResponse) m;
assertEquals(contactId0From1, response.getContactId()); assertEquals(contactId0From1, response.getContactId());
assertFalse(response.isLocal()); assertFalse(response.isLocal());
assertFalse(response.wasAccepted()); assertFalse(response.wasAccepted());
@@ -172,7 +173,7 @@ public class GroupInvitationIntegrationTest
for (InvitationMessage m : messages) { for (InvitationMessage m : messages) {
if (m instanceof GroupInvitationResponse) { if (m instanceof GroupInvitationResponse) {
foundResponse = true; foundResponse = true;
GroupInvitationResponse response = (GroupInvitationResponse) m; InvitationResponse response = (GroupInvitationResponse) m;
assertTrue(response.wasAccepted()); assertTrue(response.wasAccepted());
} }
} }
@@ -187,7 +188,7 @@ public class GroupInvitationIntegrationTest
for (InvitationMessage m : messages) { for (InvitationMessage m : messages) {
if (m instanceof GroupInvitationResponse) { if (m instanceof GroupInvitationResponse) {
foundResponse = true; foundResponse = true;
GroupInvitationResponse response = (GroupInvitationResponse) m; InvitationResponse response = (GroupInvitationResponse) m;
assertTrue(response.wasAccepted()); assertTrue(response.wasAccepted());
} }
} }

View File

@@ -90,8 +90,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
private final GroupInvitationManagerImpl groupInvitationManager; private final GroupInvitationManagerImpl groupInvitationManager;
private final Group localGroup =
new Group(new GroupId(getRandomId()), CLIENT_ID, getRandomBytes(5));
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final ContactId contactId = new ContactId(0); private final ContactId contactId = new ContactId(0);
private final Author author = private final Author author =
@@ -141,8 +139,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
will(returnValue(inviteeEngine)); will(returnValue(inviteeEngine));
oneOf(engineFactory).createPeerEngine(); oneOf(engineFactory).createPeerEngine();
will(returnValue(peerEngine)); will(returnValue(peerEngine));
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID);
will(returnValue(localGroup));
}}); }});
MetadataParser metadataParser = context.mock(MetadataParser.class); MetadataParser metadataParser = context.mock(MetadataParser.class);
MessageTracker messageTracker = context.mock(MessageTracker.class); MessageTracker messageTracker = context.mock(MessageTracker.class);
@@ -156,7 +152,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testCreateLocalState() throws Exception { public void testCreateLocalState() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).addGroup(txn, localGroup);
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(Collections.singletonList(contact))); will(returnValue(Collections.singletonList(contact)));
}}); }});
@@ -651,6 +646,9 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
new InviteMessage(message.getId(), contactGroup.getId(), new InviteMessage(message.getId(), contactGroup.getId(),
privateGroup.getId(), time1, "name", author, privateGroup.getId(), time1, "name", author,
new byte[0], null, new byte[0]); new byte[0], null, new byte[0]);
final PrivateGroup pg =
new PrivateGroup(privateGroup, invite.getGroupName(),
invite.getCreator(), invite.getSalt());
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
@@ -668,12 +666,11 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
oneOf(messageParser).parseMetadata(meta); oneOf(messageParser).parseMetadata(meta);
will(returnValue(messageMetadata1)); will(returnValue(messageMetadata1));
oneOf(db).getMessageStatus(txn, contactId, message.getId()); oneOf(db).getMessageStatus(txn, contactId, message.getId());
oneOf(clientHelper).getMessage(txn, message.getId()); oneOf(messageParser).getInviteMessage(txn, message.getId());
will(returnValue(message));
oneOf(clientHelper).toList(message);
will(returnValue(body));
oneOf(messageParser).parseInviteMessage(message, body);
will(returnValue(invite)); will(returnValue(invite));
oneOf(privateGroupFactory).createPrivateGroup(invite.getGroupName(),
invite.getCreator(), invite.getSalt());
will(returnValue(pg));
oneOf(db).containsGroup(txn, privateGroup.getId()); oneOf(db).containsGroup(txn, privateGroup.getId());
will(returnValue(true)); will(returnValue(true));
// second message // second message
@@ -742,21 +739,13 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
contactGroup.getId(), query); contactGroup.getId(), query);
will(returnValue(results)); will(returnValue(results));
// message 1 // message 1
oneOf(clientHelper).getMessage(txn, message.getId()); oneOf(messageParser).getInviteMessage(txn, message.getId());
will(returnValue(message));
oneOf(clientHelper).toList(message);
will(returnValue(body));
oneOf(messageParser).parseInviteMessage(message, body);
will(returnValue(inviteMessage1)); will(returnValue(inviteMessage1));
oneOf(privateGroupFactory).createPrivateGroup(groupName, author, oneOf(privateGroupFactory).createPrivateGroup(groupName, author,
salt); salt);
will(returnValue(pg)); will(returnValue(pg));
// message 2 // message 2
oneOf(clientHelper).getMessage(txn, messageId2); oneOf(messageParser).getInviteMessage(txn, messageId2);
will(returnValue(message2));
oneOf(clientHelper).toList(message2);
will(returnValue(body2));
oneOf(messageParser).parseInviteMessage(message2, body2);
will(returnValue(inviteMessage2)); will(returnValue(inviteMessage2));
oneOf(privateGroupFactory).createPrivateGroup(groupName, author, oneOf(privateGroupFactory).createPrivateGroup(groupName, author,
salt); salt);

View File

@@ -144,11 +144,7 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
expectSendJoinMessage(properJoinMessage, true); expectSendJoinMessage(properJoinMessage, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(messageTracker).trackOutgoingMessage(txn, message); oneOf(messageTracker).trackOutgoingMessage(txn, message);
oneOf(clientHelper).getMessage(txn, lastRemoteMessageId); oneOf(messageParser).getInviteMessage(txn, lastRemoteMessageId);
will(returnValue(inviteMsg));
oneOf(clientHelper).toList(inviteMsg);
will(returnValue(inviteList));
oneOf(messageParser).parseInviteMessage(inviteMsg, inviteList);
will(returnValue(inviteMessage)); will(returnValue(inviteMessage));
oneOf(privateGroupFactory) oneOf(privateGroupFactory)
.createPrivateGroup(inviteMessage.getGroupName(), .createPrivateGroup(inviteMessage.getGroupName(),

View File

@@ -107,7 +107,8 @@ public class BlogSharingIntegrationTest
// create invitation // create invitation
blogSharingManager0 blogSharingManager0
.sendInvitation(blog1.getId(), contactId1From0, "Hi!"); .sendInvitation(blog1.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync invitation // sync invitation
sync0To1(1, false); sync0To1(1, false);
@@ -122,7 +123,8 @@ public class BlogSharingIntegrationTest
// send invitation // send invitation
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!"); .sendInvitation(blog2.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// invitee has own blog and that of the sharer // invitee has own blog and that of the sharer
assertEquals(2, blogManager1.getBlogs().size()); assertEquals(2, blogManager1.getBlogs().size());
@@ -194,7 +196,8 @@ public class BlogSharingIntegrationTest
// send invitation // send invitation
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, null); .sendInvitation(blog2.getId(), contactId1From0, null,
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -251,7 +254,8 @@ public class BlogSharingIntegrationTest
// send invitation // send invitation
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!"); .sendInvitation(blog2.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -313,7 +317,8 @@ public class BlogSharingIntegrationTest
// sharer sends invitation for 2's blog to 1 // sharer sends invitation for 2's blog to 1
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!"); .sendInvitation(blog2.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -349,7 +354,8 @@ public class BlogSharingIntegrationTest
// send invitation // send invitation
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!"); .sendInvitation(blog2.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -399,7 +405,8 @@ public class BlogSharingIntegrationTest
// sharer sends invitation for 2's blog to 1 // sharer sends invitation for 2's blog to 1
blogSharingManager0 blogSharingManager0
.sendInvitation(blog2.getId(), contactId1From0, "Hi!"); .sendInvitation(blog2.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);

View File

@@ -2,19 +2,14 @@ package org.briarproject.briar.sharing;
import net.jodah.concurrentunit.Waiter; import net.jodah.concurrentunit.Waiter;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.test.TestDatabaseModule; import org.briarproject.bramble.test.TestDatabaseModule;
import org.briarproject.briar.api.client.MessageQueueManager; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumInvitationRequest; import org.briarproject.briar.api.forum.ForumInvitationRequest;
import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumInvitationResponse;
@@ -39,11 +34,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNotNull;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomString; import static org.briarproject.bramble.test.TestUtils.getRandomString;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.briar.api.forum.ForumSharingManager.CLIENT_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -115,7 +106,8 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!"); .sendInvitation(forum0.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -171,7 +163,8 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, null); .sendInvitation(forum0.getId(), contactId1From0, null,
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -227,7 +220,8 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!"); .sendInvitation(forum0.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -245,9 +239,8 @@ public class ForumSharingIntegrationTest
assertTrue(forumManager1.getForums().contains(forum0)); assertTrue(forumManager1.getForums().contains(forum0));
// sharer shares forum with invitee // sharer shares forum with invitee
Contact c1 = contactManager0.getContact(contactId1From0);
assertTrue(forumSharingManager0.getSharedWith(forum0.getId()) assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
.contains(c1)); .contains(contact1From0));
// invitee gets forum shared by sharer // invitee gets forum shared by sharer
Contact contact0 = contactManager1.getContact(contactId1From0); Contact contact0 = contactManager1.getContact(contactId1From0);
assertTrue(forumSharingManager1.getSharedWith(forum0.getId()) assertTrue(forumSharingManager1.getSharedWith(forum0.getId())
@@ -265,14 +258,16 @@ public class ForumSharingIntegrationTest
// sharer no longer shares forum with invitee // sharer no longer shares forum with invitee
assertFalse(forumSharingManager0.getSharedWith(forum0.getId()) assertFalse(forumSharingManager0.getSharedWith(forum0.getId())
.contains(c1)); .contains(contact1From0));
// invitee no longer gets forum shared by sharer // invitee no longer gets forum shared by sharer
assertFalse(forumSharingManager1.getSharedWith(forum0.getId()) assertFalse(forumSharingManager1.getSharedWith(forum0.getId())
.contains(contact0)); .contains(contact0));
// forum can be shared again // forum can be shared again by sharer
assertTrue(forumSharingManager0.canBeShared(forum0.getId(), c1)); assertTrue(forumSharingManager0
Contact c0 = contactManager1.getContact(contactId0From1); .canBeShared(forum0.getId(), contact1From0));
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0)); // invitee that left can not share again
assertFalse(forumSharingManager1
.canBeShared(forum0.getId(), contact0From1));
} }
@Test @Test
@@ -282,7 +277,8 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, null); .sendInvitation(forum0.getId(), contactId1From0, null,
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -336,7 +332,8 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, null); .sendInvitation(forum0.getId(), contactId1From0, null,
clock.currentTimeMillis());
// sharer un-subscribes from forum // sharer un-subscribes from forum
forumManager0.removeForum(forum0); forumManager0.removeForum(forum0);
@@ -360,7 +357,8 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, null); .sendInvitation(forum0.getId(), contactId1From0, null,
clock.currentTimeMillis());
// sharer un-subscribes from forum // sharer un-subscribes from forum
forumManager0.removeForum(forum0); forumManager0.removeForum(forum0);
@@ -375,73 +373,15 @@ public class ForumSharingIntegrationTest
assertEquals(1, forumManager1.getForums().size()); assertEquals(1, forumManager1.getForums().size());
} }
@Test @Test(expected = ProtocolStateException.class)
public void testSessionIdReuse() throws Exception {
// initialize and let invitee accept all requests
listenToEvents(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
// sync first request message
sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
sync1To0(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(1, forumManager1.getForums().size());
// reset event received state
listener1.requestReceived = false;
// get SessionId from invitation
List<InvitationMessage> list = new ArrayList<InvitationMessage>(
forumSharingManager1
.getInvitationMessages(contactId0From1));
assertEquals(2, list.size());
InvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId();
assertEquals(sessionId, list.get(1).getSessionId());
// get all sorts of stuff needed to send a message
MessageQueueManager queue = c0.getMessageQueueManager();
Contact c1 = contactManager0.getContact(contactId1From0);
Group group = contactGroupFactory.createContactGroup(CLIENT_ID, c1);
long time = clock.currentTimeMillis();
BdfList bodyList =
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId.getBytes(),
getRandomString(42), getRandomBytes(FORUM_SALT_LENGTH));
byte[] body = clientHelper.toByteArray(bodyList);
// add the message to the queue
Transaction txn = db0.startTransaction(false);
try {
queue.sendMessage(txn, group, time, body, new Metadata());
db0.commitTransaction(txn);
} finally {
db0.endTransaction(txn);
}
// actually send the message
sync0To1(1, false);
// make sure there was no new request received
assertFalse(listener1.requestReceived);
}
@Test
public void testSharingSameForumWithEachOther() throws Exception { public void testSharingSameForumWithEachOther() throws Exception {
// initialize and let invitee accept all requests // initialize and let invitee accept all requests
listenToEvents(true); listenToEvents(true);
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!"); .sendInvitation(forum0.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -455,24 +395,12 @@ public class ForumSharingIntegrationTest
// forum was added successfully // forum was added successfully
assertEquals(1, forumManager1.getForums().size()); assertEquals(1, forumManager1.getForums().size());
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1From0)
.size());
// invitee now shares same forum back // invitee now shares same forum back
forumSharingManager1.sendInvitation(forum0.getId(), forumSharingManager1.sendInvitation(forum0.getId(),
contactId0From1, contactId0From1,
"I am re-sharing this forum with you."); "I am re-sharing this forum with you.",
clock.currentTimeMillis());
// sync re-share invitation
sync1To0(1, false);
// make sure that no new request was received
assertFalse(listener0.requestReceived);
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1From0)
.size());
assertEquals(0, forumSharingManager0.getInvitations().size());
} }
@Test @Test
@@ -482,62 +410,48 @@ public class ForumSharingIntegrationTest
// invitee adds the same forum // invitee adds the same forum
Transaction txn = db1.startTransaction(false); Transaction txn = db1.startTransaction(false);
db1.addGroup(txn, forum0.getGroup()); forumManager1.addForum(txn, forum0);
db1.commitTransaction(txn); db1.commitTransaction(txn);
db1.endTransaction(txn); db1.endTransaction(txn);
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!"); .sendInvitation(forum0.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// invitee now shares same forum back // invitee now shares same forum back
forumSharingManager1.sendInvitation(forum0.getId(), forumSharingManager1.sendInvitation(forum0.getId(),
contactId0From1, "I am re-sharing this forum with you."); contactId0From1, "I am re-sharing this forum with you.",
clock.currentTimeMillis());
// find out who should be Alice, because of random keys // prevent automatic responses
Bytes key0 = new Bytes(author0.getPublicKey()); respond = false;
Bytes key1 = new Bytes(author1.getPublicKey());
// only now sync first request message // only now sync first request message
boolean alice = key1.compareTo(key0) < 0; sync0To1(1, true);
if (alice) { eventWaiter.await(TIMEOUT, 1);
sync0To1(1, false); assertTrue(listener1.requestReceived);
assertFalse(listener1.requestReceived);
} else {
sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
}
// sync second invitation // sync second invitation which counts as accept
alice = key0.compareTo(key1) < 0; sync1To0(1, true);
if (alice) { eventWaiter.await(TIMEOUT, 1);
sync1To0(1, false); assertTrue(listener0.requestReceived);
assertFalse(listener0.requestReceived);
// sharer did not receive request, but response to own request // both peers should share the forum with each other now
eventWaiter.await(TIMEOUT, 1); assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
assertTrue(listener0.responseReceived); .contains(contact1From0));
assertTrue(forumSharingManager1.getSharedWith(forum0.getId())
.contains(contact0From1));
assertEquals(2, forumSharingManager0 // and both have each other's invitations (and no response)
.getInvitationMessages(contactId1From0).size()); assertEquals(2, forumSharingManager0
assertEquals(3, forumSharingManager1 .getInvitationMessages(contactId1From0).size());
.getInvitationMessages(contactId0From1).size()); assertEquals(2, forumSharingManager1
} else { .getInvitationMessages(contactId0From1).size());
sync1To0(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.requestReceived);
// send response from sharer to invitee and make sure it arrived // there are no more open invitations
sync0To1(1, true); assertTrue(forumSharingManager0.getInvitations().isEmpty());
eventWaiter.await(TIMEOUT, 1); assertTrue(forumSharingManager1.getInvitations().isEmpty());
assertTrue(listener1.responseReceived);
assertEquals(3, forumSharingManager0
.getInvitationMessages(contactId1From0).size());
assertEquals(2, forumSharingManager1
.getInvitationMessages(contactId0From1).size());
}
} }
@Test @Test
@@ -547,7 +461,8 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!"); .sendInvitation(forum0.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -564,18 +479,12 @@ public class ForumSharingIntegrationTest
assertEquals(1, assertEquals(1,
forumSharingManager0.getSharedWith(forum0.getId()).size()); forumSharingManager0.getSharedWith(forum0.getId()).size());
// remember SessionId from invitation
List<InvitationMessage> list = new ArrayList<InvitationMessage>(
forumSharingManager1
.getInvitationMessages(contactId0From1));
assertEquals(2, list.size());
InvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId();
assertEquals(sessionId, list.get(1).getSessionId());
// contacts now remove each other // contacts now remove each other
removeAllContacts(); removeAllContacts();
// invitee still has forum
assertEquals(1, forumManager1.getForums().size());
// make sure sharer does share the forum with nobody now // make sure sharer does share the forum with nobody now
assertEquals(0, assertEquals(0,
forumSharingManager0.getSharedWith(forum0.getId()).size()); forumSharingManager0.getSharedWith(forum0.getId()).size());
@@ -584,35 +493,30 @@ public class ForumSharingIntegrationTest
addDefaultContacts(); addDefaultContacts();
addContacts1And2(); addContacts1And2();
// get all sorts of stuff needed to send a message // forum can be shared with contacts again
MessageQueueManager queue = c0.getMessageQueueManager(); assertTrue(forumSharingManager0
Contact c1 = contactManager0.getContact(contactId1From0); .canBeShared(forum0.getId(), contact1From0));
Group group = contactGroupFactory.createContactGroup(CLIENT_ID, c1); assertTrue(forumSharingManager0
long time = clock.currentTimeMillis(); .canBeShared(forum0.getId(), contact2From0));
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// construct a new message re-using the old SessionId // sync first request message
BdfList bodyList = BdfList.of(SHARE_MSG_TYPE_INVITATION,
sessionId.getBytes(),
getRandomString(42),
getRandomBytes(FORUM_SALT_LENGTH)
);
byte[] body = clientHelper.toByteArray(bodyList);
// add the message to the queue
Transaction txn = db0.startTransaction(false);
try {
queue.sendMessage(txn, group, time, body, new Metadata());
db0.commitTransaction(txn);
} finally {
db0.endTransaction(txn);
}
// actually send the message
sync0To1(1, true); sync0To1(1, true);
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
// make sure the new request was received with the same sessionId
// as proof that the state got deleted along with contacts
assertTrue(listener1.requestReceived); assertTrue(listener1.requestReceived);
// sync response back
sync1To0(1, true);
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// forum is still there
assertEquals(1, forumManager1.getForums().size());
assertEquals(1,
forumSharingManager0.getSharedWith(forum0.getId()).size());
} }
@Test @Test
@@ -633,14 +537,16 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!"); .sendInvitation(forum0.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
// second sharer sends invitation for same forum // second sharer sends invitation for same forum
assertTrue(contactId1From2 != null); assertTrue(contactId1From2 != null);
forumSharingManager2 forumSharingManager2
.sendInvitation(forum0.getId(), contactId1From2, null); .sendInvitation(forum0.getId(), contactId1From2, null,
clock.currentTimeMillis());
// sync second request message // sync second request message
sync2To1(1, true); sync2To1(1, true);
@@ -650,13 +556,6 @@ public class ForumSharingIntegrationTest
assertEquals(1, forums.size()); assertEquals(1, forums.size());
assertEquals(2, forums.iterator().next().getNewSharers().size()); assertEquals(2, forums.iterator().next().getNewSharers().size());
assertEquals(forum0, forums.iterator().next().getShareable()); assertEquals(forum0, forums.iterator().next().getShareable());
assertEquals(2,
forumSharingManager1.getSharedWith(forum0.getId()).size());
// make sure both sharers actually share the forum
Collection<Contact> contacts =
forumSharingManager1.getSharedWith(forum0.getId());
assertEquals(2, contacts.size());
// answer second request // answer second request
assertNotNull(contactId2From1); assertNotNull(contactId2From1);
@@ -675,6 +574,11 @@ public class ForumSharingIntegrationTest
sync1To0(1, true); sync1To0(1, true);
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived); assertTrue(listener0.responseReceived);
// make sure both sharers actually share the forum
Collection<Contact> contacts =
forumSharingManager1.getSharedWith(forum0.getId());
assertEquals(2, contacts.size());
} }
@Test @Test
@@ -684,7 +588,8 @@ public class ForumSharingIntegrationTest
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!"); .sendInvitation(forum0.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -745,7 +650,8 @@ public class ForumSharingIntegrationTest
// send invitation again // send invitation again
forumSharingManager0 forumSharingManager0
.sendInvitation(forum0.getId(), contactId1From0, "Hi!"); .sendInvitation(forum0.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis());
// sync first request message // sync first request message
sync0To1(1, true); sync0To1(1, true);
@@ -799,8 +705,10 @@ public class ForumSharingIntegrationTest
requestReceived = true; requestReceived = true;
Forum f = event.getShareable(); Forum f = event.getShareable();
try { try {
Contact c = contactManager0.getContact(contactId1From0); if (respond) {
forumSharingManager0.respondToInvitation(f, c, true); Contact c = contactManager0.getContact(contactId1From0);
forumSharingManager0.respondToInvitation(f, c, true);
}
} catch (DbException ex) { } catch (DbException ex) {
eventWaiter.rethrow(ex); eventWaiter.rethrow(ex);
} finally { } finally {
@@ -814,7 +722,6 @@ public class ForumSharingIntegrationTest
private class InviteeListener implements EventListener { private class InviteeListener implements EventListener {
private volatile boolean requestReceived = false; private volatile boolean requestReceived = false;
private volatile boolean responseReceived = false;
private final boolean accept, answer; private final boolean accept, answer;
@@ -836,13 +743,13 @@ public class ForumSharingIntegrationTest
if (!answer) return; if (!answer) return;
Forum f = event.getShareable(); Forum f = event.getShareable();
try { try {
eventWaiter.assertEquals(1,
forumSharingManager1.getInvitations().size());
SharingInvitationItem invitation =
forumSharingManager1.getInvitations().iterator()
.next();
eventWaiter.assertEquals(f, invitation.getShareable());
if (respond) { if (respond) {
eventWaiter.assertEquals(1,
forumSharingManager1.getInvitations().size());
SharingInvitationItem invitation =
forumSharingManager1.getInvitations().iterator()
.next();
eventWaiter.assertEquals(f, invitation.getShareable());
Contact c = Contact c =
contactManager1 contactManager1
.getContact(event.getContactId()); .getContact(event.getContactId());
@@ -859,7 +766,6 @@ public class ForumSharingIntegrationTest
ForumInvitationResponseReceivedEvent event = ForumInvitationResponseReceivedEvent event =
(ForumInvitationResponseReceivedEvent) e; (ForumInvitationResponseReceivedEvent) e;
eventWaiter.assertEquals(contactId0From1, event.getContactId()); eventWaiter.assertEquals(contactId0From1, event.getContactId());
responseReceived = true;
eventWaiter.resume(); eventWaiter.resume();
} }
} }

View File

@@ -4,340 +4,314 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UniqueId; import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.client.BdfMessageContext; import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.test.ValidatorTestCase; import org.briarproject.bramble.test.ValidatorTestCase;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumFactory;
import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;
import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_NAME; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH; import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH; import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
import static org.briarproject.briar.api.sharing.SharingConstants.INVITATION_MSG;
import static org.briarproject.briar.api.sharing.SharingConstants.LOCAL;
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH; import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
import static org.briarproject.briar.api.sharing.SharingConstants.SESSION_ID; import static org.briarproject.briar.sharing.MessageType.ABORT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT; import static org.briarproject.briar.sharing.MessageType.ACCEPT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT; import static org.briarproject.briar.sharing.MessageType.DECLINE;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE; import static org.briarproject.briar.sharing.MessageType.INVITE;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION; import static org.briarproject.briar.sharing.MessageType.LEAVE;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.briar.api.sharing.SharingConstants.TIME;
import static org.briarproject.briar.api.sharing.SharingConstants.TYPE;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue;
public class ForumSharingValidatorTest extends ValidatorTestCase { public class ForumSharingValidatorTest extends ValidatorTestCase {
private final SessionId sessionId = new SessionId(TestUtils.getRandomId()); private final MessageEncoder messageEncoder =
context.mock(MessageEncoder.class);
private final ForumFactory forumFactory = context.mock(ForumFactory.class);
private final ForumSharingValidator v =
new ForumSharingValidator(messageEncoder, clientHelper,
metadataEncoder, clock, forumFactory);
private final MessageId previousMsgId = new MessageId(getRandomId());
private final String forumName = private final String forumName =
TestUtils.getRandomString(MAX_FORUM_NAME_LENGTH); TestUtils.getRandomString(MAX_FORUM_NAME_LENGTH);
private final byte[] salt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH); private final byte[] salt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH);
private final Forum forum = new Forum(group, forumName, salt);
private final BdfList descriptor = BdfList.of(forumName, salt);
private final String content = private final String content =
TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH); TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH);
private final BdfDictionary meta =
BdfDictionary.of(new BdfEntry("meta", "data"));
@Test @Test
public void testAcceptsInvitationWithContent() throws Exception { public void testAcceptsInvitationWithContent() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, expectCreateForum(forumName);
metadataEncoder, clock); expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group, BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, descriptor,
salt, content)); content));
assertExpectedContextForInvitation(messageContext, forumName, content); assertExpectedContext(messageContext, previousMsgId);
} }
@Test @Test
public void testAcceptsInvitationWithoutContent() throws Exception { public void testAcceptsInvitationWithNullContent() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, expectCreateForum(forumName);
metadataEncoder, clock); expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group, BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, descriptor, null));
salt)); assertExpectedContext(messageContext, previousMsgId);
assertExpectedContextForInvitation(messageContext, forumName, null); }
@Test
public void testAcceptsInvitationWithNullPreviousMsgId() throws Exception {
expectCreateForum(forumName);
expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), null, descriptor, null));
assertExpectedContext(messageContext, null);
} }
@Test @Test
public void testAcceptsAccept() throws Exception { public void testAcceptsAccept() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, expectEncodeMetadata(ACCEPT);
metadataEncoder, clock);
BdfMessageContext messageContext = v.validateMessage(message, group, BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_ACCEPT, sessionId)); BdfList.of(ACCEPT.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, SHARE_MSG_TYPE_ACCEPT); assertExpectedContext(messageContext, previousMsgId);
} }
@Test @Test
public void testAcceptsDecline() throws Exception { public void testAcceptsDecline() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, expectEncodeMetadata(DECLINE);
metadataEncoder, clock);
BdfMessageContext messageContext = v.validateMessage(message, group, BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_DECLINE, sessionId)); BdfList.of(DECLINE.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, SHARE_MSG_TYPE_DECLINE); assertExpectedContext(messageContext, previousMsgId);
} }
@Test @Test
public void testAcceptsLeave() throws Exception { public void testAcceptsLeave() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, expectEncodeMetadata(LEAVE);
metadataEncoder, clock);
BdfMessageContext messageContext = v.validateMessage(message, group, BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_LEAVE, sessionId)); BdfList.of(LEAVE.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, SHARE_MSG_TYPE_LEAVE); assertExpectedContext(messageContext, previousMsgId);
} }
@Test @Test
public void testAcceptsAbort() throws Exception { public void testAcceptsAbort() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, expectEncodeMetadata(ABORT);
metadataEncoder, clock);
BdfMessageContext messageContext = v.validateMessage(message, group, BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_ABORT, sessionId)); BdfList.of(ABORT.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, SHARE_MSG_TYPE_ABORT); assertExpectedContext(messageContext, previousMsgId);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNullMessageType() throws Exception { public void testRejectsNullMessageType() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, v.validateMessage(message, group,
metadataEncoder, clock); BdfList.of(null, groupId, previousMsgId));
v.validateMessage(message, group, BdfList.of(null, sessionId));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNonLongMessageType() throws Exception { public void testRejectsNonLongMessageType() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, v.validateMessage(message, group,
metadataEncoder, clock); BdfList.of("", groupId, previousMsgId));
v.validateMessage(message, group, BdfList.of("", sessionId));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsInvalidMessageType() throws Exception { public void testRejectsInvalidMessageType() throws Exception {
int invalidMessageType = SHARE_MSG_TYPE_ABORT + 1; int invalidMessageType = ABORT.getValue() + 1;
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(invalidMessageType, sessionId)); BdfList.of(invalidMessageType, groupId, previousMsgId));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNullSessionId() throws Exception { public void testRejectsNullSessionId() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_ABORT, null)); BdfList.of(ABORT.getValue(), null, previousMsgId));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNonRawSessionId() throws Exception { public void testRejectsNonRawSessionId() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, v.validateMessage(message, group, BdfList.of(ABORT.getValue(), 123));
metadataEncoder, clock);
v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_ABORT, 123));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooShortSessionId() throws Exception { public void testRejectsTooShortSessionId() throws Exception {
byte[] invalidSessionId = TestUtils.getRandomBytes(UniqueId.LENGTH - 1); byte[] invalidGroupId = TestUtils.getRandomBytes(UniqueId.LENGTH - 1);
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_ABORT, invalidSessionId)); BdfList.of(ABORT.getValue(), invalidGroupId, previousMsgId));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooLongSessionId() throws Exception { public void testRejectsTooLongSessionId() throws Exception {
byte[] invalidSessionId = TestUtils.getRandomBytes(UniqueId.LENGTH + 1); byte[] invalidGroupId = TestUtils.getRandomBytes(UniqueId.LENGTH + 1);
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_ABORT, invalidSessionId)); BdfList.of(ABORT.getValue(), invalidGroupId, previousMsgId));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooShortBodyForAbort() throws Exception { public void testRejectsTooShortBodyForAbort() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, v.validateMessage(message, group,
metadataEncoder, clock); BdfList.of(ABORT.getValue(), groupId));
v.validateMessage(message, group, BdfList.of(SHARE_MSG_TYPE_ABORT));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooLongBodyForAbort() throws Exception { public void testRejectsTooLongBodyForAbort() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_ABORT, sessionId, 123)); BdfList.of(ABORT.getValue(), groupId, previousMsgId, 123));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooShortBodyForInvitation() throws Exception { public void testRejectsTooShortBodyForInvitation() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName)); BdfList.of(INVITE.getValue(), previousMsgId, descriptor));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooLongBodyForInvitation() throws Exception { public void testRejectsTooLongBodyForInvitation() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, descriptor, null,
salt, content, 123)); 123));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNullForumName() throws Exception { public void testRejectsNullForumName() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, BdfList invalidDescriptor = BdfList.of(null, salt);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, null, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
salt, content)); null));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNonStringForumName() throws Exception { public void testRejectsNonStringForumName() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, BdfList invalidDescriptor = BdfList.of(123, salt);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, 123, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
salt, content)); null));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooShortForumName() throws Exception { public void testRejectsTooShortForumName() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, BdfList invalidDescriptor = BdfList.of("", salt);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, "", BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
salt, content)); null));
} }
@Test @Test
public void testAcceptsMinLengthForumName() throws Exception { public void testAcceptsMinLengthForumName() throws Exception {
String shortForumName = TestUtils.getRandomString(1); String shortForumName = TestUtils.getRandomString(1);
ForumSharingValidator v = new ForumSharingValidator(clientHelper, BdfList validDescriptor = BdfList.of(shortForumName, salt);
metadataEncoder, clock); expectCreateForum(shortForumName);
expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group, BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, shortForumName, BdfList.of(INVITE.getValue(), previousMsgId, validDescriptor,
salt, content)); null));
assertExpectedContextForInvitation(messageContext, shortForumName, assertExpectedContext(messageContext, previousMsgId);
content);
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooLongForumName() throws Exception { public void testRejectsTooLongForumName() throws Exception {
String invalidForumName = String invalidForumName =
TestUtils.getRandomString(MAX_FORUM_NAME_LENGTH + 1); TestUtils.getRandomString(MAX_FORUM_NAME_LENGTH + 1);
ForumSharingValidator v = new ForumSharingValidator(clientHelper, BdfList invalidDescriptor = BdfList.of(invalidForumName, salt);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
invalidForumName, salt, content)); null));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNullSalt() throws Exception { public void testRejectsNullSalt() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, BdfList invalidDescriptor = BdfList.of(forumName, null);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
null, content)); null));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNonRawSalt() throws Exception { public void testRejectsNonRawSalt() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, BdfList invalidDescriptor = BdfList.of(forumName, 123);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
123, content)); null));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooShortSalt() throws Exception { public void testRejectsTooShortSalt() throws Exception {
byte[] invalidSalt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH - 1); byte[] invalidSalt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH - 1);
ForumSharingValidator v = new ForumSharingValidator(clientHelper, BdfList invalidDescriptor = BdfList.of(forumName, invalidSalt);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
invalidSalt, content)); null));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooLongSalt() throws Exception { public void testRejectsTooLongSalt() throws Exception {
byte[] invalidSalt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH + 1); byte[] invalidSalt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH + 1);
ForumSharingValidator v = new ForumSharingValidator(clientHelper, BdfList invalidDescriptor = BdfList.of(forumName, invalidSalt);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
invalidSalt, content)); null));
}
@Test(expected = FormatException.class)
public void testRejectsNullContent() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
salt, null));
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNonStringContent() throws Exception { public void testRejectsNonStringContent() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, expectCreateForum(forumName);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, descriptor,
salt, 123)); 123));
} }
@Test @Test
public void testAcceptsMinLengthContent() throws Exception { public void testAcceptsMinLengthContent() throws Exception {
ForumSharingValidator v = new ForumSharingValidator(clientHelper, expectCreateForum(forumName);
metadataEncoder, clock); expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group, BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, descriptor, "1"));
salt, "")); assertExpectedContext(messageContext, previousMsgId);
assertExpectedContextForInvitation(messageContext, forumName, "");
} }
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsTooLongContent() throws Exception { public void testRejectsTooLongContent() throws Exception {
String invalidContent = String invalidContent =
TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH + 1); TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH + 1);
ForumSharingValidator v = new ForumSharingValidator(clientHelper, expectCreateForum(forumName);
metadataEncoder, clock);
v.validateMessage(message, group, v.validateMessage(message, group,
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName, BdfList.of(INVITE.getValue(), previousMsgId, descriptor,
salt, invalidContent)); invalidContent));
} }
private void assertExpectedContextForInvitation( private void expectCreateForum(final String name) {
BdfMessageContext messageContext, String forumName, context.checking(new Expectations() {{
@Nullable String content) throws FormatException { oneOf(forumFactory).createForum(name, salt);
BdfDictionary meta = messageContext.getDictionary(); will(returnValue(forum));
if (content == null) { }});
assertEquals(6, meta.size()); }
} else {
assertEquals(7, meta.size()); private void expectEncodeMetadata(final MessageType type) {
assertEquals(content, meta.getString(INVITATION_MSG)); context.checking(new Expectations() {{
} oneOf(messageEncoder)
assertEquals(forumName, meta.getString(FORUM_NAME)); .encodeMetadata(type, groupId, timestamp, false, false,
assertEquals(salt, meta.getRaw(FORUM_SALT)); false, false);
assertEquals(SHARE_MSG_TYPE_INVITATION, meta.getLong(TYPE).intValue()); will(returnValue(meta));
assertEquals(sessionId.getBytes(), meta.getRaw(SESSION_ID)); }});
assertFalse(meta.getBoolean(LOCAL));
assertEquals(timestamp, meta.getLong(TIME).longValue());
assertEquals(0, messageContext.getDependencies().size());
} }
private void assertExpectedContext(BdfMessageContext messageContext, private void assertExpectedContext(BdfMessageContext messageContext,
int type) throws FormatException { @Nullable MessageId previousMsgId) throws FormatException {
BdfDictionary meta = messageContext.getDictionary(); Collection<MessageId> dependencies = messageContext.getDependencies();
assertEquals(4, meta.size()); if (previousMsgId == null) {
assertEquals(type, meta.getLong(TYPE).intValue()); assertTrue(dependencies.isEmpty());
assertEquals(sessionId.getBytes(), meta.getRaw(SESSION_ID)); } else {
assertFalse(meta.getBoolean(LOCAL)); assertEquals(1, dependencies.size());
assertEquals(timestamp, meta.getLong(TIME).longValue()); assertTrue(dependencies.contains(previousMsgId));
assertEquals(0, messageContext.getDependencies().size()); }
assertEquals(meta, messageContext.getDictionary());
} }
} }