New Forum Sharing Client

This commit is contained in:
Torsten Grote
2016-12-16 11:30:09 -02:00
parent 409e0fb5a5
commit 694e662028
64 changed files with 3168 additions and 1655 deletions

View File

@@ -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

View File

@@ -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());
}
}

View File

@@ -10,9 +10,11 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.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);

View File

@@ -10,9 +10,11 @@ import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.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);

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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.
*/

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -7,25 +7,22 @@ import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.BaseMessageHeader;
import org.briarproject.briar.api.client.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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
/**

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

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

View File

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

View File

@@ -52,7 +52,7 @@ import static org.briarproject.briar.api.sharing.SharingConstants.RESPONSE_ID;
@Immutable
@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;

View File

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

View File

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

View File

@@ -0,0 +1,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);
}
}

View File

@@ -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);
}
}

View File

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

View File

@@ -1,78 +1,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);
}
}
}

View File

@@ -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> {

View File

@@ -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> {

View File

@@ -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> {

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.sharing.Shareable;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,

View File

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

View File

@@ -0,0 +1,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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,69 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.sharing.State.START;
@Immutable
@NotNullByDefault
class Session {
private final State state;
private final GroupId contactGroupId, shareableId;
@Nullable
private final MessageId lastLocalMessageId, lastRemoteMessageId;
private final long localTimestamp, inviteTimestamp;
Session(State state, GroupId contactGroupId, GroupId shareableId,
@Nullable MessageId lastLocalMessageId,
@Nullable MessageId lastRemoteMessageId, long localTimestamp,
long inviteTimestamp) {
this.state = state;
this.contactGroupId = contactGroupId;
this.shareableId = shareableId;
this.lastLocalMessageId = lastLocalMessageId;
this.lastRemoteMessageId = lastRemoteMessageId;
this.localTimestamp = localTimestamp;
this.inviteTimestamp = inviteTimestamp;
}
Session(GroupId contactGroupId, GroupId 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,6 +29,7 @@ import static org.briarproject.briar.api.sharing.SharingMessage.SimpleMessage;
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_ACCEPT;
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_DECLINE;
@Deprecated
@Immutable
@NotNullByDefault
class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent>

View File

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

View File

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

View File

@@ -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";
}

View File

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

View File

@@ -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;
}
}

View File

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

View File

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

View File

@@ -45,7 +45,8 @@ public class ForumManagerTest
forum0 = forumManager0.addForum("Test Forum");
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);

View File

@@ -12,6 +12,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
import org.briarproject.briar.api.privategroup.invitation.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());
}
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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();
}
}