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