mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
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:
@@ -1020,13 +1020,13 @@ public class ConversationActivity extends BriarActivity
|
||||
@DatabaseExecutor
|
||||
private void respondToForumRequest(SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
forumSharingManager.respondToInvitation(id, accept);
|
||||
forumSharingManager.respondToInvitation(contactId, id, accept);
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
private void respondToBlogRequest(SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
blogSharingManager.respondToInvitation(id, accept);
|
||||
blogSharingManager.respondToInvitation(contactId, id, accept);
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
|
||||
@@ -170,8 +170,7 @@ abstract class ConversationItem {
|
||||
} else if (ir instanceof GroupInvitationRequest) {
|
||||
text = ctx.getString(
|
||||
R.string.groups_invitations_invitation_sent,
|
||||
contactName,
|
||||
((GroupInvitationRequest) ir).getGroupName());
|
||||
contactName, ir.getShareable().getName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown InvitationRequest");
|
||||
}
|
||||
@@ -194,8 +193,7 @@ abstract class ConversationItem {
|
||||
} else if (ir instanceof GroupInvitationRequest) {
|
||||
text = ctx.getString(
|
||||
R.string.groups_invitations_invitation_received,
|
||||
contactName,
|
||||
((GroupInvitationRequest) ir).getGroupName());
|
||||
contactName, ir.getShareable().getName());
|
||||
type = GROUP;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown InvitationRequest");
|
||||
@@ -203,7 +201,7 @@ abstract class ConversationItem {
|
||||
return new ConversationRequestItem(ir.getId(),
|
||||
ir.getGroupId(), type, ir.getSessionId(), text,
|
||||
ir.getMessage(), ir.getTimestamp(), ir.isRead(),
|
||||
ir.getInvitedGroupId(), !ir.isAvailable(),
|
||||
ir.getShareable().getId(), !ir.isAvailable(),
|
||||
ir.canBeOpened());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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.controller.handler.ExceptionHandler;
|
||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||
import org.briarproject.briar.api.messaging.ConversationManager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -22,6 +24,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -31,14 +34,19 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
|
||||
private final static Logger LOG =
|
||||
Logger.getLogger(ShareBlogControllerImpl.class.getName());
|
||||
|
||||
private final ConversationManager conversationManager;
|
||||
private final BlogSharingManager blogSharingManager;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
ShareBlogControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||
BlogSharingManager blogSharingManager) {
|
||||
ConversationManager conversationManager,
|
||||
BlogSharingManager blogSharingManager, Clock clock) {
|
||||
super(dbExecutor, lifecycleManager, contactManager);
|
||||
this.conversationManager = conversationManager;
|
||||
this.blogSharingManager = blogSharingManager;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,15 +56,19 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
|
||||
|
||||
@Override
|
||||
public void share(final GroupId g, final Collection<ContactId> contacts,
|
||||
final String msg,
|
||||
final String message,
|
||||
final ExceptionHandler<DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
String msg = isNullOrEmpty(message) ? null : message;
|
||||
for (ContactId c : contacts) {
|
||||
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) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
|
||||
@@ -10,9 +10,11 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
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.controller.handler.ExceptionHandler;
|
||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||
import org.briarproject.briar.api.messaging.ConversationManager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -22,6 +24,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -31,14 +34,19 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
|
||||
private final static Logger LOG =
|
||||
Logger.getLogger(ShareForumControllerImpl.class.getName());
|
||||
|
||||
private final ConversationManager conversationManager;
|
||||
private final ForumSharingManager forumSharingManager;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
ShareForumControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||
ForumSharingManager forumSharingManager) {
|
||||
ConversationManager conversationManager,
|
||||
ForumSharingManager forumSharingManager, Clock clock) {
|
||||
super(dbExecutor, lifecycleManager, contactManager);
|
||||
this.conversationManager = conversationManager;
|
||||
this.forumSharingManager = forumSharingManager;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,15 +56,19 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
|
||||
|
||||
@Override
|
||||
public void share(final GroupId g, final Collection<ContactId> contacts,
|
||||
final String msg,
|
||||
final String message,
|
||||
final ExceptionHandler<DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
String msg = isNullOrEmpty(message) ? null : message;
|
||||
for (ContactId c : contacts) {
|
||||
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) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package org.briarproject.briar.api.blog;
|
||||
|
||||
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.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
@@ -10,7 +13,7 @@ import org.briarproject.briar.api.sharing.InvitationRequest;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@NotNullByDefault
|
||||
public class BlogInvitationRequest extends InvitationRequest {
|
||||
public class BlogInvitationRequest extends InvitationRequest<Blog> {
|
||||
|
||||
private final String blogAuthorName;
|
||||
|
||||
@@ -19,9 +22,12 @@ public class BlogInvitationRequest extends InvitationRequest {
|
||||
@Nullable String message, GroupId blogId,
|
||||
boolean available, boolean canBeOpened, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, message, blogId, available,
|
||||
canBeOpened, time, local, sent, seen, read);
|
||||
// TODO pass a proper blog here when redoing the BlogSharingManager
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId,
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,8 @@ public class BlogInvitationResponse extends InvitationResponse {
|
||||
GroupId groupId, ContactId contactId, GroupId blogId,
|
||||
boolean accept, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, blogId, accept, time, local,
|
||||
sent, seen, read);
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, blogId,
|
||||
contactId, accept);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,23 +12,18 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class ForumInvitationRequest extends InvitationRequest {
|
||||
public class ForumInvitationRequest extends InvitationRequest<Forum> {
|
||||
|
||||
private final String forumName;
|
||||
|
||||
public ForumInvitationRequest(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId, GroupId forumId,
|
||||
String forumName, @Nullable String message, boolean available,
|
||||
boolean canBeOpened, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, message, forumId, available,
|
||||
canBeOpened, time, local, sent, seen, read);
|
||||
this.forumName = forumName;
|
||||
public ForumInvitationRequest(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, Forum forum, ContactId contactId,
|
||||
@Nullable String message, boolean available, boolean canBeOpened) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, forum,
|
||||
contactId, message, available, canBeOpened);
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
return getShareable().getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,13 +13,12 @@ import javax.annotation.concurrent.Immutable;
|
||||
@NotNullByDefault
|
||||
public class ForumInvitationResponse extends InvitationResponse {
|
||||
|
||||
public ForumInvitationResponse(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId, GroupId forumId,
|
||||
boolean accept, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, forumId, accept, time, local,
|
||||
sent, seen, read);
|
||||
public ForumInvitationResponse(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, GroupId forumId, ContactId contactId,
|
||||
boolean accept) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, forumId,
|
||||
contactId, accept);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ public interface ForumManager {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
package org.briarproject.briar.api.forum.event;
|
||||
|
||||
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.sharing.event.InvitationResponseReceivedEvent;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class ForumInvitationResponseReceivedEvent extends
|
||||
InvitationResponseReceivedEvent {
|
||||
|
||||
private final String forumName;
|
||||
|
||||
public ForumInvitationResponseReceivedEvent(String forumName,
|
||||
ContactId contactId, ForumInvitationResponse response) {
|
||||
public ForumInvitationResponseReceivedEvent(ContactId contactId,
|
||||
ForumInvitationResponse response) {
|
||||
super(contactId, response);
|
||||
this.forumName = forumName;
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.briarproject.briar.api.privategroup.invitation;
|
||||
|
||||
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.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.briar.api.sharing.InvitationRequest;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -13,28 +13,14 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class GroupInvitationRequest extends InvitationRequest {
|
||||
public class GroupInvitationRequest extends InvitationRequest<PrivateGroup> {
|
||||
|
||||
private final String groupName;
|
||||
private final Author creator;
|
||||
|
||||
public GroupInvitationRequest(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId, @Nullable String message,
|
||||
GroupId privateGroupId, String groupName, Author creator,
|
||||
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;
|
||||
public GroupInvitationRequest(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, PrivateGroup shareable, ContactId contactId,
|
||||
@Nullable String message, boolean available, boolean canBeOpened) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, shareable,
|
||||
contactId, message, available, canBeOpened);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,11 +13,12 @@ import javax.annotation.concurrent.Immutable;
|
||||
@NotNullByDefault
|
||||
public class GroupInvitationResponse extends InvitationResponse {
|
||||
|
||||
public GroupInvitationResponse(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId, GroupId privateGroupId,
|
||||
boolean accept, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
super(id, sessionId, groupId, contactId, privateGroupId, accept, time,
|
||||
local, sent, seen, read);
|
||||
public GroupInvitationResponse(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, GroupId shareableId, ContactId contactId,
|
||||
boolean accept) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId,
|
||||
shareableId, contactId, accept);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
|
||||
@Deprecated
|
||||
public interface InvitationFactory<I extends SharingMessage.Invitation> {
|
||||
|
||||
I build(GroupId groupId, BdfDictionary d) throws FormatException;
|
||||
|
||||
@@ -7,25 +7,22 @@ import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.BaseMessageHeader;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public abstract class InvitationMessage extends BaseMessageHeader {
|
||||
public class InvitationMessage extends BaseMessageHeader {
|
||||
|
||||
private final SessionId sessionId;
|
||||
private final ContactId contactId;
|
||||
private final GroupId invitedGroupId;
|
||||
|
||||
public InvitationMessage(MessageId id, SessionId sessionId, GroupId groupId,
|
||||
ContactId contactId, GroupId invitedGroupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
public InvitationMessage(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, ContactId contactId) {
|
||||
|
||||
super(id, groupId, time, local, read, sent, seen);
|
||||
this.sessionId = sessionId;
|
||||
this.contactId = contactId;
|
||||
this.invitedGroupId = invitedGroupId;
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
@@ -36,9 +33,4 @@ public abstract class InvitationMessage extends BaseMessageHeader {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GroupId getInvitedGroupId() {
|
||||
return invitedGroupId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,20 +11,19 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public abstract class InvitationRequest extends InvitationMessage {
|
||||
public class InvitationRequest<S extends Shareable> extends InvitationMessage {
|
||||
|
||||
private final S shareable;
|
||||
@Nullable
|
||||
private final String message;
|
||||
private final boolean available, canBeOpened;
|
||||
|
||||
public InvitationRequest(MessageId id, SessionId sessionId, GroupId groupId,
|
||||
ContactId contactId, @Nullable String message,
|
||||
GroupId invitedGroupId, boolean available,
|
||||
boolean canBeOpened, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
super(id, sessionId, groupId, contactId, invitedGroupId, time, local,
|
||||
sent, seen, read);
|
||||
if (available && canBeOpened) throw new IllegalArgumentException();
|
||||
public InvitationRequest(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, S shareable, ContactId contactId,
|
||||
@Nullable String message, boolean available, boolean canBeOpened) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, contactId);
|
||||
this.shareable = shareable;
|
||||
this.message = message;
|
||||
this.available = available;
|
||||
this.canBeOpened = canBeOpened;
|
||||
@@ -43,4 +42,8 @@ public abstract class InvitationRequest extends InvitationMessage {
|
||||
return canBeOpened;
|
||||
}
|
||||
|
||||
public S getShareable() {
|
||||
return shareable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,21 +10,26 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public abstract class InvitationResponse extends InvitationMessage {
|
||||
public class InvitationResponse extends InvitationMessage {
|
||||
|
||||
private final GroupId shareableId;
|
||||
private final boolean accept;
|
||||
|
||||
public InvitationResponse(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId,
|
||||
GroupId invitedGroupId, boolean accept, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, invitedGroupId, time, local,
|
||||
sent, seen, read);
|
||||
public InvitationResponse(MessageId id, GroupId groupId,
|
||||
long time, boolean local, boolean sent, boolean seen,
|
||||
boolean read, SessionId sessionId, GroupId shareableId,
|
||||
ContactId contactId, boolean accept) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, contactId);
|
||||
this.shareableId = shareableId;
|
||||
this.accept = accept;
|
||||
}
|
||||
|
||||
public boolean wasAccepted() {
|
||||
return accept;
|
||||
}
|
||||
|
||||
public GroupId getShareableId() {
|
||||
return shareableId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,34 +15,63 @@ public interface SharingConstants {
|
||||
*/
|
||||
int MAX_INVITATION_MESSAGE_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
|
||||
|
||||
@Deprecated
|
||||
String CONTACT_ID = "contactId";
|
||||
@Deprecated
|
||||
String GROUP_ID = "groupId";
|
||||
@Deprecated
|
||||
String TO_BE_SHARED_BY_US = "toBeSharedByUs";
|
||||
@Deprecated
|
||||
String SHARED_BY_US = "sharedByUs";
|
||||
@Deprecated
|
||||
String SHARED_WITH_US = "sharedWithUs";
|
||||
@Deprecated
|
||||
String TYPE = "type";
|
||||
@Deprecated
|
||||
String SESSION_ID = "sessionId";
|
||||
@Deprecated
|
||||
String STORAGE_ID = "storageId";
|
||||
@Deprecated
|
||||
String STATE = "state";
|
||||
@Deprecated
|
||||
String LOCAL = "local";
|
||||
@Deprecated
|
||||
String TIME = "time";
|
||||
@Deprecated
|
||||
String IS_SHARER = "isSharer";
|
||||
@Deprecated
|
||||
String SHAREABLE_ID = "shareableId";
|
||||
@Deprecated
|
||||
String INVITATION_MSG = "invitationMsg";
|
||||
@Deprecated
|
||||
String INVITATION_ID = "invitationId";
|
||||
@Deprecated
|
||||
String RESPONSE_ID = "responseId";
|
||||
@Deprecated
|
||||
int SHARE_MSG_TYPE_INVITATION = 1;
|
||||
@Deprecated
|
||||
int SHARE_MSG_TYPE_ACCEPT = 2;
|
||||
@Deprecated
|
||||
int SHARE_MSG_TYPE_DECLINE = 3;
|
||||
@Deprecated
|
||||
int SHARE_MSG_TYPE_LEAVE = 4;
|
||||
@Deprecated
|
||||
int SHARE_MSG_TYPE_ABORT = 5;
|
||||
@Deprecated
|
||||
int TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US = 0;
|
||||
@Deprecated
|
||||
int TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US = 1;
|
||||
@Deprecated
|
||||
int TASK_ADD_SHARED_SHAREABLE = 2;
|
||||
@Deprecated
|
||||
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;
|
||||
@Deprecated
|
||||
int TASK_SHARE_SHAREABLE = 5;
|
||||
@Deprecated
|
||||
int TASK_UNSHARE_SHAREABLE_SHARED_BY_US = 6;
|
||||
@Deprecated
|
||||
int TASK_UNSHARE_SHAREABLE_SHARED_WITH_US = 7;
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ public interface SharingManager<S extends Shareable>
|
||||
* Sends an invitation to share the given group with the given contact
|
||||
* and sends an optional message along with it.
|
||||
*/
|
||||
void sendInvitation(GroupId groupId, ContactId contactId,
|
||||
@Nullable String message) throws DbException;
|
||||
void sendInvitation(GroupId shareableId, ContactId contactId,
|
||||
@Nullable String message, long timestamp) throws DbException;
|
||||
|
||||
/**
|
||||
* Responds to a pending group invitation
|
||||
@@ -32,7 +32,7 @@ public interface SharingManager<S extends Shareable>
|
||||
/**
|
||||
* Responds to a pending group invitation
|
||||
*/
|
||||
void respondToInvitation(SessionId id, boolean accept)
|
||||
void respondToInvitation(ContactId c, SessionId id, boolean accept)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.TYPE;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
public interface SharingMessage {
|
||||
|
||||
|
||||
@@ -108,6 +108,11 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addForum(Transaction txn, Forum f) throws DbException {
|
||||
db.addGroup(txn, f.getGroup());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeForum(Forum f) throws DbException {
|
||||
Transaction txn = db.startTransaction(false);
|
||||
|
||||
@@ -4,7 +4,6 @@ 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;
|
||||
@@ -177,7 +176,7 @@ abstract class AbstractProtocolEngine<S extends Session>
|
||||
|
||||
void subscribeToPrivateGroup(Transaction txn, MessageId inviteId)
|
||||
throws DbException, FormatException {
|
||||
InviteMessage invite = getInviteMessage(txn, inviteId);
|
||||
InviteMessage invite = messageParser.getInviteMessage(txn, inviteId);
|
||||
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
|
||||
invite.getGroupName(), invite.getCreator(), invite.getSalt());
|
||||
long timestamp =
|
||||
@@ -197,14 +196,6 @@ abstract class AbstractProtocolEngine<S extends Session>
|
||||
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,
|
||||
GroupId privateGroupId, boolean visibleInConversation)
|
||||
throws DbException {
|
||||
|
||||
@@ -254,8 +254,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
|
||||
private GroupInvitationResponse createInvitationResponse(
|
||||
GroupInvitationMessage m, ContactId c, boolean accept) {
|
||||
SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes());
|
||||
return new GroupInvitationResponse(m.getId(), sessionId,
|
||||
m.getContactGroupId(), c, m.getPrivateGroupId(), accept,
|
||||
m.getTimestamp(), false, false, true, false);
|
||||
return new GroupInvitationResponse(m.getId(), m.getContactGroupId(),
|
||||
m.getTimestamp(), false, false, true, false, sessionId,
|
||||
m.getPrivateGroupId(), c, accept);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
private final ProtocolEngine<CreatorSession> creatorEngine;
|
||||
private final ProtocolEngine<InviteeSession> inviteeEngine;
|
||||
private final ProtocolEngine<PeerSession> peerEngine;
|
||||
private final Group localGroup;
|
||||
|
||||
@Inject
|
||||
GroupInvitationManagerImpl(DatabaseComponent db,
|
||||
@@ -93,12 +92,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
creatorEngine = engineFactory.createCreatorEngine();
|
||||
inviteeEngine = engineFactory.createInviteeEngine();
|
||||
peerEngine = engineFactory.createPeerEngine();
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLocalState(Transaction txn) throws DbException {
|
||||
db.addGroup(txn, localGroup);
|
||||
// Ensure we've set things up for any pre-existing contacts
|
||||
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
||||
}
|
||||
@@ -404,22 +401,15 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
throws DbException, FormatException {
|
||||
SessionId sessionId = getSessionId(meta.getPrivateGroupId());
|
||||
// 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());
|
||||
return new GroupInvitationRequest(m, sessionId, contactGroupId, c,
|
||||
invite.getMessage(), invite.getPrivateGroupId(),
|
||||
invite.getGroupName(), invite.getCreator(),
|
||||
meta.isAvailableToAnswer(), canBeOpened, meta.getTimestamp(),
|
||||
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);
|
||||
return new GroupInvitationRequest(m, contactGroupId,
|
||||
meta.getTimestamp(), meta.isLocal(), status.isSent(),
|
||||
status.isSeen(), meta.isRead(), sessionId, pg, c,
|
||||
invite.getMessage(), meta.isAvailableToAnswer(), canBeOpened);
|
||||
}
|
||||
|
||||
private GroupInvitationResponse parseInvitationResponse(ContactId c,
|
||||
@@ -427,10 +417,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
MessageStatus status, boolean accept)
|
||||
throws DbException, FormatException {
|
||||
SessionId sessionId = getSessionId(meta.getPrivateGroupId());
|
||||
return new GroupInvitationResponse(m, sessionId, contactGroupId, c,
|
||||
meta.getPrivateGroupId(), accept, meta.getTimestamp(),
|
||||
meta.isLocal(), status.isSent(), status.isSeen(),
|
||||
meta.isRead());
|
||||
return new GroupInvitationResponse(m, contactGroupId,
|
||||
meta.getTimestamp(), meta.isLocal(), status.isSent(),
|
||||
status.isSeen(), meta.isRead(), sessionId,
|
||||
meta.getPrivateGroupId(), c, accept);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -481,7 +471,7 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
|
||||
private GroupInvitationItem parseGroupInvitationItem(Transaction txn,
|
||||
Contact c, MessageId m) throws DbException, FormatException {
|
||||
InviteMessage invite = getInviteMessage(txn, m);
|
||||
InviteMessage invite = messageParser.getInviteMessage(txn, m);
|
||||
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
|
||||
invite.getGroupName(), invite.getCreator(), invite.getSalt());
|
||||
return new GroupInvitationItem(privateGroup, c);
|
||||
|
||||
@@ -234,8 +234,9 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
// Broadcast an event
|
||||
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
|
||||
m.getGroupName(), m.getCreator(), m.getSalt());
|
||||
txn.attach(new GroupInvitationRequestReceivedEvent(privateGroup,
|
||||
contactId, createInvitationRequest(m, contactId)));
|
||||
txn.attach(
|
||||
new GroupInvitationRequestReceivedEvent(privateGroup, contactId,
|
||||
createInvitationRequest(m, privateGroup, contactId)));
|
||||
// Move to the INVITED state
|
||||
return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
|
||||
@@ -317,11 +318,11 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
}
|
||||
|
||||
private GroupInvitationRequest createInvitationRequest(InviteMessage m,
|
||||
ContactId c) {
|
||||
PrivateGroup pg, ContactId c) {
|
||||
SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes());
|
||||
return new GroupInvitationRequest(m.getId(), sessionId,
|
||||
m.getContactGroupId(), c, m.getMessage(), m.getPrivateGroupId(),
|
||||
m.getGroupName(), m.getCreator(), true, false, m.getTimestamp(),
|
||||
false, false, true, false);
|
||||
return new GroupInvitationRequest(m.getId(), m.getContactGroupId(),
|
||||
m.getTimestamp(), false, false, true, false, sessionId, pg, c,
|
||||
m.getMessage(), true, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,9 +3,12 @@ package org.briarproject.briar.privategroup.invitation;
|
||||
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;
|
||||
|
||||
@NotNullByDefault
|
||||
interface MessageParser {
|
||||
@@ -18,6 +21,9 @@ interface MessageParser {
|
||||
|
||||
MessageMetadata parseMetadata(BdfDictionary meta) throws FormatException;
|
||||
|
||||
InviteMessage getInviteMessage(Transaction txn, MessageId m)
|
||||
throws DbException, FormatException;
|
||||
|
||||
InviteMessage parseInviteMessage(Message m, BdfList body)
|
||||
throws FormatException;
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.briarproject.briar.privategroup.invitation;
|
||||
|
||||
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.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
@@ -31,12 +34,14 @@ class MessageParserImpl implements MessageParser {
|
||||
|
||||
private final AuthorFactory authorFactory;
|
||||
private final PrivateGroupFactory privateGroupFactory;
|
||||
private final ClientHelper clientHelper;
|
||||
|
||||
@Inject
|
||||
MessageParserImpl(AuthorFactory authorFactory,
|
||||
PrivateGroupFactory privateGroupFactory) {
|
||||
PrivateGroupFactory privateGroupFactory, ClientHelper clientHelper) {
|
||||
this.authorFactory = authorFactory;
|
||||
this.privateGroupFactory = privateGroupFactory;
|
||||
this.clientHelper = clientHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,6 +83,15 @@ class MessageParserImpl implements MessageParser {
|
||||
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
|
||||
public InviteMessage parseInviteMessage(Message m, BdfList body)
|
||||
throws FormatException {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,7 +52,7 @@ import static org.briarproject.briar.api.sharing.SharingConstants.RESPONSE_ID;
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class BlogSharingManagerImpl extends
|
||||
SharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationRequestReceivedEvent, BlogInvitationResponseReceivedEvent>
|
||||
OldSharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationRequestReceivedEvent, BlogInvitationResponseReceivedEvent>
|
||||
implements BlogSharingManager, RemoveBlogHook {
|
||||
|
||||
private final ContactManager contactManager;
|
||||
@@ -159,7 +159,7 @@ class BlogSharingManagerImpl extends
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationFactory<BlogInvitation, BlogSharerSessionState> getIFactory() {
|
||||
protected OldInvitationFactory<BlogInvitation, BlogSharerSessionState> getIFactory() {
|
||||
return iFactory;
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ class BlogSharingManagerImpl extends
|
||||
}
|
||||
|
||||
private static class IFactory implements
|
||||
InvitationFactory<BlogInvitation, BlogSharerSessionState> {
|
||||
OldInvitationFactory<BlogInvitation, BlogSharerSessionState> {
|
||||
@Override
|
||||
public BlogInvitation build(GroupId groupId, BdfDictionary d)
|
||||
throws FormatException {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,78 +1,35 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.client.ContactGroupFactory;
|
||||
import org.briarproject.bramble.api.contact.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.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.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.SessionId;
|
||||
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.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 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
|
||||
class ForumSharingManagerImpl extends
|
||||
SharingManagerImpl<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationRequestReceivedEvent, ForumInvitationResponseReceivedEvent>
|
||||
class ForumSharingManagerImpl extends SharingManagerImpl<Forum>
|
||||
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
|
||||
ForumSharingManagerImpl(ClientHelper clientHelper,
|
||||
Clock clock, DatabaseComponent db,
|
||||
ForumFactory forumFactory,
|
||||
ForumManager forumManager,
|
||||
MessageQueueManager messageQueueManager,
|
||||
MetadataEncoder metadataEncoder,
|
||||
MetadataParser metadataParser,
|
||||
ForumSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||
MetadataParser metadataParser, MessageParser<Forum> messageParser,
|
||||
SessionEncoder sessionEncoder, SessionParser sessionParser,
|
||||
MessageTracker messageTracker,
|
||||
ContactGroupFactory contactGroupFactory,
|
||||
SecureRandom random, MessageTracker messageTracker) {
|
||||
super(db, messageQueueManager, clientHelper, metadataParser,
|
||||
metadataEncoder, random, contactGroupFactory, messageTracker,
|
||||
clock);
|
||||
|
||||
sFactory = new SFactory(forumFactory, forumManager);
|
||||
iFactory = new IFactory();
|
||||
isFactory = new ISFactory();
|
||||
ssFactory = new SSFactory();
|
||||
irFactory = new IRFactory(sFactory);
|
||||
irrFactory = new IRRFactory();
|
||||
ProtocolEngine<Forum> engine,
|
||||
InvitationFactory<Forum> invitationFactory) {
|
||||
super(db, clientHelper, metadataParser, messageParser, sessionEncoder,
|
||||
sessionParser, messageTracker, contactGroupFactory, engine,
|
||||
invitationFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,221 +37,9 @@ class ForumSharingManagerImpl extends
|
||||
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
|
||||
public void removingForum(Transaction txn, Forum f) throws DbException {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +1,47 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.client.BdfMessageContext;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.client.BdfQueueMessageValidator;
|
||||
import org.briarproject.briar.api.forum.Forum;
|
||||
import org.briarproject.briar.api.forum.ForumFactory;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||
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.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
|
||||
@NotNullByDefault
|
||||
class ForumSharingValidator extends BdfQueueMessageValidator {
|
||||
class ForumSharingValidator extends SharingValidator {
|
||||
|
||||
private final ForumFactory forumFactory;
|
||||
|
||||
@Inject
|
||||
ForumSharingValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
ForumSharingValidator(MessageEncoder messageEncoder,
|
||||
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
|
||||
Clock clock, ForumFactory forumFactory) {
|
||||
super(messageEncoder, clientHelper, metadataEncoder, clock);
|
||||
this.forumFactory = forumFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BdfMessageContext validateMessage(Message m, Group g,
|
||||
BdfList body) throws FormatException {
|
||||
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
long type = body.getLong(0);
|
||||
byte[] id = body.getRaw(1);
|
||||
checkLength(id, SessionId.LENGTH);
|
||||
|
||||
if (type == SHARE_MSG_TYPE_INVITATION) {
|
||||
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);
|
||||
protected GroupId validateDescriptor(BdfList descriptor)
|
||||
throws FormatException {
|
||||
checkSize(descriptor, 2);
|
||||
String name = descriptor.getString(0);
|
||||
checkLength(name, 1, MAX_FORUM_NAME_LENGTH);
|
||||
byte[] salt = descriptor.getRaw(1);
|
||||
checkLength(salt, FORUM_SALT_LENGTH);
|
||||
Forum forum = forumFactory.createForum(name, salt);
|
||||
return forum.getId();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.api.sharing.SharingMessage;
|
||||
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.sharing.InvitationRequest;
|
||||
import org.briarproject.briar.api.sharing.InvitationResponse;
|
||||
import org.briarproject.briar.api.sharing.Shareable;
|
||||
|
||||
@NotNullByDefault
|
||||
interface InvitationFactory<I extends SharingMessage.Invitation, SS extends SharerSessionState>
|
||||
extends org.briarproject.briar.api.sharing.InvitationFactory<I> {
|
||||
public interface InvitationFactory<S extends Shareable> {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent> {
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.sharing;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
interface InvitationResponseReceivedEventFactory<SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.SimpleMessage;
|
||||
|
||||
@Deprecated
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent>
|
||||
|
||||
@@ -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_LEAVE;
|
||||
|
||||
@Deprecated
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
public abstract class InviteeSessionState extends SharingSessionState {
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.sharing.Shareable;
|
||||
|
||||
@Deprecated
|
||||
interface InviteeSessionStateFactory<S extends Shareable, IS extends InviteeSessionState> {
|
||||
|
||||
IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.briar.api.sharing.Shareable;
|
||||
import org.briarproject.briar.api.sharing.SharingMessage;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
interface ShareableFactory<S extends Shareable, I extends SharingMessage.Invitation, IS extends InviteeSessionState, SS extends SharerSessionState> {
|
||||
|
||||
|
||||
@@ -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_DECLINE;
|
||||
|
||||
@Deprecated
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
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 =
|
||||
Logger.getLogger(SharerEngine.class.getName());
|
||||
|
||||
private final InvitationFactory<I, SS> invitationFactory;
|
||||
private final OldInvitationFactory<I, SS> invitationFactory;
|
||||
private final InvitationResponseReceivedEventFactory<SS, IRR>
|
||||
invitationResponseReceivedEventFactory;
|
||||
private final Clock clock;
|
||||
|
||||
SharerEngine(InvitationFactory<I, SS> invitationFactory,
|
||||
SharerEngine(OldInvitationFactory<I, SS> invitationFactory,
|
||||
InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory,
|
||||
Clock clock) {
|
||||
this.invitationFactory = invitationFactory;
|
||||
|
||||
@@ -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_LEAVE;
|
||||
|
||||
@Deprecated
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
public abstract class SharerSessionState extends SharingSessionState {
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.sharing.Shareable;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
interface SharerSessionStateFactory<S extends Shareable, SS extends SharerSessionState> {
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,10 +4,13 @@ import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.sync.ValidationManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.api.blog.BlogManager;
|
||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||
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.ForumSharingManager;
|
||||
import org.briarproject.briar.api.messaging.ConversationManager;
|
||||
@@ -68,14 +71,15 @@ public class SharingModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
ForumSharingValidator provideForumSharingValidator(
|
||||
MessageQueueManager messageQueueManager, ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
|
||||
ValidationManager validationManager, MessageEncoder messageEncoder,
|
||||
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
|
||||
Clock clock, ForumFactory forumFactory) {
|
||||
ForumSharingValidator validator =
|
||||
new ForumSharingValidator(clientHelper, metadataEncoder, clock);
|
||||
messageQueueManager.registerMessageValidator(
|
||||
ForumSharingManager.CLIENT_ID, validator);
|
||||
|
||||
new ForumSharingValidator(messageEncoder, clientHelper,
|
||||
metadataEncoder, clock, forumFactory);
|
||||
validationManager
|
||||
.registerMessageValidator(ForumSharingManager.CLIENT_ID,
|
||||
validator);
|
||||
return validator;
|
||||
}
|
||||
|
||||
@@ -83,14 +87,14 @@ public class SharingModule {
|
||||
@Singleton
|
||||
ForumSharingManager provideForumSharingManager(
|
||||
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||
MessageQueueManager messageQueueManager,
|
||||
ValidationManager validationManager,
|
||||
ConversationManager conversationManager, ForumManager forumManager,
|
||||
ForumSharingManagerImpl forumSharingManager) {
|
||||
|
||||
lifecycleManager.registerClient(forumSharingManager);
|
||||
contactManager.registerAddContactHook(forumSharingManager);
|
||||
contactManager.registerRemoveContactHook(forumSharingManager);
|
||||
messageQueueManager.registerIncomingMessageHook(
|
||||
validationManager.registerIncomingMessageHook(
|
||||
ForumSharingManager.CLIENT_ID, forumSharingManager);
|
||||
conversationManager.registerConversationClient(forumSharingManager);
|
||||
forumManager.registerRemoveForumHook(forumSharingManager);
|
||||
@@ -98,4 +102,37 @@ public class SharingModule {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.STORAGE_ID;
|
||||
|
||||
@Deprecated
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
abstract class SharingSessionState {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,7 +45,8 @@ public class ForumManagerTest
|
||||
forum0 = forumManager0.addForum("Test Forum");
|
||||
groupId0 = forum0.getId();
|
||||
// share forum
|
||||
forumSharingManager0.sendInvitation(groupId0, contactId1From0, null);
|
||||
forumSharingManager0.sendInvitation(groupId0, contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
sync0To1(1, true);
|
||||
forumSharingManager1.respondToInvitation(forum0, contact0From1, true);
|
||||
sync1To0(1, true);
|
||||
@@ -189,7 +190,8 @@ public class ForumManagerTest
|
||||
// share a second forum
|
||||
Forum forum1 = forumManager0.addForum("Test Forum1");
|
||||
GroupId g1 = forum1.getId();
|
||||
forumSharingManager0.sendInvitation(g1, contactId1From0, null);
|
||||
forumSharingManager0.sendInvitation(g1, contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
sync0To1(1, true);
|
||||
forumSharingManager1.respondToInvitation(forum1, contact0From1, true);
|
||||
sync1To0(1, true);
|
||||
|
||||
@@ -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.GroupInvitationResponse;
|
||||
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.BriarIntegrationTestComponent;
|
||||
import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent;
|
||||
@@ -97,10 +98,10 @@ public class GroupInvitationIntegrationTest
|
||||
GroupInvitationRequest request =
|
||||
(GroupInvitationRequest) messages.iterator().next();
|
||||
assertEquals(msg, request.getMessage());
|
||||
assertEquals(author0, request.getCreator());
|
||||
assertEquals(author0, request.getShareable().getCreator());
|
||||
assertEquals(timestamp, request.getTimestamp());
|
||||
assertEquals(contactId0From1, request.getContactId());
|
||||
assertEquals(privateGroup0.getName(), request.getGroupName());
|
||||
assertEquals(privateGroup0.getName(), request.getShareable().getName());
|
||||
assertFalse(request.isLocal());
|
||||
assertFalse(request.isRead());
|
||||
}
|
||||
@@ -123,7 +124,7 @@ public class GroupInvitationIntegrationTest
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
foundResponse = true;
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
InvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertEquals(contactId0From1, response.getContactId());
|
||||
assertTrue(response.isLocal());
|
||||
assertFalse(response.wasAccepted());
|
||||
@@ -140,7 +141,7 @@ public class GroupInvitationIntegrationTest
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
foundResponse = true;
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
InvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertEquals(contactId0From1, response.getContactId());
|
||||
assertFalse(response.isLocal());
|
||||
assertFalse(response.wasAccepted());
|
||||
@@ -172,7 +173,7 @@ public class GroupInvitationIntegrationTest
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
foundResponse = true;
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
InvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertTrue(response.wasAccepted());
|
||||
}
|
||||
}
|
||||
@@ -187,7 +188,7 @@ public class GroupInvitationIntegrationTest
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
foundResponse = true;
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
InvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertTrue(response.wasAccepted());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +90,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
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 ContactId contactId = new ContactId(0);
|
||||
private final Author author =
|
||||
@@ -141,8 +139,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(inviteeEngine));
|
||||
oneOf(engineFactory).createPeerEngine();
|
||||
will(returnValue(peerEngine));
|
||||
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID);
|
||||
will(returnValue(localGroup));
|
||||
}});
|
||||
MetadataParser metadataParser = context.mock(MetadataParser.class);
|
||||
MessageTracker messageTracker = context.mock(MessageTracker.class);
|
||||
@@ -156,7 +152,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testCreateLocalState() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).addGroup(txn, localGroup);
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(Collections.singletonList(contact)));
|
||||
}});
|
||||
@@ -651,6 +646,9 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
new InviteMessage(message.getId(), contactGroup.getId(),
|
||||
privateGroup.getId(), time1, "name", author,
|
||||
new byte[0], null, new byte[0]);
|
||||
final PrivateGroup pg =
|
||||
new PrivateGroup(privateGroup, invite.getGroupName(),
|
||||
invite.getCreator(), invite.getSalt());
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).startTransaction(true);
|
||||
@@ -668,12 +666,11 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(messageParser).parseMetadata(meta);
|
||||
will(returnValue(messageMetadata1));
|
||||
oneOf(db).getMessageStatus(txn, contactId, message.getId());
|
||||
oneOf(clientHelper).getMessage(txn, message.getId());
|
||||
will(returnValue(message));
|
||||
oneOf(clientHelper).toList(message);
|
||||
will(returnValue(body));
|
||||
oneOf(messageParser).parseInviteMessage(message, body);
|
||||
oneOf(messageParser).getInviteMessage(txn, message.getId());
|
||||
will(returnValue(invite));
|
||||
oneOf(privateGroupFactory).createPrivateGroup(invite.getGroupName(),
|
||||
invite.getCreator(), invite.getSalt());
|
||||
will(returnValue(pg));
|
||||
oneOf(db).containsGroup(txn, privateGroup.getId());
|
||||
will(returnValue(true));
|
||||
// second message
|
||||
@@ -742,21 +739,13 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroup.getId(), query);
|
||||
will(returnValue(results));
|
||||
// message 1
|
||||
oneOf(clientHelper).getMessage(txn, message.getId());
|
||||
will(returnValue(message));
|
||||
oneOf(clientHelper).toList(message);
|
||||
will(returnValue(body));
|
||||
oneOf(messageParser).parseInviteMessage(message, body);
|
||||
oneOf(messageParser).getInviteMessage(txn, message.getId());
|
||||
will(returnValue(inviteMessage1));
|
||||
oneOf(privateGroupFactory).createPrivateGroup(groupName, author,
|
||||
salt);
|
||||
will(returnValue(pg));
|
||||
// message 2
|
||||
oneOf(clientHelper).getMessage(txn, messageId2);
|
||||
will(returnValue(message2));
|
||||
oneOf(clientHelper).toList(message2);
|
||||
will(returnValue(body2));
|
||||
oneOf(messageParser).parseInviteMessage(message2, body2);
|
||||
oneOf(messageParser).getInviteMessage(txn, messageId2);
|
||||
will(returnValue(inviteMessage2));
|
||||
oneOf(privateGroupFactory).createPrivateGroup(groupName, author,
|
||||
salt);
|
||||
|
||||
@@ -144,11 +144,7 @@ public class InviteeProtocolEngineTest extends AbstractProtocolEngineTest {
|
||||
expectSendJoinMessage(properJoinMessage, true);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(messageTracker).trackOutgoingMessage(txn, message);
|
||||
oneOf(clientHelper).getMessage(txn, lastRemoteMessageId);
|
||||
will(returnValue(inviteMsg));
|
||||
oneOf(clientHelper).toList(inviteMsg);
|
||||
will(returnValue(inviteList));
|
||||
oneOf(messageParser).parseInviteMessage(inviteMsg, inviteList);
|
||||
oneOf(messageParser).getInviteMessage(txn, lastRemoteMessageId);
|
||||
will(returnValue(inviteMessage));
|
||||
oneOf(privateGroupFactory)
|
||||
.createPrivateGroup(inviteMessage.getGroupName(),
|
||||
|
||||
@@ -107,7 +107,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// create invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog1.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog1.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync invitation
|
||||
sync0To1(1, false);
|
||||
@@ -122,7 +123,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// invitee has own blog and that of the sharer
|
||||
assertEquals(2, blogManager1.getBlogs().size());
|
||||
@@ -194,7 +196,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, null);
|
||||
.sendInvitation(blog2.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -251,7 +254,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -313,7 +317,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// sharer sends invitation for 2's blog to 1
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -349,7 +354,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -399,7 +405,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// sharer sends invitation for 2's blog to 1
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
|
||||
@@ -2,19 +2,14 @@ package org.briarproject.briar.sharing;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
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.Metadata;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||
import org.briarproject.briar.api.client.MessageQueueManager;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.client.ProtocolStateException;
|
||||
import org.briarproject.briar.api.forum.Forum;
|
||||
import org.briarproject.briar.api.forum.ForumInvitationRequest;
|
||||
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
||||
@@ -39,11 +34,7 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
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.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.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -115,7 +106,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -171,7 +163,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -227,7 +220,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -245,9 +239,8 @@ public class ForumSharingIntegrationTest
|
||||
assertTrue(forumManager1.getForums().contains(forum0));
|
||||
|
||||
// sharer shares forum with invitee
|
||||
Contact c1 = contactManager0.getContact(contactId1From0);
|
||||
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
|
||||
.contains(c1));
|
||||
.contains(contact1From0));
|
||||
// invitee gets forum shared by sharer
|
||||
Contact contact0 = contactManager1.getContact(contactId1From0);
|
||||
assertTrue(forumSharingManager1.getSharedWith(forum0.getId())
|
||||
@@ -265,14 +258,16 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// sharer no longer shares forum with invitee
|
||||
assertFalse(forumSharingManager0.getSharedWith(forum0.getId())
|
||||
.contains(c1));
|
||||
.contains(contact1From0));
|
||||
// invitee no longer gets forum shared by sharer
|
||||
assertFalse(forumSharingManager1.getSharedWith(forum0.getId())
|
||||
.contains(contact0));
|
||||
// forum can be shared again
|
||||
assertTrue(forumSharingManager0.canBeShared(forum0.getId(), c1));
|
||||
Contact c0 = contactManager1.getContact(contactId0From1);
|
||||
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0));
|
||||
// forum can be shared again by sharer
|
||||
assertTrue(forumSharingManager0
|
||||
.canBeShared(forum0.getId(), contact1From0));
|
||||
// invitee that left can not share again
|
||||
assertFalse(forumSharingManager1
|
||||
.canBeShared(forum0.getId(), contact0From1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -282,7 +277,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -336,7 +332,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sharer un-subscribes from forum
|
||||
forumManager0.removeForum(forum0);
|
||||
@@ -360,7 +357,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sharer un-subscribes from forum
|
||||
forumManager0.removeForum(forum0);
|
||||
@@ -375,73 +373,15 @@ public class ForumSharingIntegrationTest
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
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
|
||||
@Test(expected = ProtocolStateException.class)
|
||||
public void testSharingSameForumWithEachOther() throws Exception {
|
||||
// initialize and let invitee accept all requests
|
||||
listenToEvents(true);
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -455,24 +395,12 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
assertEquals(2,
|
||||
forumSharingManager0.getInvitationMessages(contactId1From0)
|
||||
.size());
|
||||
|
||||
// invitee now shares same forum back
|
||||
forumSharingManager1.sendInvitation(forum0.getId(),
|
||||
contactId0From1,
|
||||
"I am re-sharing this forum with you.");
|
||||
|
||||
// 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());
|
||||
"I am re-sharing this forum with you.",
|
||||
clock.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -482,62 +410,48 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// invitee adds the same forum
|
||||
Transaction txn = db1.startTransaction(false);
|
||||
db1.addGroup(txn, forum0.getGroup());
|
||||
forumManager1.addForum(txn, forum0);
|
||||
db1.commitTransaction(txn);
|
||||
db1.endTransaction(txn);
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// invitee now shares same forum back
|
||||
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
|
||||
Bytes key0 = new Bytes(author0.getPublicKey());
|
||||
Bytes key1 = new Bytes(author1.getPublicKey());
|
||||
// prevent automatic responses
|
||||
respond = false;
|
||||
|
||||
// only now sync first request message
|
||||
boolean alice = key1.compareTo(key0) < 0;
|
||||
if (alice) {
|
||||
sync0To1(1, false);
|
||||
assertFalse(listener1.requestReceived);
|
||||
} else {
|
||||
sync0To1(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
}
|
||||
sync0To1(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// sync second invitation
|
||||
alice = key0.compareTo(key1) < 0;
|
||||
if (alice) {
|
||||
sync1To0(1, false);
|
||||
assertFalse(listener0.requestReceived);
|
||||
// sync second invitation which counts as accept
|
||||
sync1To0(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.requestReceived);
|
||||
|
||||
// sharer did not receive request, but response to own request
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
// both peers should share the forum with each other now
|
||||
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
|
||||
.contains(contact1From0));
|
||||
assertTrue(forumSharingManager1.getSharedWith(forum0.getId())
|
||||
.contains(contact0From1));
|
||||
|
||||
assertEquals(2, forumSharingManager0
|
||||
.getInvitationMessages(contactId1From0).size());
|
||||
assertEquals(3, forumSharingManager1
|
||||
.getInvitationMessages(contactId0From1).size());
|
||||
} else {
|
||||
sync1To0(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.requestReceived);
|
||||
// and both have each other's invitations (and no response)
|
||||
assertEquals(2, forumSharingManager0
|
||||
.getInvitationMessages(contactId1From0).size());
|
||||
assertEquals(2, forumSharingManager1
|
||||
.getInvitationMessages(contactId0From1).size());
|
||||
|
||||
// send response from sharer to invitee and make sure it arrived
|
||||
sync0To1(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.responseReceived);
|
||||
|
||||
assertEquals(3, forumSharingManager0
|
||||
.getInvitationMessages(contactId1From0).size());
|
||||
assertEquals(2, forumSharingManager1
|
||||
.getInvitationMessages(contactId0From1).size());
|
||||
}
|
||||
// there are no more open invitations
|
||||
assertTrue(forumSharingManager0.getInvitations().isEmpty());
|
||||
assertTrue(forumSharingManager1.getInvitations().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -547,7 +461,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -564,18 +479,12 @@ public class ForumSharingIntegrationTest
|
||||
assertEquals(1,
|
||||
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
|
||||
removeAllContacts();
|
||||
|
||||
// invitee still has forum
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
|
||||
// make sure sharer does share the forum with nobody now
|
||||
assertEquals(0,
|
||||
forumSharingManager0.getSharedWith(forum0.getId()).size());
|
||||
@@ -584,35 +493,30 @@ public class ForumSharingIntegrationTest
|
||||
addDefaultContacts();
|
||||
addContacts1And2();
|
||||
|
||||
// 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();
|
||||
// forum can be shared with contacts again
|
||||
assertTrue(forumSharingManager0
|
||||
.canBeShared(forum0.getId(), contact1From0));
|
||||
assertTrue(forumSharingManager0
|
||||
.canBeShared(forum0.getId(), contact2From0));
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// construct a new message re-using the old SessionId
|
||||
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
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
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);
|
||||
|
||||
// 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
|
||||
@@ -633,14 +537,16 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
|
||||
// second sharer sends invitation for same forum
|
||||
assertTrue(contactId1From2 != null);
|
||||
forumSharingManager2
|
||||
.sendInvitation(forum0.getId(), contactId1From2, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From2, null,
|
||||
clock.currentTimeMillis());
|
||||
// sync second request message
|
||||
sync2To1(1, true);
|
||||
|
||||
@@ -650,13 +556,6 @@ public class ForumSharingIntegrationTest
|
||||
assertEquals(1, forums.size());
|
||||
assertEquals(2, forums.iterator().next().getNewSharers().size());
|
||||
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
|
||||
assertNotNull(contactId2From1);
|
||||
@@ -675,6 +574,11 @@ public class ForumSharingIntegrationTest
|
||||
sync1To0(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// make sure both sharers actually share the forum
|
||||
Collection<Contact> contacts =
|
||||
forumSharingManager1.getSharedWith(forum0.getId());
|
||||
assertEquals(2, contacts.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -684,7 +588,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -745,7 +650,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation again
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -799,8 +705,10 @@ public class ForumSharingIntegrationTest
|
||||
requestReceived = true;
|
||||
Forum f = event.getShareable();
|
||||
try {
|
||||
Contact c = contactManager0.getContact(contactId1From0);
|
||||
forumSharingManager0.respondToInvitation(f, c, true);
|
||||
if (respond) {
|
||||
Contact c = contactManager0.getContact(contactId1From0);
|
||||
forumSharingManager0.respondToInvitation(f, c, true);
|
||||
}
|
||||
} catch (DbException ex) {
|
||||
eventWaiter.rethrow(ex);
|
||||
} finally {
|
||||
@@ -814,7 +722,6 @@ public class ForumSharingIntegrationTest
|
||||
private class InviteeListener implements EventListener {
|
||||
|
||||
private volatile boolean requestReceived = false;
|
||||
private volatile boolean responseReceived = false;
|
||||
|
||||
private final boolean accept, answer;
|
||||
|
||||
@@ -836,13 +743,13 @@ public class ForumSharingIntegrationTest
|
||||
if (!answer) return;
|
||||
Forum f = event.getShareable();
|
||||
try {
|
||||
eventWaiter.assertEquals(1,
|
||||
forumSharingManager1.getInvitations().size());
|
||||
SharingInvitationItem invitation =
|
||||
forumSharingManager1.getInvitations().iterator()
|
||||
.next();
|
||||
eventWaiter.assertEquals(f, invitation.getShareable());
|
||||
if (respond) {
|
||||
eventWaiter.assertEquals(1,
|
||||
forumSharingManager1.getInvitations().size());
|
||||
SharingInvitationItem invitation =
|
||||
forumSharingManager1.getInvitations().iterator()
|
||||
.next();
|
||||
eventWaiter.assertEquals(f, invitation.getShareable());
|
||||
Contact c =
|
||||
contactManager1
|
||||
.getContact(event.getContactId());
|
||||
@@ -859,7 +766,6 @@ public class ForumSharingIntegrationTest
|
||||
ForumInvitationResponseReceivedEvent event =
|
||||
(ForumInvitationResponseReceivedEvent) e;
|
||||
eventWaiter.assertEquals(contactId0From1, event.getContactId());
|
||||
responseReceived = true;
|
||||
eventWaiter.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,340 +4,314 @@ import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.UniqueId;
|
||||
import org.briarproject.bramble.api.client.BdfMessageContext;
|
||||
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.sync.MessageId;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
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 java.util.Collection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_NAME;
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
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.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;
|
||||
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.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
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 =
|
||||
TestUtils.getRandomString(MAX_FORUM_NAME_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 =
|
||||
TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH);
|
||||
private final BdfDictionary meta =
|
||||
BdfDictionary.of(new BdfEntry("meta", "data"));
|
||||
|
||||
@Test
|
||||
public void testAcceptsInvitationWithContent() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
expectCreateForum(forumName);
|
||||
expectEncodeMetadata(INVITE);
|
||||
BdfMessageContext messageContext = v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
salt, content));
|
||||
assertExpectedContextForInvitation(messageContext, forumName, content);
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, descriptor,
|
||||
content));
|
||||
assertExpectedContext(messageContext, previousMsgId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsInvitationWithoutContent() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
public void testAcceptsInvitationWithNullContent() throws Exception {
|
||||
expectCreateForum(forumName);
|
||||
expectEncodeMetadata(INVITE);
|
||||
BdfMessageContext messageContext = v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
salt));
|
||||
assertExpectedContextForInvitation(messageContext, forumName, null);
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, descriptor, null));
|
||||
assertExpectedContext(messageContext, previousMsgId);
|
||||
}
|
||||
|
||||
@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
|
||||
public void testAcceptsAccept() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
expectEncodeMetadata(ACCEPT);
|
||||
BdfMessageContext messageContext = v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_ACCEPT, sessionId));
|
||||
assertExpectedContext(messageContext, SHARE_MSG_TYPE_ACCEPT);
|
||||
BdfList.of(ACCEPT.getValue(), groupId, previousMsgId));
|
||||
assertExpectedContext(messageContext, previousMsgId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsDecline() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
expectEncodeMetadata(DECLINE);
|
||||
BdfMessageContext messageContext = v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_DECLINE, sessionId));
|
||||
assertExpectedContext(messageContext, SHARE_MSG_TYPE_DECLINE);
|
||||
BdfList.of(DECLINE.getValue(), groupId, previousMsgId));
|
||||
assertExpectedContext(messageContext, previousMsgId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsLeave() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
expectEncodeMetadata(LEAVE);
|
||||
BdfMessageContext messageContext = v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_LEAVE, sessionId));
|
||||
assertExpectedContext(messageContext, SHARE_MSG_TYPE_LEAVE);
|
||||
BdfList.of(LEAVE.getValue(), groupId, previousMsgId));
|
||||
assertExpectedContext(messageContext, previousMsgId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsAbort() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
expectEncodeMetadata(ABORT);
|
||||
BdfMessageContext messageContext = v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_ABORT, sessionId));
|
||||
assertExpectedContext(messageContext, SHARE_MSG_TYPE_ABORT);
|
||||
BdfList.of(ABORT.getValue(), groupId, previousMsgId));
|
||||
assertExpectedContext(messageContext, previousMsgId);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsNullMessageType() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
v.validateMessage(message, group, BdfList.of(null, sessionId));
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(null, groupId, previousMsgId));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsNonLongMessageType() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
v.validateMessage(message, group, BdfList.of("", sessionId));
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of("", groupId, previousMsgId));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsInvalidMessageType() throws Exception {
|
||||
int invalidMessageType = SHARE_MSG_TYPE_ABORT + 1;
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
int invalidMessageType = ABORT.getValue() + 1;
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(invalidMessageType, sessionId));
|
||||
BdfList.of(invalidMessageType, groupId, previousMsgId));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsNullSessionId() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_ABORT, null));
|
||||
BdfList.of(ABORT.getValue(), null, previousMsgId));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsNonRawSessionId() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_ABORT, 123));
|
||||
v.validateMessage(message, group, BdfList.of(ABORT.getValue(), 123));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooShortSessionId() throws Exception {
|
||||
byte[] invalidSessionId = TestUtils.getRandomBytes(UniqueId.LENGTH - 1);
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
byte[] invalidGroupId = TestUtils.getRandomBytes(UniqueId.LENGTH - 1);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_ABORT, invalidSessionId));
|
||||
BdfList.of(ABORT.getValue(), invalidGroupId, previousMsgId));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooLongSessionId() throws Exception {
|
||||
byte[] invalidSessionId = TestUtils.getRandomBytes(UniqueId.LENGTH + 1);
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
byte[] invalidGroupId = TestUtils.getRandomBytes(UniqueId.LENGTH + 1);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_ABORT, invalidSessionId));
|
||||
BdfList.of(ABORT.getValue(), invalidGroupId, previousMsgId));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooShortBodyForAbort() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
v.validateMessage(message, group, BdfList.of(SHARE_MSG_TYPE_ABORT));
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(ABORT.getValue(), groupId));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooLongBodyForAbort() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_ABORT, sessionId, 123));
|
||||
BdfList.of(ABORT.getValue(), groupId, previousMsgId, 123));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooShortBodyForInvitation() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, descriptor));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooLongBodyForInvitation() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
salt, content, 123));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, descriptor, null,
|
||||
123));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsNullForumName() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
BdfList invalidDescriptor = BdfList.of(null, salt);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, null,
|
||||
salt, content));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
|
||||
null));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsNonStringForumName() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
BdfList invalidDescriptor = BdfList.of(123, salt);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, 123,
|
||||
salt, content));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
|
||||
null));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooShortForumName() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
BdfList invalidDescriptor = BdfList.of("", salt);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, "",
|
||||
salt, content));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
|
||||
null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsMinLengthForumName() throws Exception {
|
||||
String shortForumName = TestUtils.getRandomString(1);
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
BdfList validDescriptor = BdfList.of(shortForumName, salt);
|
||||
expectCreateForum(shortForumName);
|
||||
expectEncodeMetadata(INVITE);
|
||||
BdfMessageContext messageContext = v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, shortForumName,
|
||||
salt, content));
|
||||
assertExpectedContextForInvitation(messageContext, shortForumName,
|
||||
content);
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, validDescriptor,
|
||||
null));
|
||||
assertExpectedContext(messageContext, previousMsgId);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooLongForumName() throws Exception {
|
||||
String invalidForumName =
|
||||
TestUtils.getRandomString(MAX_FORUM_NAME_LENGTH + 1);
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
BdfList invalidDescriptor = BdfList.of(invalidForumName, salt);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId,
|
||||
invalidForumName, salt, content));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
|
||||
null));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsNullSalt() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
BdfList invalidDescriptor = BdfList.of(forumName, null);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
null, content));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
|
||||
null));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsNonRawSalt() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
BdfList invalidDescriptor = BdfList.of(forumName, 123);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
123, content));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
|
||||
null));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooShortSalt() throws Exception {
|
||||
byte[] invalidSalt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH - 1);
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
BdfList invalidDescriptor = BdfList.of(forumName, invalidSalt);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
invalidSalt, content));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
|
||||
null));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooLongSalt() throws Exception {
|
||||
byte[] invalidSalt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH + 1);
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
BdfList invalidDescriptor = BdfList.of(forumName, invalidSalt);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
invalidSalt, content));
|
||||
}
|
||||
|
||||
@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));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
|
||||
null));
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsNonStringContent() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
expectCreateForum(forumName);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
salt, 123));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, descriptor,
|
||||
123));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsMinLengthContent() throws Exception {
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
expectCreateForum(forumName);
|
||||
expectEncodeMetadata(INVITE);
|
||||
BdfMessageContext messageContext = v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
salt, ""));
|
||||
assertExpectedContextForInvitation(messageContext, forumName, "");
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, descriptor, "1"));
|
||||
assertExpectedContext(messageContext, previousMsgId);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testRejectsTooLongContent() throws Exception {
|
||||
String invalidContent =
|
||||
TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH + 1);
|
||||
ForumSharingValidator v = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
expectCreateForum(forumName);
|
||||
v.validateMessage(message, group,
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId, forumName,
|
||||
salt, invalidContent));
|
||||
BdfList.of(INVITE.getValue(), previousMsgId, descriptor,
|
||||
invalidContent));
|
||||
}
|
||||
|
||||
private void assertExpectedContextForInvitation(
|
||||
BdfMessageContext messageContext, String forumName,
|
||||
@Nullable String content) throws FormatException {
|
||||
BdfDictionary meta = messageContext.getDictionary();
|
||||
if (content == null) {
|
||||
assertEquals(6, meta.size());
|
||||
} else {
|
||||
assertEquals(7, meta.size());
|
||||
assertEquals(content, meta.getString(INVITATION_MSG));
|
||||
}
|
||||
assertEquals(forumName, meta.getString(FORUM_NAME));
|
||||
assertEquals(salt, meta.getRaw(FORUM_SALT));
|
||||
assertEquals(SHARE_MSG_TYPE_INVITATION, meta.getLong(TYPE).intValue());
|
||||
assertEquals(sessionId.getBytes(), meta.getRaw(SESSION_ID));
|
||||
assertFalse(meta.getBoolean(LOCAL));
|
||||
assertEquals(timestamp, meta.getLong(TIME).longValue());
|
||||
assertEquals(0, messageContext.getDependencies().size());
|
||||
private void expectCreateForum(final String name) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(forumFactory).createForum(name, salt);
|
||||
will(returnValue(forum));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectEncodeMetadata(final MessageType type) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(messageEncoder)
|
||||
.encodeMetadata(type, groupId, timestamp, false, false,
|
||||
false, false);
|
||||
will(returnValue(meta));
|
||||
}});
|
||||
}
|
||||
|
||||
private void assertExpectedContext(BdfMessageContext messageContext,
|
||||
int type) throws FormatException {
|
||||
BdfDictionary meta = messageContext.getDictionary();
|
||||
assertEquals(4, meta.size());
|
||||
assertEquals(type, meta.getLong(TYPE).intValue());
|
||||
assertEquals(sessionId.getBytes(), meta.getRaw(SESSION_ID));
|
||||
assertFalse(meta.getBoolean(LOCAL));
|
||||
assertEquals(timestamp, meta.getLong(TIME).longValue());
|
||||
assertEquals(0, messageContext.getDependencies().size());
|
||||
@Nullable MessageId previousMsgId) throws FormatException {
|
||||
Collection<MessageId> dependencies = messageContext.getDependencies();
|
||||
if (previousMsgId == null) {
|
||||
assertTrue(dependencies.isEmpty());
|
||||
} else {
|
||||
assertEquals(1, dependencies.size());
|
||||
assertTrue(dependencies.contains(previousMsgId));
|
||||
}
|
||||
assertEquals(meta, messageContext.getDictionary());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user