mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
New Forum Sharing Client
This commit is contained in:
@@ -1020,13 +1020,13 @@ public class ConversationActivity extends BriarActivity
|
||||
@DatabaseExecutor
|
||||
private void respondToForumRequest(SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
forumSharingManager.respondToInvitation(id, accept);
|
||||
forumSharingManager.respondToInvitation(contactId, id, accept);
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
private void respondToBlogRequest(SessionId id, boolean accept)
|
||||
throws DbException {
|
||||
blogSharingManager.respondToInvitation(id, accept);
|
||||
blogSharingManager.respondToInvitation(contactId, id, accept);
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
|
||||
@@ -170,8 +170,7 @@ abstract class ConversationItem {
|
||||
} else if (ir instanceof GroupInvitationRequest) {
|
||||
text = ctx.getString(
|
||||
R.string.groups_invitations_invitation_sent,
|
||||
contactName,
|
||||
((GroupInvitationRequest) ir).getGroupName());
|
||||
contactName, ir.getShareable().getName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown InvitationRequest");
|
||||
}
|
||||
@@ -194,8 +193,7 @@ abstract class ConversationItem {
|
||||
} else if (ir instanceof GroupInvitationRequest) {
|
||||
text = ctx.getString(
|
||||
R.string.groups_invitations_invitation_received,
|
||||
contactName,
|
||||
((GroupInvitationRequest) ir).getGroupName());
|
||||
contactName, ir.getShareable().getName());
|
||||
type = GROUP;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown InvitationRequest");
|
||||
@@ -203,7 +201,7 @@ abstract class ConversationItem {
|
||||
return new ConversationRequestItem(ir.getId(),
|
||||
ir.getGroupId(), type, ir.getSessionId(), text,
|
||||
ir.getMessage(), ir.getTimestamp(), ir.isRead(),
|
||||
ir.getInvitedGroupId(), !ir.isAvailable(),
|
||||
ir.getShareable().getId(), !ir.isAvailable(),
|
||||
ir.canBeOpened());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
|
||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||
import org.briarproject.briar.api.messaging.ConversationManager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -22,6 +24,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -31,14 +34,19 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
|
||||
private final static Logger LOG =
|
||||
Logger.getLogger(ShareBlogControllerImpl.class.getName());
|
||||
|
||||
private final ConversationManager conversationManager;
|
||||
private final BlogSharingManager blogSharingManager;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
ShareBlogControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||
BlogSharingManager blogSharingManager) {
|
||||
ConversationManager conversationManager,
|
||||
BlogSharingManager blogSharingManager, Clock clock) {
|
||||
super(dbExecutor, lifecycleManager, contactManager);
|
||||
this.conversationManager = conversationManager;
|
||||
this.blogSharingManager = blogSharingManager;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,15 +56,19 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
|
||||
|
||||
@Override
|
||||
public void share(final GroupId g, final Collection<ContactId> contacts,
|
||||
final String msg,
|
||||
final String message,
|
||||
final ExceptionHandler<DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
String msg = isNullOrEmpty(message) ? null : message;
|
||||
for (ContactId c : contacts) {
|
||||
try {
|
||||
blogSharingManager.sendInvitation(g, c, msg);
|
||||
long time = Math.max(clock.currentTimeMillis(),
|
||||
conversationManager.getGroupCount(c)
|
||||
.getLatestMsgTime() + 1);
|
||||
blogSharingManager.sendInvitation(g, c, msg, time);
|
||||
} catch (NoSuchContactException | NoSuchGroupException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
|
||||
@@ -10,9 +10,11 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
|
||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||
import org.briarproject.briar.api.messaging.ConversationManager;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -22,6 +24,7 @@ import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -31,14 +34,19 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
|
||||
private final static Logger LOG =
|
||||
Logger.getLogger(ShareForumControllerImpl.class.getName());
|
||||
|
||||
private final ConversationManager conversationManager;
|
||||
private final ForumSharingManager forumSharingManager;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
ShareForumControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||
ForumSharingManager forumSharingManager) {
|
||||
ConversationManager conversationManager,
|
||||
ForumSharingManager forumSharingManager, Clock clock) {
|
||||
super(dbExecutor, lifecycleManager, contactManager);
|
||||
this.conversationManager = conversationManager;
|
||||
this.forumSharingManager = forumSharingManager;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,15 +56,19 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
|
||||
|
||||
@Override
|
||||
public void share(final GroupId g, final Collection<ContactId> contacts,
|
||||
final String msg,
|
||||
final String message,
|
||||
final ExceptionHandler<DbException> handler) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
String msg = isNullOrEmpty(message) ? null : message;
|
||||
for (ContactId c : contacts) {
|
||||
try {
|
||||
forumSharingManager.sendInvitation(g, c, msg);
|
||||
long time = Math.max(clock.currentTimeMillis(),
|
||||
conversationManager.getGroupCount(c)
|
||||
.getLatestMsgTime() + 1);
|
||||
forumSharingManager.sendInvitation(g, c, msg, time);
|
||||
} catch (NoSuchContactException | NoSuchGroupException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package org.briarproject.briar.api.blog;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.AuthorId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
@@ -10,7 +13,7 @@ import org.briarproject.briar.api.sharing.InvitationRequest;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@NotNullByDefault
|
||||
public class BlogInvitationRequest extends InvitationRequest {
|
||||
public class BlogInvitationRequest extends InvitationRequest<Blog> {
|
||||
|
||||
private final String blogAuthorName;
|
||||
|
||||
@@ -19,9 +22,12 @@ public class BlogInvitationRequest extends InvitationRequest {
|
||||
@Nullable String message, GroupId blogId,
|
||||
boolean available, boolean canBeOpened, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, message, blogId, available,
|
||||
canBeOpened, time, local, sent, seen, read);
|
||||
// TODO pass a proper blog here when redoing the BlogSharingManager
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId,
|
||||
new Blog(new Group(blogId, BlogManager.CLIENT_ID, new byte[0]),
|
||||
new Author(new AuthorId(new byte[AuthorId.LENGTH]),
|
||||
blogAuthorName, new byte[0])), contactId,
|
||||
message, available, canBeOpened);
|
||||
this.blogAuthorName = blogAuthorName;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,8 @@ public class BlogInvitationResponse extends InvitationResponse {
|
||||
GroupId groupId, ContactId contactId, GroupId blogId,
|
||||
boolean accept, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, blogId, accept, time, local,
|
||||
sent, seen, read);
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, blogId,
|
||||
contactId, accept);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,23 +12,18 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class ForumInvitationRequest extends InvitationRequest {
|
||||
public class ForumInvitationRequest extends InvitationRequest<Forum> {
|
||||
|
||||
private final String forumName;
|
||||
|
||||
public ForumInvitationRequest(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId, GroupId forumId,
|
||||
String forumName, @Nullable String message, boolean available,
|
||||
boolean canBeOpened, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, message, forumId, available,
|
||||
canBeOpened, time, local, sent, seen, read);
|
||||
this.forumName = forumName;
|
||||
public ForumInvitationRequest(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, Forum forum, ContactId contactId,
|
||||
@Nullable String message, boolean available, boolean canBeOpened) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, forum,
|
||||
contactId, message, available, canBeOpened);
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
return getShareable().getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,13 +13,12 @@ import javax.annotation.concurrent.Immutable;
|
||||
@NotNullByDefault
|
||||
public class ForumInvitationResponse extends InvitationResponse {
|
||||
|
||||
public ForumInvitationResponse(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId, GroupId forumId,
|
||||
boolean accept, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, forumId, accept, time, local,
|
||||
sent, seen, read);
|
||||
public ForumInvitationResponse(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, GroupId forumId, ContactId contactId,
|
||||
boolean accept) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, forumId,
|
||||
contactId, accept);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ public interface ForumManager {
|
||||
*/
|
||||
Forum addForum(String name) throws DbException;
|
||||
|
||||
/**
|
||||
* Subscribes to a forum within the given {@link Transaction}.
|
||||
*/
|
||||
void addForum(Transaction txn, Forum f) throws DbException;
|
||||
|
||||
/**
|
||||
* Unsubscribes from a forum.
|
||||
*/
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
package org.briarproject.briar.api.forum.event;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
||||
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class ForumInvitationResponseReceivedEvent extends
|
||||
InvitationResponseReceivedEvent {
|
||||
|
||||
private final String forumName;
|
||||
|
||||
public ForumInvitationResponseReceivedEvent(String forumName,
|
||||
ContactId contactId, ForumInvitationResponse response) {
|
||||
public ForumInvitationResponseReceivedEvent(ContactId contactId,
|
||||
ForumInvitationResponse response) {
|
||||
super(contactId, response);
|
||||
this.forumName = forumName;
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.briarproject.briar.api.privategroup.invitation;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.briar.api.sharing.InvitationRequest;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -13,28 +13,14 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class GroupInvitationRequest extends InvitationRequest {
|
||||
public class GroupInvitationRequest extends InvitationRequest<PrivateGroup> {
|
||||
|
||||
private final String groupName;
|
||||
private final Author creator;
|
||||
|
||||
public GroupInvitationRequest(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId, @Nullable String message,
|
||||
GroupId privateGroupId, String groupName, Author creator,
|
||||
boolean available, boolean canBeOpened, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
super(id, sessionId, groupId, contactId, message, privateGroupId,
|
||||
available, canBeOpened, time, local, sent, seen, read);
|
||||
this.groupName = groupName;
|
||||
this.creator = creator;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return groupName;
|
||||
}
|
||||
|
||||
public Author getCreator() {
|
||||
return creator;
|
||||
public GroupInvitationRequest(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, PrivateGroup shareable, ContactId contactId,
|
||||
@Nullable String message, boolean available, boolean canBeOpened) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, shareable,
|
||||
contactId, message, available, canBeOpened);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,11 +13,12 @@ import javax.annotation.concurrent.Immutable;
|
||||
@NotNullByDefault
|
||||
public class GroupInvitationResponse extends InvitationResponse {
|
||||
|
||||
public GroupInvitationResponse(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId, GroupId privateGroupId,
|
||||
boolean accept, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
super(id, sessionId, groupId, contactId, privateGroupId, accept, time,
|
||||
local, sent, seen, read);
|
||||
public GroupInvitationResponse(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, GroupId shareableId, ContactId contactId,
|
||||
boolean accept) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId,
|
||||
shareableId, contactId, accept);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,25 +7,22 @@ import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.BaseMessageHeader;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public abstract class InvitationMessage extends BaseMessageHeader {
|
||||
public class InvitationMessage extends BaseMessageHeader {
|
||||
|
||||
private final SessionId sessionId;
|
||||
private final ContactId contactId;
|
||||
private final GroupId invitedGroupId;
|
||||
|
||||
public InvitationMessage(MessageId id, SessionId sessionId, GroupId groupId,
|
||||
ContactId contactId, GroupId invitedGroupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
public InvitationMessage(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, ContactId contactId) {
|
||||
|
||||
super(id, groupId, time, local, read, sent, seen);
|
||||
this.sessionId = sessionId;
|
||||
this.contactId = contactId;
|
||||
this.invitedGroupId = invitedGroupId;
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
@@ -36,9 +33,4 @@ public abstract class InvitationMessage extends BaseMessageHeader {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public GroupId getInvitedGroupId() {
|
||||
return invitedGroupId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,20 +11,19 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public abstract class InvitationRequest extends InvitationMessage {
|
||||
public class InvitationRequest<S extends Shareable> extends InvitationMessage {
|
||||
|
||||
private final S shareable;
|
||||
@Nullable
|
||||
private final String message;
|
||||
private final boolean available, canBeOpened;
|
||||
|
||||
public InvitationRequest(MessageId id, SessionId sessionId, GroupId groupId,
|
||||
ContactId contactId, @Nullable String message,
|
||||
GroupId invitedGroupId, boolean available,
|
||||
boolean canBeOpened, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
super(id, sessionId, groupId, contactId, invitedGroupId, time, local,
|
||||
sent, seen, read);
|
||||
if (available && canBeOpened) throw new IllegalArgumentException();
|
||||
public InvitationRequest(MessageId id, GroupId groupId, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, S shareable, ContactId contactId,
|
||||
@Nullable String message, boolean available, boolean canBeOpened) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, contactId);
|
||||
this.shareable = shareable;
|
||||
this.message = message;
|
||||
this.available = available;
|
||||
this.canBeOpened = canBeOpened;
|
||||
@@ -43,4 +42,8 @@ public abstract class InvitationRequest extends InvitationMessage {
|
||||
return canBeOpened;
|
||||
}
|
||||
|
||||
public S getShareable() {
|
||||
return shareable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,21 +10,26 @@ import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public abstract class InvitationResponse extends InvitationMessage {
|
||||
public class InvitationResponse extends InvitationMessage {
|
||||
|
||||
private final GroupId shareableId;
|
||||
private final boolean accept;
|
||||
|
||||
public InvitationResponse(MessageId id, SessionId sessionId,
|
||||
GroupId groupId, ContactId contactId,
|
||||
GroupId invitedGroupId, boolean accept, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
|
||||
super(id, sessionId, groupId, contactId, invitedGroupId, time, local,
|
||||
sent, seen, read);
|
||||
public InvitationResponse(MessageId id, GroupId groupId,
|
||||
long time, boolean local, boolean sent, boolean seen,
|
||||
boolean read, SessionId sessionId, GroupId shareableId,
|
||||
ContactId contactId, boolean accept) {
|
||||
super(id, groupId, time, local, sent, seen, read, sessionId, contactId);
|
||||
this.shareableId = shareableId;
|
||||
this.accept = accept;
|
||||
}
|
||||
|
||||
public boolean wasAccepted() {
|
||||
return accept;
|
||||
}
|
||||
|
||||
public GroupId getShareableId() {
|
||||
return shareableId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ public interface SharingManager<S extends Shareable>
|
||||
* Sends an invitation to share the given group with the given contact
|
||||
* and sends an optional message along with it.
|
||||
*/
|
||||
void sendInvitation(GroupId groupId, ContactId contactId,
|
||||
@Nullable String message) throws DbException;
|
||||
void sendInvitation(GroupId shareableId, ContactId contactId,
|
||||
@Nullable String message, long timestamp) throws DbException;
|
||||
|
||||
/**
|
||||
* Responds to a pending group invitation
|
||||
@@ -32,7 +32,7 @@ public interface SharingManager<S extends Shareable>
|
||||
/**
|
||||
* Responds to a pending group invitation
|
||||
*/
|
||||
void respondToInvitation(SessionId id, boolean accept)
|
||||
void respondToInvitation(ContactId c, SessionId id, boolean accept)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -108,6 +108,11 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addForum(Transaction txn, Forum f) throws DbException {
|
||||
db.addGroup(txn, f.getGroup());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeForum(Forum f) throws DbException {
|
||||
Transaction txn = db.startTransaction(false);
|
||||
|
||||
@@ -254,8 +254,8 @@ class CreatorProtocolEngine extends AbstractProtocolEngine<CreatorSession> {
|
||||
private GroupInvitationResponse createInvitationResponse(
|
||||
GroupInvitationMessage m, ContactId c, boolean accept) {
|
||||
SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes());
|
||||
return new GroupInvitationResponse(m.getId(), sessionId,
|
||||
m.getContactGroupId(), c, m.getPrivateGroupId(), accept,
|
||||
m.getTimestamp(), false, false, true, false);
|
||||
return new GroupInvitationResponse(m.getId(), m.getContactGroupId(),
|
||||
m.getTimestamp(), false, false, true, false, sessionId,
|
||||
m.getPrivateGroupId(), c, accept);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,6 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
private final ProtocolEngine<CreatorSession> creatorEngine;
|
||||
private final ProtocolEngine<InviteeSession> inviteeEngine;
|
||||
private final ProtocolEngine<PeerSession> peerEngine;
|
||||
private final Group localGroup;
|
||||
|
||||
@Inject
|
||||
GroupInvitationManagerImpl(DatabaseComponent db,
|
||||
@@ -93,12 +92,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
creatorEngine = engineFactory.createCreatorEngine();
|
||||
inviteeEngine = engineFactory.createInviteeEngine();
|
||||
peerEngine = engineFactory.createPeerEngine();
|
||||
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLocalState(Transaction txn) throws DbException {
|
||||
db.addGroup(txn, localGroup);
|
||||
// Ensure we've set things up for any pre-existing contacts
|
||||
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
||||
}
|
||||
@@ -405,13 +402,14 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
SessionId sessionId = getSessionId(meta.getPrivateGroupId());
|
||||
// Look up the invite message to get the details of the private group
|
||||
InviteMessage invite = getInviteMessage(txn, m);
|
||||
PrivateGroup pg = privateGroupFactory
|
||||
.createPrivateGroup(invite.getGroupName(), invite.getCreator(),
|
||||
invite.getSalt());
|
||||
boolean canBeOpened = db.containsGroup(txn, invite.getPrivateGroupId());
|
||||
return new GroupInvitationRequest(m, sessionId, contactGroupId, c,
|
||||
invite.getMessage(), invite.getPrivateGroupId(),
|
||||
invite.getGroupName(), invite.getCreator(),
|
||||
meta.isAvailableToAnswer(), canBeOpened, meta.getTimestamp(),
|
||||
meta.isLocal(), status.isSent(), status.isSeen(),
|
||||
meta.isRead());
|
||||
return new GroupInvitationRequest(m, contactGroupId,
|
||||
meta.getTimestamp(), meta.isLocal(), status.isSent(),
|
||||
status.isSeen(), meta.isRead(), sessionId, pg, c,
|
||||
invite.getMessage(), meta.isAvailableToAnswer(), canBeOpened);
|
||||
}
|
||||
|
||||
private InviteMessage getInviteMessage(Transaction txn, MessageId m)
|
||||
@@ -427,10 +425,10 @@ class GroupInvitationManagerImpl extends ConversationClientImpl
|
||||
MessageStatus status, boolean accept)
|
||||
throws DbException, FormatException {
|
||||
SessionId sessionId = getSessionId(meta.getPrivateGroupId());
|
||||
return new GroupInvitationResponse(m, sessionId, contactGroupId, c,
|
||||
meta.getPrivateGroupId(), accept, meta.getTimestamp(),
|
||||
meta.isLocal(), status.isSent(), status.isSeen(),
|
||||
meta.isRead());
|
||||
return new GroupInvitationResponse(m, contactGroupId,
|
||||
meta.getTimestamp(), meta.isLocal(), status.isSent(),
|
||||
status.isSeen(), meta.isRead(), sessionId,
|
||||
meta.getPrivateGroupId(), c, accept);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -234,8 +234,9 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
// Broadcast an event
|
||||
PrivateGroup privateGroup = privateGroupFactory.createPrivateGroup(
|
||||
m.getGroupName(), m.getCreator(), m.getSalt());
|
||||
txn.attach(new GroupInvitationRequestReceivedEvent(privateGroup,
|
||||
contactId, createInvitationRequest(m, contactId)));
|
||||
txn.attach(
|
||||
new GroupInvitationRequestReceivedEvent(privateGroup, contactId,
|
||||
createInvitationRequest(m, privateGroup, contactId)));
|
||||
// Move to the INVITED state
|
||||
return new InviteeSession(s.getContactGroupId(), s.getPrivateGroupId(),
|
||||
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),
|
||||
@@ -317,11 +318,11 @@ class InviteeProtocolEngine extends AbstractProtocolEngine<InviteeSession> {
|
||||
}
|
||||
|
||||
private GroupInvitationRequest createInvitationRequest(InviteMessage m,
|
||||
ContactId c) {
|
||||
PrivateGroup pg, ContactId c) {
|
||||
SessionId sessionId = new SessionId(m.getPrivateGroupId().getBytes());
|
||||
return new GroupInvitationRequest(m.getId(), sessionId,
|
||||
m.getContactGroupId(), c, m.getMessage(), m.getPrivateGroupId(),
|
||||
m.getGroupName(), m.getCreator(), true, false, m.getTimestamp(),
|
||||
false, false, true, false);
|
||||
return new GroupInvitationRequest(m.getId(), m.getContactGroupId(),
|
||||
m.getTimestamp(), false, false, true, false, sessionId, pg, c,
|
||||
m.getMessage(), true, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
public 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
|
||||
public class AcceptMessage extends SharingMessage {
|
||||
|
||||
AcceptMessage(MessageId id, @Nullable MessageId previousMessageId,
|
||||
GroupId contactGroupId, GroupId shareableId, long timestamp) {
|
||||
super(id, contactGroupId, shareableId, timestamp, previousMessageId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,7 +52,7 @@ import static org.briarproject.briar.api.sharing.SharingConstants.RESPONSE_ID;
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class BlogSharingManagerImpl extends
|
||||
SharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationRequestReceivedEvent, BlogInvitationResponseReceivedEvent>
|
||||
OldSharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationRequestReceivedEvent, BlogInvitationResponseReceivedEvent>
|
||||
implements BlogSharingManager, RemoveBlogHook {
|
||||
|
||||
private final ContactManager contactManager;
|
||||
|
||||
@@ -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
|
||||
public class DeclineMessage extends SharingMessage {
|
||||
|
||||
DeclineMessage(MessageId id, GroupId contactGroupId,
|
||||
GroupId shareableId, long timestamp,
|
||||
@Nullable MessageId previousMessageId) {
|
||||
super(id, contactGroupId, shareableId, timestamp, previousMessageId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,32 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
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(ForumFactory forumFactory) {
|
||||
super();
|
||||
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,109 @@
|
||||
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.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.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.SessionId;
|
||||
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.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;
|
||||
|
||||
@Inject
|
||||
ForumProtocolEngineImpl(DatabaseComponent db,
|
||||
ClientHelper clientHelper, MessageEncoder messageEncoder,
|
||||
MessageParser<Forum> messageParser, MessageTracker messageTracker,
|
||||
Clock clock, ForumManager forumManager) {
|
||||
super(db, clientHelper, messageEncoder, messageParser, messageTracker,
|
||||
clock);
|
||||
this.forumManager = forumManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
Event getInvitationRequestReceivedEvent(InviteMessage<Forum> m,
|
||||
ContactId contactId, boolean available, boolean canBeOpened) {
|
||||
ForumInvitationRequest request =
|
||||
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 =
|
||||
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 =
|
||||
createInvitationResponse(m.getId(), m.getContactGroupId(),
|
||||
m.getTimestamp(), false, false, true, false,
|
||||
m.getShareableId(), contactId, true);
|
||||
return new ForumInvitationResponseReceivedEvent(contactId, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addShareable(Transaction txn, MessageId inviteId)
|
||||
throws DbException, FormatException {
|
||||
InviteMessage<Forum> invite = getInviteMessage(txn, inviteId);
|
||||
forumManager.addForum(txn, invite.getShareable());
|
||||
}
|
||||
|
||||
private InviteMessage<Forum> 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);
|
||||
}
|
||||
|
||||
@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,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,33 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.client.ContactGroupFactory;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||
import org.briarproject.bramble.api.data.MetadataParser;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.api.client.MessageQueueManager;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.forum.Forum;
|
||||
import org.briarproject.briar.api.forum.ForumFactory;
|
||||
import org.briarproject.briar.api.forum.ForumInvitationRequest;
|
||||
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
||||
import org.briarproject.briar.api.forum.ForumManager;
|
||||
import org.briarproject.briar.api.forum.ForumManager.RemoveForumHook;
|
||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||
import org.briarproject.briar.api.forum.ForumSharingMessage.ForumInvitation;
|
||||
import org.briarproject.briar.api.forum.event.ForumInvitationRequestReceivedEvent;
|
||||
import org.briarproject.briar.api.forum.event.ForumInvitationResponseReceivedEvent;
|
||||
import org.briarproject.briar.api.sharing.InvitationMessage;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_NAME;
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT;
|
||||
import static org.briarproject.briar.api.sharing.SharingConstants.INVITATION_ID;
|
||||
import static org.briarproject.briar.api.sharing.SharingConstants.RESPONSE_ID;
|
||||
|
||||
@NotNullByDefault
|
||||
class ForumSharingManagerImpl extends
|
||||
SharingManagerImpl<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationRequestReceivedEvent, ForumInvitationResponseReceivedEvent>
|
||||
class ForumSharingManagerImpl extends SharingManagerImpl<Forum>
|
||||
implements ForumSharingManager, RemoveForumHook {
|
||||
|
||||
private final SFactory sFactory;
|
||||
private final IFactory iFactory;
|
||||
private final ISFactory isFactory;
|
||||
private final SSFactory ssFactory;
|
||||
private final IRFactory irFactory;
|
||||
private final IRRFactory irrFactory;
|
||||
|
||||
@Inject
|
||||
ForumSharingManagerImpl(ClientHelper clientHelper,
|
||||
Clock clock, DatabaseComponent db,
|
||||
ForumFactory forumFactory,
|
||||
ForumManager forumManager,
|
||||
MessageQueueManager messageQueueManager,
|
||||
MetadataEncoder metadataEncoder,
|
||||
MetadataParser metadataParser,
|
||||
ForumSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||
MetadataParser metadataParser, MessageParser<Forum> messageParser,
|
||||
SessionEncoder sessionEncoder, SessionParser sessionParser,
|
||||
MessageTracker messageTracker,
|
||||
ContactGroupFactory contactGroupFactory,
|
||||
SecureRandom random, MessageTracker messageTracker) {
|
||||
super(db, messageQueueManager, clientHelper, metadataParser,
|
||||
metadataEncoder, random, contactGroupFactory, messageTracker,
|
||||
clock);
|
||||
|
||||
sFactory = new SFactory(forumFactory, forumManager);
|
||||
iFactory = new IFactory();
|
||||
isFactory = new ISFactory();
|
||||
ssFactory = new SSFactory();
|
||||
irFactory = new IRFactory(sFactory);
|
||||
irrFactory = new IRRFactory();
|
||||
ProtocolEngine<Forum> engine) {
|
||||
super(db, clientHelper, metadataParser, messageParser, sessionEncoder,
|
||||
sessionParser, messageTracker, contactGroupFactory, engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,221 +35,9 @@ class ForumSharingManagerImpl extends
|
||||
return CLIENT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationMessage createInvitationRequest(MessageId id,
|
||||
ForumInvitation msg, ContactId contactId, GroupId forumId,
|
||||
boolean available, boolean canBeOpened, long time,
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
return new ForumInvitationRequest(id, msg.getSessionId(),
|
||||
msg.getGroupId(), contactId, forumId, msg.getForumName(),
|
||||
msg.getMessage(), available, canBeOpened, time, local, sent,
|
||||
seen, read);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationMessage createInvitationResponse(MessageId id,
|
||||
SessionId sessionId, GroupId groupId, ContactId contactId,
|
||||
GroupId forumId, boolean accept, long time, boolean local,
|
||||
boolean sent, boolean seen, boolean read) {
|
||||
return new ForumInvitationResponse(id, sessionId, groupId, contactId,
|
||||
forumId, accept, time, local, sent, seen, read);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShareableFactory<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState> getSFactory() {
|
||||
return sFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationFactory<ForumInvitation, ForumSharerSessionState> getIFactory() {
|
||||
return iFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InviteeSessionStateFactory<Forum, ForumInviteeSessionState> getISFactory() {
|
||||
return isFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SharerSessionStateFactory<Forum, ForumSharerSessionState> getSSFactory() {
|
||||
return ssFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationReceivedEventFactory<ForumInviteeSessionState, ForumInvitationRequestReceivedEvent> getIRFactory() {
|
||||
return irFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> getIRRFactory() {
|
||||
return irrFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingForum(Transaction txn, Forum f) throws DbException {
|
||||
removingShareable(txn, f);
|
||||
}
|
||||
|
||||
private static class SFactory implements
|
||||
ShareableFactory<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState> {
|
||||
|
||||
private final ForumFactory forumFactory;
|
||||
private final ForumManager forumManager;
|
||||
|
||||
private SFactory(ForumFactory forumFactory, ForumManager forumManager) {
|
||||
this.forumFactory = forumFactory;
|
||||
this.forumManager = forumManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList encode(Forum f) {
|
||||
return BdfList.of(f.getName(), f.getSalt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum get(Transaction txn, GroupId groupId)
|
||||
throws DbException {
|
||||
return forumManager.getForum(txn, groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum parse(BdfList shareable) throws FormatException {
|
||||
return forumFactory
|
||||
.createForum(shareable.getString(0), shareable.getRaw(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum parse(ForumInvitation msg) {
|
||||
return forumFactory
|
||||
.createForum(msg.getForumName(), msg.getForumSalt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum parse(ForumInviteeSessionState state) {
|
||||
return forumFactory
|
||||
.createForum(state.getForumName(), state.getForumSalt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum parse(ForumSharerSessionState state) {
|
||||
return forumFactory
|
||||
.createForum(state.getForumName(), state.getForumSalt());
|
||||
}
|
||||
}
|
||||
|
||||
private static class IFactory implements
|
||||
InvitationFactory<ForumInvitation, ForumSharerSessionState> {
|
||||
@Override
|
||||
public ForumInvitation build(GroupId groupId, BdfDictionary d)
|
||||
throws FormatException {
|
||||
return ForumInvitation.from(groupId, d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumInvitation build(ForumSharerSessionState localState,
|
||||
long time) {
|
||||
return new ForumInvitation(localState.getContactGroupId(),
|
||||
localState.getSessionId(), localState.getForumName(),
|
||||
localState.getForumSalt(), time, localState.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private static class ISFactory implements
|
||||
InviteeSessionStateFactory<Forum, ForumInviteeSessionState> {
|
||||
@Override
|
||||
public ForumInviteeSessionState build(SessionId sessionId,
|
||||
MessageId storageId, GroupId groupId,
|
||||
InviteeSessionState.State state, ContactId contactId,
|
||||
GroupId forumId, BdfDictionary d) throws FormatException {
|
||||
String forumName = d.getString(FORUM_NAME);
|
||||
byte[] forumSalt = d.getRaw(FORUM_SALT);
|
||||
MessageId invitationId = new MessageId(d.getRaw(INVITATION_ID));
|
||||
return new ForumInviteeSessionState(sessionId, storageId,
|
||||
groupId, state, contactId, forumId, forumName, forumSalt,
|
||||
invitationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumInviteeSessionState build(SessionId sessionId,
|
||||
MessageId storageId, GroupId groupId,
|
||||
InviteeSessionState.State state, ContactId contactId,
|
||||
Forum forum, MessageId invitationId) {
|
||||
return new ForumInviteeSessionState(sessionId, storageId,
|
||||
groupId, state, contactId, forum.getId(), forum.getName(),
|
||||
forum.getSalt(), invitationId);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SSFactory implements
|
||||
SharerSessionStateFactory<Forum, ForumSharerSessionState> {
|
||||
@Override
|
||||
public ForumSharerSessionState build(SessionId sessionId,
|
||||
MessageId storageId, GroupId groupId,
|
||||
SharerSessionState.State state, ContactId contactId,
|
||||
GroupId forumId, BdfDictionary d) throws FormatException {
|
||||
String forumName = d.getString(FORUM_NAME);
|
||||
byte[] forumSalt = d.getRaw(FORUM_SALT);
|
||||
MessageId responseId = null;
|
||||
byte[] responseIdBytes = d.getOptionalRaw(RESPONSE_ID);
|
||||
if (responseIdBytes != null)
|
||||
responseId = new MessageId(responseIdBytes);
|
||||
return new ForumSharerSessionState(sessionId, storageId,
|
||||
groupId, state, contactId, forumId, forumName, forumSalt,
|
||||
responseId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumSharerSessionState build(SessionId sessionId,
|
||||
MessageId storageId, GroupId groupId,
|
||||
SharerSessionState.State state, ContactId contactId,
|
||||
Forum forum) {
|
||||
return new ForumSharerSessionState(sessionId, storageId,
|
||||
groupId, state, contactId, forum.getId(), forum.getName(),
|
||||
forum.getSalt(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class IRFactory implements
|
||||
InvitationReceivedEventFactory<ForumInviteeSessionState, ForumInvitationRequestReceivedEvent> {
|
||||
|
||||
private final SFactory sFactory;
|
||||
|
||||
private IRFactory(SFactory sFactory) {
|
||||
this.sFactory = sFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumInvitationRequestReceivedEvent build(
|
||||
ForumInviteeSessionState localState, long time,
|
||||
@Nullable String msg) {
|
||||
Forum forum = sFactory.parse(localState);
|
||||
ContactId contactId = localState.getContactId();
|
||||
ForumInvitationRequest request = new ForumInvitationRequest(
|
||||
localState.getInvitationId(), localState.getSessionId(),
|
||||
localState.getContactGroupId(), contactId,
|
||||
localState.getShareableId(), forum.getName(), msg, true,
|
||||
false, time, false, false, false, false);
|
||||
return new ForumInvitationRequestReceivedEvent(forum, contactId,
|
||||
request);
|
||||
}
|
||||
}
|
||||
|
||||
private static class IRRFactory implements
|
||||
InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> {
|
||||
@Override
|
||||
public ForumInvitationResponseReceivedEvent build(
|
||||
ForumSharerSessionState localState, boolean accept, long time) {
|
||||
String name = localState.getForumName();
|
||||
ContactId c = localState.getContactId();
|
||||
MessageId responseId = localState.getResponseId();
|
||||
if (responseId == null)
|
||||
throw new IllegalStateException("No responseId");
|
||||
ForumInvitationResponse response = new ForumInvitationResponse(
|
||||
responseId, localState.getSessionId(),
|
||||
localState.getContactGroupId(), localState.getContactId(),
|
||||
localState.getShareableId(), accept, time, false, false,
|
||||
false, false);
|
||||
return new ForumInvitationResponseReceivedEvent(name, c, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.sharing;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.api.sharing.SharingMessage;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
interface InvitationFactory<I extends SharingMessage.Invitation, SS extends SharerSessionState>
|
||||
extends org.briarproject.briar.api.sharing.InvitationFactory<I> {
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent> {
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.sharing;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
interface InvitationResponseReceivedEventFactory<SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> {
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.sharing.Shareable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class InviteMessage<S extends Shareable> extends SharingMessage {
|
||||
|
||||
private final S shareable;
|
||||
@Nullable
|
||||
private final String message;
|
||||
|
||||
InviteMessage(MessageId id, @Nullable MessageId previousMessageId,
|
||||
GroupId contactGroupId, S shareable, @Nullable String message,
|
||||
long timestamp) {
|
||||
super(id, contactGroupId, shareable.getId(), timestamp,
|
||||
previousMessageId);
|
||||
if (message != null && message.equals(""))
|
||||
throw new IllegalArgumentException();
|
||||
this.shareable = shareable;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public S getShareable() {
|
||||
return shareable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import static org.briarproject.briar.api.sharing.SharingConstants.TASK_UNSHARE_S
|
||||
import static org.briarproject.briar.api.sharing.SharingMessage.BaseMessage;
|
||||
import static org.briarproject.briar.api.sharing.SharingMessage.SimpleMessage;
|
||||
|
||||
@Deprecated
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent>
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.briarproject.briar.sharing.InviteeSessionState.Action.LOCAL_LE
|
||||
import static org.briarproject.briar.sharing.InviteeSessionState.Action.REMOTE_INVITATION;
|
||||
import static org.briarproject.briar.sharing.InviteeSessionState.Action.REMOTE_LEAVE;
|
||||
|
||||
@Deprecated
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
public abstract class InviteeSessionState extends SharingSessionState {
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.sharing.Shareable;
|
||||
|
||||
@Deprecated
|
||||
interface InviteeSessionStateFactory<S extends Shareable, IS extends InviteeSessionState> {
|
||||
|
||||
IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class LeaveMessage extends SharingMessage {
|
||||
|
||||
LeaveMessage(MessageId id, GroupId contactGroupId,
|
||||
GroupId shareableId, long timestamp,
|
||||
@Nullable MessageId previousMessageId) {
|
||||
super(id, contactGroupId, shareableId, timestamp, previousMessageId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@ 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_PRIVATE_GROUP_ID;
|
||||
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;
|
||||
@@ -47,7 +47,7 @@ class MessageEncoderImpl implements MessageEncoder {
|
||||
boolean visible, boolean available) {
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(MSG_KEY_MESSAGE_TYPE, type.getValue());
|
||||
meta.put(MSG_KEY_PRIVATE_GROUP_ID, groupId);
|
||||
meta.put(MSG_KEY_SHAREABLE_ID, groupId);
|
||||
meta.put(MSG_KEY_TIMESTAMP, timestamp);
|
||||
meta.put(MSG_KEY_LOCAL, local);
|
||||
meta.put(MSG_KEY_READ, read);
|
||||
@@ -70,6 +70,8 @@ class MessageEncoderImpl implements MessageEncoder {
|
||||
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,
|
||||
|
||||
@@ -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,37 @@
|
||||
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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.briar.api.sharing.Shareable;
|
||||
|
||||
@NotNullByDefault
|
||||
interface MessageParser<S extends Shareable> {
|
||||
|
||||
BdfDictionary getMessagesVisibleInUiQuery();
|
||||
|
||||
BdfDictionary getInvitesAvailableToAnswerQuery();
|
||||
|
||||
BdfDictionary getInvitesAvailableToAnswerQuery(GroupId privateGroupId);
|
||||
|
||||
MessageMetadata parseMetadata(BdfDictionary meta) throws 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,124 @@
|
||||
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.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 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> {
|
||||
|
||||
MessageParserImpl() {
|
||||
}
|
||||
|
||||
@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> 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);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
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.MessageId;
|
||||
import org.briarproject.briar.api.sharing.InvitationRequest;
|
||||
import org.briarproject.briar.api.sharing.InvitationResponse;
|
||||
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;
|
||||
|
||||
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 groupId,
|
||||
long time, boolean local, boolean sent, boolean seen,
|
||||
boolean read, GroupId shareableId, ContactId contactId,
|
||||
boolean accept);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,601 @@
|
||||
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.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.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);
|
||||
// Move to the REMOTE_INVITED state
|
||||
long localTimestamp = Math.max(timestamp, getLocalTimestamp(s));
|
||||
return new Session(REMOTE_INVITED, s.getContactGroupId(),
|
||||
s.getShareableId(), sent.getId(), s.getLastRemoteMessageId(),
|
||||
localTimestamp, 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
|
||||
}
|
||||
Message m = messageEncoder
|
||||
.encodeInviteMessage(s.getContactGroupId(), timestamp,
|
||||
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, true);
|
||||
// Track the message
|
||||
messageTracker.trackOutgoingMessage(txn, sent);
|
||||
try {
|
||||
// Add and subscribe to the shareable
|
||||
addShareable(txn, inviteId);
|
||||
// Share the shareable with the contact
|
||||
setPrivateGroupVisibility(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,
|
||||
boolean visibleInUi) throws DbException {
|
||||
Message m = messageEncoder.encodeAcceptMessage(
|
||||
session.getContactGroupId(), session.getShareableId(),
|
||||
getLocalTimestamp(session), session.getLastLocalMessageId());
|
||||
sendMessage(txn, m, ACCEPT, session.getShareableId(), visibleInUi);
|
||||
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, true);
|
||||
// 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,
|
||||
boolean visibleInUi) throws DbException {
|
||||
Message m = messageEncoder.encodeDeclineMessage(
|
||||
session.getContactGroupId(), session.getShareableId(),
|
||||
getLocalTimestamp(session), session.getLastLocalMessageId());
|
||||
sendMessage(txn, m, DECLINE, session.getShareableId(), visibleInUi);
|
||||
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:
|
||||
throw new ProtocolStateException(); // Invalid in these states
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
private Session onLocalLeave(Transaction txn, Session s, State nextState)
|
||||
throws DbException {
|
||||
try {
|
||||
// Stop sharing the shareable with the contact
|
||||
setPrivateGroupVisibility(txn, s, INVISIBLE);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e); // Invalid group metadata
|
||||
}
|
||||
// Send a LEAVE message
|
||||
Message sent = sendLeaveMessage(txn, s, false);
|
||||
// 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,
|
||||
boolean visibleInUi) throws DbException {
|
||||
Message m = messageEncoder.encodeLeaveMessage(
|
||||
session.getContactGroupId(), session.getShareableId(),
|
||||
getLocalTimestamp(session), session.getLastLocalMessageId());
|
||||
sendMessage(txn, m, LEAVE, session.getShareableId(), visibleInUi);
|
||||
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);
|
||||
// Mark the invite message visible in the UI and 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 available 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
|
||||
setPrivateGroupVisibility(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 onRemoteAccept(txn, s, m);
|
||||
case REMOTE_HANGING:
|
||||
return onRemoteAcceptWhenHanging(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 onRemoteAcceptWhenHanging(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 onRemoteAccept(Transaction txn, Session s, AcceptMessage m)
|
||||
throws DbException, FormatException {
|
||||
// Perform normal remote accept validation and operation
|
||||
Session session = onRemoteAcceptWhenHanging(txn, s, m, SHARING);
|
||||
// Share the shareable with the contact
|
||||
if (session.getState() != ERROR)
|
||||
setPrivateGroupVisibility(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, START);
|
||||
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, 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());
|
||||
}
|
||||
|
||||
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, REMOTE_LEFT);
|
||||
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, State nextState)
|
||||
throws DbException, FormatException {
|
||||
// Carry out normal leave validation and operation
|
||||
Session session = onRemoteLeave(txn, s, m, nextState);
|
||||
// Stop sharing the shareable with the contact
|
||||
if (session.getState() != ERROR)
|
||||
setPrivateGroupVisibility(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()))
|
||||
setPrivateGroupVisibility(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 {
|
||||
return db.containsGroup(txn, g);
|
||||
}
|
||||
|
||||
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 setPrivateGroupVisibility(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 privateGroupId) {
|
||||
this(START, contactGroupId, privateGroupId, null, null, 0, 0);
|
||||
}
|
||||
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
GroupId getContactGroupId() {
|
||||
return contactGroupId;
|
||||
}
|
||||
|
||||
GroupId getShareableId() {
|
||||
return shareableId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
MessageId getLastLocalMessageId() {
|
||||
return lastLocalMessageId;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
MessageId getLastRemoteMessageId() {
|
||||
return lastRemoteMessageId;
|
||||
}
|
||||
|
||||
long getLocalTimestamp() {
|
||||
return localTimestamp;
|
||||
}
|
||||
|
||||
long getInviteTimestamp() {
|
||||
return inviteTimestamp;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
interface SessionEncoder {
|
||||
|
||||
BdfDictionary encodeSession(Session s);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_INVITE_TIMESTAMP;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LOCAL_TIMESTAMP;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_SESSION_ID;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_SHAREABLE_ID;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_STATE;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class SessionEncoderImpl implements SessionEncoder {
|
||||
|
||||
@Inject
|
||||
SessionEncoderImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfDictionary encodeSession(Session s) {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put(SESSION_KEY_SESSION_ID, s.getShareableId());
|
||||
d.put(SESSION_KEY_SHAREABLE_ID, s.getShareableId());
|
||||
MessageId lastLocalMessageId = s.getLastLocalMessageId();
|
||||
if (lastLocalMessageId == null)
|
||||
d.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, NULL_VALUE);
|
||||
else d.put(SESSION_KEY_LAST_LOCAL_MESSAGE_ID, lastLocalMessageId);
|
||||
MessageId lastRemoteMessageId = s.getLastRemoteMessageId();
|
||||
if (lastRemoteMessageId == null)
|
||||
d.put(SESSION_KEY_LAST_REMOTE_MESSAGE_ID, NULL_VALUE);
|
||||
else d.put(SESSION_KEY_LAST_REMOTE_MESSAGE_ID, lastRemoteMessageId);
|
||||
d.put(SESSION_KEY_LOCAL_TIMESTAMP, s.getLocalTimestamp());
|
||||
d.put(SESSION_KEY_INVITE_TIMESTAMP, s.getInviteTimestamp());
|
||||
d.put(SESSION_KEY_STATE, s.getState().getValue());
|
||||
return d;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
|
||||
@NotNullByDefault
|
||||
interface SessionParser {
|
||||
|
||||
BdfDictionary getSessionQuery(SessionId s);
|
||||
|
||||
Session parseSession(GroupId contactGroupId, BdfDictionary d)
|
||||
throws FormatException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfEntry;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_INVITE_TIMESTAMP;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_LOCAL_MESSAGE_ID;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LAST_REMOTE_MESSAGE_ID;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_LOCAL_TIMESTAMP;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_SESSION_ID;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_SHAREABLE_ID;
|
||||
import static org.briarproject.briar.sharing.SharingConstants.SESSION_KEY_STATE;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class SessionParserImpl implements SessionParser {
|
||||
|
||||
@Inject
|
||||
SessionParserImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfDictionary getSessionQuery(SessionId s) {
|
||||
return BdfDictionary.of(new BdfEntry(SESSION_KEY_SESSION_ID, s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Session parseSession(GroupId contactGroupId,
|
||||
BdfDictionary d) throws FormatException {
|
||||
return new Session(State.fromValue(getState(d)), contactGroupId,
|
||||
getShareableId(d), getLastLocalMessageId(d),
|
||||
getLastRemoteMessageId(d), getLocalTimestamp(d),
|
||||
getInviteTimestamp(d));
|
||||
}
|
||||
|
||||
private int getState(BdfDictionary d) throws FormatException {
|
||||
return d.getLong(SESSION_KEY_STATE).intValue();
|
||||
}
|
||||
|
||||
private GroupId getShareableId(BdfDictionary d) throws FormatException {
|
||||
return new GroupId(d.getRaw(SESSION_KEY_SHAREABLE_ID));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MessageId getLastLocalMessageId(BdfDictionary d)
|
||||
throws FormatException {
|
||||
byte[] b = d.getOptionalRaw(SESSION_KEY_LAST_LOCAL_MESSAGE_ID);
|
||||
return b == null ? null : new MessageId(b);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MessageId getLastRemoteMessageId(BdfDictionary d)
|
||||
throws FormatException {
|
||||
byte[] b = d.getOptionalRaw(SESSION_KEY_LAST_REMOTE_MESSAGE_ID);
|
||||
return b == null ? null : new MessageId(b);
|
||||
}
|
||||
|
||||
private long getLocalTimestamp(BdfDictionary d) throws FormatException {
|
||||
return d.getLong(SESSION_KEY_LOCAL_TIMESTAMP);
|
||||
}
|
||||
|
||||
private long getInviteTimestamp(BdfDictionary d) throws FormatException {
|
||||
return d.getLong(SESSION_KEY_INVITE_TIMESTAMP);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.briar.api.sharing.Shareable;
|
||||
import org.briarproject.briar.api.sharing.SharingMessage;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
interface ShareableFactory<S extends Shareable, I extends SharingMessage.Invitation, IS extends InviteeSessionState, SS extends SharerSessionState> {
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import static org.briarproject.briar.api.sharing.SharingMessage.SimpleMessage;
|
||||
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_ACCEPT;
|
||||
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_DECLINE;
|
||||
|
||||
@Deprecated
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent>
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_AC
|
||||
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_DECLINE;
|
||||
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_LEAVE;
|
||||
|
||||
@Deprecated
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
public abstract class SharerSessionState extends SharingSessionState {
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.sharing.Shareable;
|
||||
|
||||
@Deprecated
|
||||
@NotNullByDefault
|
||||
interface SharerSessionStateFactory<S extends Shareable, SS extends SharerSessionState> {
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ interface SharingConstants {
|
||||
|
||||
// Message metadata keys
|
||||
String MSG_KEY_MESSAGE_TYPE = "messageType";
|
||||
String MSG_KEY_PRIVATE_GROUP_ID = "privateGroupId";
|
||||
String MSG_KEY_SHAREABLE_ID = "shareableId";
|
||||
String MSG_KEY_TIMESTAMP = "timestamp";
|
||||
String MSG_KEY_READ = MessageTrackerConstants.MSG_KEY_READ;
|
||||
String MSG_KEY_LOCAL = "local";
|
||||
@@ -17,13 +17,12 @@ interface SharingConstants {
|
||||
String MSG_KEY_AVAILABLE_TO_ANSWER = "availableToAnswer";
|
||||
|
||||
// Session keys
|
||||
String SESSION_KEY_STATE = "state";
|
||||
String SESSION_KEY_SESSION_ID = "sessionId";
|
||||
String SESSION_KEY_GROUP_ID = "groupId";
|
||||
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";
|
||||
String SESSION_KEY_ROLE = "role";
|
||||
String SESSION_KEY_STATE = "state";
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.api.blog.BlogManager;
|
||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||
import org.briarproject.briar.api.client.MessageQueueManager;
|
||||
import org.briarproject.briar.api.forum.Forum;
|
||||
import org.briarproject.briar.api.forum.ForumFactory;
|
||||
import org.briarproject.briar.api.forum.ForumManager;
|
||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||
@@ -86,14 +87,14 @@ public class SharingModule {
|
||||
@Singleton
|
||||
ForumSharingManager provideForumSharingManager(
|
||||
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||
MessageQueueManager messageQueueManager,
|
||||
ValidationManager validationManager,
|
||||
ConversationManager conversationManager, ForumManager forumManager,
|
||||
ForumSharingManagerImpl forumSharingManager) {
|
||||
|
||||
lifecycleManager.registerClient(forumSharingManager);
|
||||
contactManager.registerAddContactHook(forumSharingManager);
|
||||
contactManager.registerRemoveContactHook(forumSharingManager);
|
||||
messageQueueManager.registerIncomingMessageHook(
|
||||
validationManager.registerIncomingMessageHook(
|
||||
ForumSharingManager.CLIENT_ID, forumSharingManager);
|
||||
conversationManager.registerConversationClient(forumSharingManager);
|
||||
forumManager.registerRemoveForumHook(forumSharingManager);
|
||||
@@ -106,4 +107,26 @@ public class SharingModule {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import static org.briarproject.briar.api.sharing.SharingConstants.SHAREABLE_ID;
|
||||
import static org.briarproject.briar.api.sharing.SharingConstants.STATE;
|
||||
import static org.briarproject.briar.api.sharing.SharingConstants.STORAGE_ID;
|
||||
|
||||
@Deprecated
|
||||
@NotThreadSafe
|
||||
@NotNullByDefault
|
||||
abstract class SharingSessionState {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package org.briarproject.briar.sharing;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
enum State {
|
||||
|
||||
START(0), LOCAL_INVITED(1), REMOTE_INVITED(2), SHARING(3), LOCAL_LEFT(4),
|
||||
REMOTE_LEFT(5), REMOTE_HANGING(6), ERROR(7);
|
||||
|
||||
private final int value;
|
||||
|
||||
State(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public boolean canInvite() {
|
||||
return this == START || this == REMOTE_LEFT;
|
||||
}
|
||||
|
||||
static State fromValue(int value) throws FormatException {
|
||||
for (State s : values()) if (s.value == value) return s;
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,7 +45,8 @@ public class ForumManagerTest
|
||||
forum0 = forumManager0.addForum("Test Forum");
|
||||
groupId0 = forum0.getId();
|
||||
// share forum
|
||||
forumSharingManager0.sendInvitation(groupId0, contactId1From0, null);
|
||||
forumSharingManager0.sendInvitation(groupId0, contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
sync0To1(1, true);
|
||||
forumSharingManager1.respondToInvitation(forum0, contact0From1, true);
|
||||
sync1To0(1, true);
|
||||
@@ -189,7 +190,8 @@ public class ForumManagerTest
|
||||
// share a second forum
|
||||
Forum forum1 = forumManager0.addForum("Test Forum1");
|
||||
GroupId g1 = forum1.getId();
|
||||
forumSharingManager0.sendInvitation(g1, contactId1From0, null);
|
||||
forumSharingManager0.sendInvitation(g1, contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
sync0To1(1, true);
|
||||
forumSharingManager1.respondToInvitation(forum1, contact0From1, true);
|
||||
sync1To0(1, true);
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager
|
||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
|
||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse;
|
||||
import org.briarproject.briar.api.sharing.InvitationMessage;
|
||||
import org.briarproject.briar.api.sharing.InvitationResponse;
|
||||
import org.briarproject.briar.test.BriarIntegrationTest;
|
||||
import org.briarproject.briar.test.BriarIntegrationTestComponent;
|
||||
import org.briarproject.briar.test.DaggerBriarIntegrationTestComponent;
|
||||
@@ -97,10 +98,10 @@ public class GroupInvitationIntegrationTest
|
||||
GroupInvitationRequest request =
|
||||
(GroupInvitationRequest) messages.iterator().next();
|
||||
assertEquals(msg, request.getMessage());
|
||||
assertEquals(author0, request.getCreator());
|
||||
assertEquals(author0, request.getShareable().getCreator());
|
||||
assertEquals(timestamp, request.getTimestamp());
|
||||
assertEquals(contactId0From1, request.getContactId());
|
||||
assertEquals(privateGroup0.getName(), request.getGroupName());
|
||||
assertEquals(privateGroup0.getName(), request.getShareable().getName());
|
||||
assertFalse(request.isLocal());
|
||||
assertFalse(request.isRead());
|
||||
}
|
||||
@@ -123,7 +124,7 @@ public class GroupInvitationIntegrationTest
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
foundResponse = true;
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
InvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertEquals(contactId0From1, response.getContactId());
|
||||
assertTrue(response.isLocal());
|
||||
assertFalse(response.wasAccepted());
|
||||
@@ -140,7 +141,7 @@ public class GroupInvitationIntegrationTest
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
foundResponse = true;
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
InvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertEquals(contactId0From1, response.getContactId());
|
||||
assertFalse(response.isLocal());
|
||||
assertFalse(response.wasAccepted());
|
||||
@@ -172,7 +173,7 @@ public class GroupInvitationIntegrationTest
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
foundResponse = true;
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
InvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertTrue(response.wasAccepted());
|
||||
}
|
||||
}
|
||||
@@ -187,7 +188,7 @@ public class GroupInvitationIntegrationTest
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
foundResponse = true;
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
InvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertTrue(response.wasAccepted());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +90,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final GroupInvitationManagerImpl groupInvitationManager;
|
||||
|
||||
private final Group localGroup =
|
||||
new Group(new GroupId(getRandomId()), CLIENT_ID, getRandomBytes(5));
|
||||
private final Transaction txn = new Transaction(null, false);
|
||||
private final ContactId contactId = new ContactId(0);
|
||||
private final Author author =
|
||||
@@ -141,8 +139,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(inviteeEngine));
|
||||
oneOf(engineFactory).createPeerEngine();
|
||||
will(returnValue(peerEngine));
|
||||
oneOf(contactGroupFactory).createLocalGroup(CLIENT_ID);
|
||||
will(returnValue(localGroup));
|
||||
}});
|
||||
MetadataParser metadataParser = context.mock(MetadataParser.class);
|
||||
MessageTracker messageTracker = context.mock(MessageTracker.class);
|
||||
@@ -156,7 +152,6 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testCreateLocalState() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).addGroup(txn, localGroup);
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(Collections.singletonList(contact)));
|
||||
}});
|
||||
@@ -651,6 +646,9 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
new InviteMessage(message.getId(), contactGroup.getId(),
|
||||
privateGroup.getId(), time1, "name", author,
|
||||
new byte[0], null, new byte[0]);
|
||||
final PrivateGroup pg =
|
||||
new PrivateGroup(privateGroup, invite.getGroupName(),
|
||||
invite.getCreator(), invite.getSalt());
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).startTransaction(true);
|
||||
@@ -674,6 +672,9 @@ public class GroupInvitationManagerImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(body));
|
||||
oneOf(messageParser).parseInviteMessage(message, body);
|
||||
will(returnValue(invite));
|
||||
oneOf(privateGroupFactory).createPrivateGroup(invite.getGroupName(),
|
||||
invite.getCreator(), invite.getSalt());
|
||||
will(returnValue(pg));
|
||||
oneOf(db).containsGroup(txn, privateGroup.getId());
|
||||
will(returnValue(true));
|
||||
// second message
|
||||
|
||||
@@ -107,7 +107,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// create invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog1.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog1.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync invitation
|
||||
sync0To1(1, false);
|
||||
@@ -122,7 +123,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// invitee has own blog and that of the sharer
|
||||
assertEquals(2, blogManager1.getBlogs().size());
|
||||
@@ -194,7 +196,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, null);
|
||||
.sendInvitation(blog2.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -251,7 +254,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -313,7 +317,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// sharer sends invitation for 2's blog to 1
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -349,7 +354,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -399,7 +405,8 @@ public class BlogSharingIntegrationTest
|
||||
|
||||
// sharer sends invitation for 2's blog to 1
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(blog2.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
|
||||
@@ -2,19 +2,14 @@ package org.briarproject.briar.sharing;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.bramble.api.Bytes;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.data.BdfList;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Metadata;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.test.TestDatabaseModule;
|
||||
import org.briarproject.briar.api.client.MessageQueueManager;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
import org.briarproject.briar.api.client.ProtocolStateException;
|
||||
import org.briarproject.briar.api.forum.Forum;
|
||||
import org.briarproject.briar.api.forum.ForumInvitationRequest;
|
||||
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
||||
@@ -38,12 +33,10 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomString;
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH;
|
||||
import static org.briarproject.briar.api.forum.ForumSharingManager.CLIENT_ID;
|
||||
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -56,6 +49,9 @@ public class ForumSharingIntegrationTest
|
||||
private InviteeListener listener1;
|
||||
private Forum forum0;
|
||||
|
||||
@Inject
|
||||
MessageEncoder messageEncoder;
|
||||
|
||||
// objects accessed from background threads need to be volatile
|
||||
private volatile ForumSharingManager forumSharingManager0;
|
||||
private volatile ForumSharingManager forumSharingManager1;
|
||||
@@ -115,7 +111,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -171,7 +168,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -227,7 +225,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -245,9 +244,8 @@ public class ForumSharingIntegrationTest
|
||||
assertTrue(forumManager1.getForums().contains(forum0));
|
||||
|
||||
// sharer shares forum with invitee
|
||||
Contact c1 = contactManager0.getContact(contactId1From0);
|
||||
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
|
||||
.contains(c1));
|
||||
.contains(contact1From0));
|
||||
// invitee gets forum shared by sharer
|
||||
Contact contact0 = contactManager1.getContact(contactId1From0);
|
||||
assertTrue(forumSharingManager1.getSharedWith(forum0.getId())
|
||||
@@ -265,14 +263,16 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// sharer no longer shares forum with invitee
|
||||
assertFalse(forumSharingManager0.getSharedWith(forum0.getId())
|
||||
.contains(c1));
|
||||
.contains(contact1From0));
|
||||
// invitee no longer gets forum shared by sharer
|
||||
assertFalse(forumSharingManager1.getSharedWith(forum0.getId())
|
||||
.contains(contact0));
|
||||
// forum can be shared again
|
||||
assertTrue(forumSharingManager0.canBeShared(forum0.getId(), c1));
|
||||
Contact c0 = contactManager1.getContact(contactId0From1);
|
||||
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0));
|
||||
// forum can be shared again by sharer
|
||||
assertTrue(forumSharingManager0
|
||||
.canBeShared(forum0.getId(), contact1From0));
|
||||
// invitee that left can not share again
|
||||
assertFalse(forumSharingManager1
|
||||
.canBeShared(forum0.getId(), contact0From1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -282,7 +282,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -336,7 +337,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sharer un-subscribes from forum
|
||||
forumManager0.removeForum(forum0);
|
||||
@@ -360,7 +362,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From0, null,
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sharer un-subscribes from forum
|
||||
forumManager0.removeForum(forum0);
|
||||
@@ -375,73 +378,15 @@ public class ForumSharingIntegrationTest
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSessionIdReuse() throws Exception {
|
||||
// initialize and let invitee accept all requests
|
||||
listenToEvents(true);
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// sync response back
|
||||
sync1To0(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
|
||||
// reset event received state
|
||||
listener1.requestReceived = false;
|
||||
|
||||
// get SessionId from invitation
|
||||
List<InvitationMessage> list = new ArrayList<InvitationMessage>(
|
||||
forumSharingManager1
|
||||
.getInvitationMessages(contactId0From1));
|
||||
assertEquals(2, list.size());
|
||||
InvitationMessage msg = list.get(0);
|
||||
SessionId sessionId = msg.getSessionId();
|
||||
assertEquals(sessionId, list.get(1).getSessionId());
|
||||
|
||||
// get all sorts of stuff needed to send a message
|
||||
MessageQueueManager queue = c0.getMessageQueueManager();
|
||||
Contact c1 = contactManager0.getContact(contactId1From0);
|
||||
Group group = contactGroupFactory.createContactGroup(CLIENT_ID, c1);
|
||||
long time = clock.currentTimeMillis();
|
||||
BdfList bodyList =
|
||||
BdfList.of(SHARE_MSG_TYPE_INVITATION, sessionId.getBytes(),
|
||||
getRandomString(42), getRandomBytes(FORUM_SALT_LENGTH));
|
||||
byte[] body = clientHelper.toByteArray(bodyList);
|
||||
|
||||
// add the message to the queue
|
||||
Transaction txn = db0.startTransaction(false);
|
||||
try {
|
||||
queue.sendMessage(txn, group, time, body, new Metadata());
|
||||
db0.commitTransaction(txn);
|
||||
} finally {
|
||||
db0.endTransaction(txn);
|
||||
}
|
||||
|
||||
// actually send the message
|
||||
sync0To1(1, false);
|
||||
// make sure there was no new request received
|
||||
assertFalse(listener1.requestReceived);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test(expected = ProtocolStateException.class)
|
||||
public void testSharingSameForumWithEachOther() throws Exception {
|
||||
// initialize and let invitee accept all requests
|
||||
listenToEvents(true);
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -455,24 +400,12 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
assertEquals(2,
|
||||
forumSharingManager0.getInvitationMessages(contactId1From0)
|
||||
.size());
|
||||
|
||||
// invitee now shares same forum back
|
||||
forumSharingManager1.sendInvitation(forum0.getId(),
|
||||
contactId0From1,
|
||||
"I am re-sharing this forum with you.");
|
||||
|
||||
// sync re-share invitation
|
||||
sync1To0(1, false);
|
||||
|
||||
// make sure that no new request was received
|
||||
assertFalse(listener0.requestReceived);
|
||||
assertEquals(2,
|
||||
forumSharingManager0.getInvitationMessages(contactId1From0)
|
||||
.size());
|
||||
assertEquals(0, forumSharingManager0.getInvitations().size());
|
||||
"I am re-sharing this forum with you.",
|
||||
clock.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -482,62 +415,48 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// invitee adds the same forum
|
||||
Transaction txn = db1.startTransaction(false);
|
||||
db1.addGroup(txn, forum0.getGroup());
|
||||
forumManager1.addForum(txn, forum0);
|
||||
db1.commitTransaction(txn);
|
||||
db1.endTransaction(txn);
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// invitee now shares same forum back
|
||||
forumSharingManager1.sendInvitation(forum0.getId(),
|
||||
contactId0From1, "I am re-sharing this forum with you.");
|
||||
contactId0From1, "I am re-sharing this forum with you.",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// find out who should be Alice, because of random keys
|
||||
Bytes key0 = new Bytes(author0.getPublicKey());
|
||||
Bytes key1 = new Bytes(author1.getPublicKey());
|
||||
// prevent automatic responses
|
||||
respond = false;
|
||||
|
||||
// only now sync first request message
|
||||
boolean alice = key1.compareTo(key0) < 0;
|
||||
if (alice) {
|
||||
sync0To1(1, false);
|
||||
assertFalse(listener1.requestReceived);
|
||||
} else {
|
||||
sync0To1(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
}
|
||||
sync0To1(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// sync second invitation
|
||||
alice = key0.compareTo(key1) < 0;
|
||||
if (alice) {
|
||||
sync1To0(1, false);
|
||||
assertFalse(listener0.requestReceived);
|
||||
// sync second invitation which counts as accept
|
||||
sync1To0(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.requestReceived);
|
||||
|
||||
// sharer did not receive request, but response to own request
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
// both peers should share the forum with each other now
|
||||
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
|
||||
.contains(contact1From0));
|
||||
assertTrue(forumSharingManager1.getSharedWith(forum0.getId())
|
||||
.contains(contact0From1));
|
||||
|
||||
assertEquals(2, forumSharingManager0
|
||||
.getInvitationMessages(contactId1From0).size());
|
||||
assertEquals(3, forumSharingManager1
|
||||
.getInvitationMessages(contactId0From1).size());
|
||||
} else {
|
||||
sync1To0(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.requestReceived);
|
||||
// and both have each other's invitations (and no response)
|
||||
assertEquals(2, forumSharingManager0
|
||||
.getInvitationMessages(contactId1From0).size());
|
||||
assertEquals(2, forumSharingManager1
|
||||
.getInvitationMessages(contactId0From1).size());
|
||||
|
||||
// send response from sharer to invitee and make sure it arrived
|
||||
sync0To1(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.responseReceived);
|
||||
|
||||
assertEquals(3, forumSharingManager0
|
||||
.getInvitationMessages(contactId1From0).size());
|
||||
assertEquals(2, forumSharingManager1
|
||||
.getInvitationMessages(contactId0From1).size());
|
||||
}
|
||||
// there are no more open invitations
|
||||
assertTrue(forumSharingManager0.getInvitations().isEmpty());
|
||||
assertTrue(forumSharingManager1.getInvitations().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -547,7 +466,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -564,18 +484,12 @@ public class ForumSharingIntegrationTest
|
||||
assertEquals(1,
|
||||
forumSharingManager0.getSharedWith(forum0.getId()).size());
|
||||
|
||||
// remember SessionId from invitation
|
||||
List<InvitationMessage> list = new ArrayList<InvitationMessage>(
|
||||
forumSharingManager1
|
||||
.getInvitationMessages(contactId0From1));
|
||||
assertEquals(2, list.size());
|
||||
InvitationMessage msg = list.get(0);
|
||||
SessionId sessionId = msg.getSessionId();
|
||||
assertEquals(sessionId, list.get(1).getSessionId());
|
||||
|
||||
// contacts now remove each other
|
||||
removeAllContacts();
|
||||
|
||||
// invitee still has forum
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
|
||||
// make sure sharer does share the forum with nobody now
|
||||
assertEquals(0,
|
||||
forumSharingManager0.getSharedWith(forum0.getId()).size());
|
||||
@@ -584,35 +498,30 @@ public class ForumSharingIntegrationTest
|
||||
addDefaultContacts();
|
||||
addContacts1And2();
|
||||
|
||||
// get all sorts of stuff needed to send a message
|
||||
MessageQueueManager queue = c0.getMessageQueueManager();
|
||||
Contact c1 = contactManager0.getContact(contactId1From0);
|
||||
Group group = contactGroupFactory.createContactGroup(CLIENT_ID, c1);
|
||||
long time = clock.currentTimeMillis();
|
||||
// forum can be shared with contacts again
|
||||
assertTrue(forumSharingManager0
|
||||
.canBeShared(forum0.getId(), contact1From0));
|
||||
assertTrue(forumSharingManager0
|
||||
.canBeShared(forum0.getId(), contact2From0));
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// construct a new message re-using the old SessionId
|
||||
BdfList bodyList = BdfList.of(SHARE_MSG_TYPE_INVITATION,
|
||||
sessionId.getBytes(),
|
||||
getRandomString(42),
|
||||
getRandomBytes(FORUM_SALT_LENGTH)
|
||||
);
|
||||
byte[] body = clientHelper.toByteArray(bodyList);
|
||||
|
||||
// add the message to the queue
|
||||
Transaction txn = db0.startTransaction(false);
|
||||
try {
|
||||
queue.sendMessage(txn, group, time, body, new Metadata());
|
||||
db0.commitTransaction(txn);
|
||||
} finally {
|
||||
db0.endTransaction(txn);
|
||||
}
|
||||
|
||||
// actually send the message
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
// make sure the new request was received with the same sessionId
|
||||
// as proof that the state got deleted along with contacts
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// sync response back
|
||||
sync1To0(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum is still there
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
assertEquals(1,
|
||||
forumSharingManager0.getSharedWith(forum0.getId()).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -633,14 +542,16 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
|
||||
// second sharer sends invitation for same forum
|
||||
assertTrue(contactId1From2 != null);
|
||||
forumSharingManager2
|
||||
.sendInvitation(forum0.getId(), contactId1From2, null);
|
||||
.sendInvitation(forum0.getId(), contactId1From2, null,
|
||||
clock.currentTimeMillis());
|
||||
// sync second request message
|
||||
sync2To1(1, true);
|
||||
|
||||
@@ -650,13 +561,6 @@ public class ForumSharingIntegrationTest
|
||||
assertEquals(1, forums.size());
|
||||
assertEquals(2, forums.iterator().next().getNewSharers().size());
|
||||
assertEquals(forum0, forums.iterator().next().getShareable());
|
||||
assertEquals(2,
|
||||
forumSharingManager1.getSharedWith(forum0.getId()).size());
|
||||
|
||||
// make sure both sharers actually share the forum
|
||||
Collection<Contact> contacts =
|
||||
forumSharingManager1.getSharedWith(forum0.getId());
|
||||
assertEquals(2, contacts.size());
|
||||
|
||||
// answer second request
|
||||
assertNotNull(contactId2From1);
|
||||
@@ -675,6 +579,11 @@ public class ForumSharingIntegrationTest
|
||||
sync1To0(1, true);
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// make sure both sharers actually share the forum
|
||||
Collection<Contact> contacts =
|
||||
forumSharingManager1.getSharedWith(forum0.getId());
|
||||
assertEquals(2, contacts.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -684,7 +593,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -745,7 +655,8 @@ public class ForumSharingIntegrationTest
|
||||
|
||||
// send invitation again
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1From0, "Hi!",
|
||||
clock.currentTimeMillis());
|
||||
|
||||
// sync first request message
|
||||
sync0To1(1, true);
|
||||
@@ -799,8 +710,10 @@ public class ForumSharingIntegrationTest
|
||||
requestReceived = true;
|
||||
Forum f = event.getShareable();
|
||||
try {
|
||||
Contact c = contactManager0.getContact(contactId1From0);
|
||||
forumSharingManager0.respondToInvitation(f, c, true);
|
||||
if (respond) {
|
||||
Contact c = contactManager0.getContact(contactId1From0);
|
||||
forumSharingManager0.respondToInvitation(f, c, true);
|
||||
}
|
||||
} catch (DbException ex) {
|
||||
eventWaiter.rethrow(ex);
|
||||
} finally {
|
||||
@@ -814,7 +727,6 @@ public class ForumSharingIntegrationTest
|
||||
private class InviteeListener implements EventListener {
|
||||
|
||||
private volatile boolean requestReceived = false;
|
||||
private volatile boolean responseReceived = false;
|
||||
|
||||
private final boolean accept, answer;
|
||||
|
||||
@@ -836,13 +748,13 @@ public class ForumSharingIntegrationTest
|
||||
if (!answer) return;
|
||||
Forum f = event.getShareable();
|
||||
try {
|
||||
eventWaiter.assertEquals(1,
|
||||
forumSharingManager1.getInvitations().size());
|
||||
SharingInvitationItem invitation =
|
||||
forumSharingManager1.getInvitations().iterator()
|
||||
.next();
|
||||
eventWaiter.assertEquals(f, invitation.getShareable());
|
||||
if (respond) {
|
||||
eventWaiter.assertEquals(1,
|
||||
forumSharingManager1.getInvitations().size());
|
||||
SharingInvitationItem invitation =
|
||||
forumSharingManager1.getInvitations().iterator()
|
||||
.next();
|
||||
eventWaiter.assertEquals(f, invitation.getShareable());
|
||||
Contact c =
|
||||
contactManager1
|
||||
.getContact(event.getContactId());
|
||||
@@ -859,7 +771,6 @@ public class ForumSharingIntegrationTest
|
||||
ForumInvitationResponseReceivedEvent event =
|
||||
(ForumInvitationResponseReceivedEvent) e;
|
||||
eventWaiter.assertEquals(contactId0From1, event.getContactId());
|
||||
responseReceived = true;
|
||||
eventWaiter.resume();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user