Migrate blog sharing to new sharing client infrastructure

This commit is contained in:
Torsten Grote
2017-01-04 15:48:34 -02:00
parent d5443e9651
commit c13eafef14
36 changed files with 695 additions and 3129 deletions

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.api.blog; package org.briarproject.briar.api.blog;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
public interface BlogConstants { public interface BlogConstants {
@@ -7,12 +8,7 @@ public interface BlogConstants {
/** /**
* The maximum length of a blogs's name in UTF-8 bytes. * The maximum length of a blogs's name in UTF-8 bytes.
*/ */
int MAX_BLOG_TITLE_LENGTH = 100; int MAX_BLOG_NAME_LENGTH = MAX_AUTHOR_NAME_LENGTH;
/**
* The length of a blogs's description in UTF-8 bytes.
*/
int MAX_BLOG_DESC_LENGTH = 240;
/** /**
* The maximum length of a blog post's body in bytes. * The maximum length of a blog post's body in bytes.
@@ -24,13 +20,8 @@ public interface BlogConstants {
*/ */
int MAX_BLOG_COMMENT_LENGTH = MAX_BLOG_POST_BODY_LENGTH; int MAX_BLOG_COMMENT_LENGTH = MAX_BLOG_POST_BODY_LENGTH;
/* Blog Sharing Constants */
String BLOG_AUTHOR_NAME = "blogAuthorName";
String BLOG_PUBLIC_KEY = "blogPublicKey";
// Metadata keys // Metadata keys
String KEY_TYPE = "type"; String KEY_TYPE = "type";
String KEY_DESCRIPTION = "description";
String KEY_TIMESTAMP = "timestamp"; String KEY_TIMESTAMP = "timestamp";
String KEY_TIME_RECEIVED = "timeReceived"; String KEY_TIME_RECEIVED = "timeReceived";
String KEY_AUTHOR_ID = "id"; String KEY_AUTHOR_ID = "id";

View File

@@ -1,10 +1,7 @@
package org.briarproject.briar.api.blog; package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
@@ -15,24 +12,16 @@ import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public class BlogInvitationRequest extends InvitationRequest<Blog> { public class BlogInvitationRequest extends InvitationRequest<Blog> {
private final String blogAuthorName; public BlogInvitationRequest(MessageId id, GroupId groupId, long time,
boolean local, boolean sent, boolean seen, boolean read,
public BlogInvitationRequest(MessageId id, SessionId sessionId, SessionId sessionId, Blog blog, ContactId contactId,
GroupId groupId, ContactId contactId, String blogAuthorName, @Nullable String message, boolean available, boolean canBeOpened) {
@Nullable String message, GroupId blogId, super(id, groupId, time, local, sent, seen, read, sessionId, blog,
boolean available, boolean canBeOpened, long time, contactId, message, available, canBeOpened);
boolean local, boolean sent, boolean seen, boolean 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;
} }
public String getBlogAuthorName() { public String getBlogAuthorName() {
return blogAuthorName; return getShareable().getName();
} }
} }

View File

@@ -10,10 +10,10 @@ import org.briarproject.briar.api.sharing.InvitationResponse;
@NotNullByDefault @NotNullByDefault
public class BlogInvitationResponse extends InvitationResponse { public class BlogInvitationResponse extends InvitationResponse {
public BlogInvitationResponse(MessageId id, SessionId sessionId, public BlogInvitationResponse(MessageId id, GroupId groupId, long time,
GroupId groupId, ContactId contactId, GroupId blogId, boolean local, boolean sent, boolean seen, boolean read,
boolean accept, long time, boolean local, boolean sent, SessionId sessionId, GroupId blogId, ContactId contactId,
boolean seen, boolean read) { boolean accept) {
super(id, groupId, time, local, sent, seen, read, sessionId, blogId, super(id, groupId, time, local, sent, seen, read, sessionId, blogId,
contactId, accept); contactId, accept);
} }

View File

@@ -21,6 +21,16 @@ public interface BlogManager {
*/ */
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog");
/**
* Adds a blog from the given author.
*/
Blog addBlog(Author author) throws DbException;
/**
* Adds the given {@link Blog} within the given {@link Transaction}.
*/
void addBlog(Transaction txn, Blog b) throws DbException;
/** /**
* Returns true if a blog can be removed. * Returns true if a blog can be removed.
*/ */

View File

@@ -1,74 +0,0 @@
package org.briarproject.briar.api.blog;
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.briar.api.client.SessionId;
import org.briarproject.briar.api.sharing.SharingMessage.Invitation;
import javax.annotation.Nullable;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_AUTHOR_NAME;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_PUBLIC_KEY;
import static org.briarproject.briar.api.sharing.SharingConstants.INVITATION_MSG;
import static org.briarproject.briar.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.TIME;
@NotNullByDefault
public interface BlogSharingMessage {
class BlogInvitation extends Invitation {
private final String blogAuthorName;
private final byte[] blogPublicKey;
public BlogInvitation(GroupId groupId, SessionId sessionId,
String blogAuthorName, byte[] blogPublicKey, long time,
@Nullable String message) {
super(groupId, sessionId, time, message);
this.blogAuthorName = blogAuthorName;
this.blogPublicKey = blogPublicKey;
}
@Override
public BdfList toBdfList() {
BdfList list = super.toBdfList();
list.add(BdfList.of(blogAuthorName, blogPublicKey));
if (message != null) list.add(message);
return list;
}
@Override
public BdfDictionary toBdfDictionary() {
BdfDictionary d = toBdfDictionaryHelper();
d.put(BLOG_AUTHOR_NAME, blogAuthorName);
d.put(BLOG_PUBLIC_KEY, blogPublicKey);
if (message != null) d.put(INVITATION_MSG, message);
return d;
}
public static BlogInvitation from(GroupId groupId, BdfDictionary d)
throws FormatException {
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
String blogAuthorName = d.getString(BLOG_AUTHOR_NAME);
byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY);
String message = d.getOptionalString(INVITATION_MSG);
long time = d.getLong(TIME);
return new BlogInvitation(groupId, sessionId, blogAuthorName,
blogPublicKey, time, message);
}
public String getBlogAuthorName() {
return blogAuthorName;
}
public byte[] getBlogPublicKey() {
return blogPublicKey;
}
}
}

View File

@@ -1,79 +0,0 @@
package org.briarproject.briar.api.forum;
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.briar.api.client.SessionId;
import org.briarproject.briar.api.sharing.SharingMessage.Invitation;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
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_MSG;
import static org.briarproject.briar.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.TIME;
@NotNullByDefault
public interface ForumSharingMessage {
@Immutable
@NotNullByDefault
class ForumInvitation extends Invitation {
private final String forumName;
private final byte[] forumSalt;
public ForumInvitation(GroupId groupId, SessionId sessionId,
String forumName, byte[] forumSalt, long time,
@Nullable String message) {
super(groupId, sessionId, time, message);
this.forumName = forumName;
this.forumSalt = forumSalt;
}
@Override
public BdfList toBdfList() {
BdfList list = super.toBdfList();
list.add(forumName);
list.add(forumSalt);
if (message != null) list.add(message);
return list;
}
@Override
public BdfDictionary toBdfDictionary() {
BdfDictionary d = toBdfDictionaryHelper();
d.put(FORUM_NAME, forumName);
d.put(FORUM_SALT, forumSalt);
if (message != null) d.put(INVITATION_MSG, message);
return d;
}
public static ForumInvitation from(GroupId groupId, BdfDictionary d)
throws FormatException {
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT);
String message = d.getOptionalString(INVITATION_MSG);
long time = d.getLong(TIME);
return new ForumInvitation(groupId, sessionId, forumName, forumSalt,
time, message);
}
public String getForumName() {
return forumName;
}
public byte[] getForumSalt() {
return forumSalt;
}
}
}

View File

@@ -1,11 +0,0 @@
package org.briarproject.briar.api.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.sync.GroupId;
@Deprecated
public interface InvitationFactory<I extends SharingMessage.Invitation> {
I build(GroupId groupId, BdfDictionary d) throws FormatException;
}

View File

@@ -4,74 +4,10 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_L
public interface SharingConstants { public interface SharingConstants {
/**
* The length of a sharing session's random salt in bytes.
*/
int SHARING_SALT_LENGTH = 32;
/** /**
* The maximum length of the optional message from the inviter to the * The maximum length of the optional message from the inviter to the
* invitee in UTF-8 bytes. * invitee in UTF-8 bytes.
*/ */
int MAX_INVITATION_MESSAGE_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024; int MAX_INVITATION_MESSAGE_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
@Deprecated
String CONTACT_ID = "contactId";
@Deprecated
String GROUP_ID = "groupId";
@Deprecated
String TO_BE_SHARED_BY_US = "toBeSharedByUs";
@Deprecated
String SHARED_BY_US = "sharedByUs";
@Deprecated
String SHARED_WITH_US = "sharedWithUs";
@Deprecated
String TYPE = "type";
@Deprecated
String SESSION_ID = "sessionId";
@Deprecated
String STORAGE_ID = "storageId";
@Deprecated
String STATE = "state";
@Deprecated
String LOCAL = "local";
@Deprecated
String TIME = "time";
@Deprecated
String IS_SHARER = "isSharer";
@Deprecated
String SHAREABLE_ID = "shareableId";
@Deprecated
String INVITATION_MSG = "invitationMsg";
@Deprecated
String INVITATION_ID = "invitationId";
@Deprecated
String RESPONSE_ID = "responseId";
@Deprecated
int SHARE_MSG_TYPE_INVITATION = 1;
@Deprecated
int SHARE_MSG_TYPE_ACCEPT = 2;
@Deprecated
int SHARE_MSG_TYPE_DECLINE = 3;
@Deprecated
int SHARE_MSG_TYPE_LEAVE = 4;
@Deprecated
int SHARE_MSG_TYPE_ABORT = 5;
@Deprecated
int TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US = 0;
@Deprecated
int TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US = 1;
@Deprecated
int TASK_ADD_SHARED_SHAREABLE = 2;
@Deprecated
int TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US = 3;
@Deprecated
int TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US = 4;
@Deprecated
int TASK_SHARE_SHAREABLE = 5;
@Deprecated
int TASK_UNSHARE_SHAREABLE_SHARED_BY_US = 6;
@Deprecated
int TASK_UNSHARE_SHAREABLE_SHARED_WITH_US = 7;
} }

View File

@@ -1,145 +0,0 @@
package org.briarproject.briar.api.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.briar.api.client.SessionId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.sharing.SharingConstants.GROUP_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.briar.api.sharing.SharingConstants.TIME;
import static org.briarproject.briar.api.sharing.SharingConstants.TYPE;
@Deprecated
@NotNullByDefault
public interface SharingMessage {
@Immutable
@NotNullByDefault
abstract class BaseMessage {
private final GroupId groupId;
private final SessionId sessionId;
private final long time;
BaseMessage(GroupId groupId, SessionId sessionId, long time) {
this.groupId = groupId;
this.sessionId = sessionId;
this.time = time;
}
public BdfList toBdfList() {
return BdfList.of(getType(), getSessionId());
}
public abstract BdfDictionary toBdfDictionary();
protected BdfDictionary toBdfDictionaryHelper() {
return BdfDictionary.of(
new BdfEntry(TYPE, getType()),
new BdfEntry(GROUP_ID, groupId),
new BdfEntry(SESSION_ID, sessionId)
);
}
public static BaseMessage from(InvitationFactory invitationFactory,
GroupId groupId, BdfDictionary d)
throws FormatException {
long type = d.getLong(TYPE);
if (type == SHARE_MSG_TYPE_INVITATION)
return invitationFactory.build(groupId, d);
else
return SimpleMessage.from(type, groupId, d);
}
public abstract long getType();
public GroupId getGroupId() {
return groupId;
}
public SessionId getSessionId() {
return sessionId;
}
public long getTime() {
return time;
}
}
@Immutable
@NotNullByDefault
abstract class Invitation extends BaseMessage {
@Nullable
protected final String message;
public Invitation(GroupId groupId, SessionId sessionId, long time,
@Nullable String message) {
super(groupId, sessionId, time);
this.message = message;
}
@Override
public long getType() {
return SHARE_MSG_TYPE_INVITATION;
}
@Nullable
public String getMessage() {
return message;
}
}
@Immutable
@NotNullByDefault
class SimpleMessage extends BaseMessage {
private final long type;
public SimpleMessage(long type, GroupId groupId, SessionId sessionId,
long time) {
super(groupId, sessionId, time);
this.type = type;
}
@Override
public long getType() {
return type;
}
@Override
public BdfDictionary toBdfDictionary() {
return toBdfDictionaryHelper();
}
public static SimpleMessage from(long type, GroupId groupId,
BdfDictionary d) throws FormatException {
if (type != SHARE_MSG_TYPE_ACCEPT &&
type != SHARE_MSG_TYPE_DECLINE &&
type != SHARE_MSG_TYPE_LEAVE &&
type != SHARE_MSG_TYPE_ABORT) throw new FormatException();
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
long time = d.getLong(TIME);
return new SimpleMessage(type, groupId, sessionId, time);
}
}
}

View File

@@ -108,7 +108,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Add the personal blog of the contact and share it with the contact // Add the personal blog of the contact and share it with the contact
Blog b = blogFactory.createBlog(c.getAuthor()); Blog b = blogFactory.createBlog(c.getAuthor());
db.addGroup(txn, b.getGroup()); addBlog(txn, b);
db.setGroupVisibility(txn, c.getId(), b.getId(), SHARED); db.setGroupVisibility(txn, c.getId(), b.getId(), SHARED);
// Share our personal blog with the contact // Share our personal blog with the contact
LocalAuthor a = identityManager.getLocalAuthor(txn); LocalAuthor a = identityManager.getLocalAuthor(txn);
@@ -170,6 +170,25 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
return false; return false;
} }
@Override
public Blog addBlog(Author author) throws DbException {
Blog b = blogFactory.createBlog(author);
Transaction txn = db.startTransaction(false);
try {
db.addGroup(txn, b.getGroup());
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
return b;
}
@Override
public void addBlog(Transaction txn, Blog b) throws DbException {
db.addGroup(txn, b.getGroup());
}
@Override @Override
public boolean canBeRemoved(GroupId g) throws DbException { public boolean canBeRemoved(GroupId g) throws DbException {
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogInvitationRequest;
import org.briarproject.briar.api.blog.BlogInvitationResponse;
import org.briarproject.briar.api.client.SessionId;
import javax.inject.Inject;
public class BlogInvitationFactoryImpl implements InvitationFactory<Blog> {
@Inject
BlogInvitationFactoryImpl() {
}
@Override
public BlogInvitationRequest createInvitationRequest(boolean local,
boolean sent, boolean seen, boolean read, InviteMessage<Blog> m,
ContactId c, boolean available, boolean canBeOpened) {
SessionId sessionId = new SessionId(m.getShareableId().getBytes());
return new BlogInvitationRequest(m.getId(), m.getContactGroupId(),
m.getTimestamp(), local, sent, seen, read, sessionId,
m.getShareable(), c, m.getMessage(), available, canBeOpened);
}
@Override
public BlogInvitationResponse 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 BlogInvitationResponse(id, contactGroupId, time, local,
sent, seen, read, sessionId, shareableId, contactId, accept);
}
}

View File

@@ -1,47 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_AUTHOR_NAME;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_PUBLIC_KEY;
@NotThreadSafe
@NotNullByDefault
class BlogInviteeSessionState extends InviteeSessionState {
private final String blogAuthorName;
private final byte[] blogPublicKey;
BlogInviteeSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId blogId,
String blogAuthorName, byte[] blogPublicKey,
MessageId invitationId) {
super(sessionId, storageId, groupId, state, contactId, blogId,
invitationId);
this.blogAuthorName = blogAuthorName;
this.blogPublicKey = blogPublicKey;
}
@Override
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(BLOG_AUTHOR_NAME, getBlogAuthorName());
d.put(BLOG_PUBLIC_KEY, getBlogPublicKey());
return d;
}
String getBlogAuthorName() {
return blogAuthorName;
}
byte[] getBlogPublicKey() {
return blogPublicKey;
}
}

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogFactory;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class BlogMessageParserImpl extends MessageParserImpl<Blog> {
private final BlogFactory blogFactory;
private final AuthorFactory authorFactory;
@Inject
BlogMessageParserImpl(ClientHelper clientHelper, BlogFactory blogFactory,
AuthorFactory authorFactory) {
super(clientHelper);
this.blogFactory = blogFactory;
this.authorFactory = authorFactory;
}
@Override
protected Blog createShareable(BdfList descriptor)
throws FormatException {
String name = descriptor.getString(0);
byte[] publicKey = descriptor.getRaw(1);
Author author = authorFactory.createAuthor(name, publicKey);
return blogFactory.createBlog(author);
}
}

View File

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

View File

@@ -1,49 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_AUTHOR_NAME;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_PUBLIC_KEY;
@NotThreadSafe
@NotNullByDefault
class BlogSharerSessionState extends SharerSessionState {
private final String blogAuthorName;
private final byte[] blogPublicKey;
BlogSharerSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId blogId,
String blogAuthorName, byte[] blogPublicKey,
@Nullable MessageId responseId) {
super(sessionId, storageId, groupId, state, contactId, blogId,
responseId);
this.blogAuthorName = blogAuthorName;
this.blogPublicKey = blogPublicKey;
}
@Override
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(BLOG_AUTHOR_NAME, getBlogAuthorName());
d.put(BLOG_PUBLIC_KEY, getBlogPublicKey());
return d;
}
String getBlogAuthorName() {
return blogAuthorName;
}
byte[] getBlogPublicKey() {
return blogPublicKey;
}
}

View File

@@ -1,93 +1,48 @@
package org.briarproject.briar.sharing; package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.data.MetadataParser; import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogFactory;
import org.briarproject.briar.api.blog.BlogInvitationRequest;
import org.briarproject.briar.api.blog.BlogInvitationResponse;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogManager.RemoveBlogHook; import org.briarproject.briar.api.blog.BlogManager.RemoveBlogHook;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.blog.BlogSharingMessage.BlogInvitation;
import org.briarproject.briar.api.blog.event.BlogInvitationRequestReceivedEvent;
import org.briarproject.briar.api.blog.event.BlogInvitationResponseReceivedEvent;
import org.briarproject.briar.api.client.MessageQueueManager;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.sharing.InvitationMessage;
import java.security.SecureRandom;
import java.util.Collection;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_AUTHOR_NAME;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_PUBLIC_KEY;
import static org.briarproject.briar.api.sharing.SharingConstants.INVITATION_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.RESPONSE_ID;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class BlogSharingManagerImpl extends class BlogSharingManagerImpl extends SharingManagerImpl<Blog>
OldSharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationRequestReceivedEvent, BlogInvitationResponseReceivedEvent>
implements BlogSharingManager, RemoveBlogHook { implements BlogSharingManager, RemoveBlogHook {
private final ContactManager contactManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final BlogManager blogManager; private final BlogManager blogManager;
private final SFactory sFactory;
private final IFactory iFactory;
private final ISFactory isFactory;
private final SSFactory ssFactory;
private final IRFactory irFactory;
private final IRRFactory irrFactory;
@Inject @Inject
BlogSharingManagerImpl(AuthorFactory authorFactory, BlogFactory blogFactory, BlogSharingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
BlogManager blogManager, ClientHelper clientHelper, Clock clock, MetadataParser metadataParser, MessageParser<Blog> messageParser,
DatabaseComponent db, MessageQueueManager messageQueueManager, SessionEncoder sessionEncoder, SessionParser sessionParser,
MetadataEncoder metadataEncoder, MetadataParser metadataParser, MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory, SecureRandom random, ContactGroupFactory contactGroupFactory,
ContactManager contactManager, IdentityManager identityManager, ProtocolEngine<Blog> engine,
MessageTracker messageTracker) { InvitationFactory<Blog> invitationFactory,
IdentityManager identityManager, BlogManager blogManager) {
super(db, messageQueueManager, clientHelper, metadataParser, super(db, clientHelper, metadataParser, messageParser, sessionEncoder,
metadataEncoder, random, contactGroupFactory, messageTracker, sessionParser, messageTracker, contactGroupFactory, engine,
clock); invitationFactory);
this.blogManager = blogManager;
this.contactManager = contactManager;
this.identityManager = identityManager; this.identityManager = identityManager;
sFactory = new SFactory(authorFactory, blogFactory, blogManager); this.blogManager = blogManager;
iFactory = new IFactory();
isFactory = new ISFactory();
ssFactory = new SSFactory();
irFactory = new IRFactory(sFactory);
irrFactory = new IRRFactory();
} }
@Override @Override
@@ -96,91 +51,18 @@ class BlogSharingManagerImpl extends
} }
@Override @Override
protected boolean canBeShared(Transaction txn, GroupId g, Contact c) protected boolean canBeShared(Transaction txn, GroupId shareableId,
throws DbException { Contact c) throws DbException {
// check if shareableId belongs to our personal blog
// check if g is our personal blog
LocalAuthor author = identityManager.getLocalAuthor(txn); LocalAuthor author = identityManager.getLocalAuthor(txn);
Blog b = blogManager.getPersonalBlog(author); Blog b = blogManager.getPersonalBlog(author);
if (b.getId().equals(g)) return false; if (b.getId().equals(shareableId)) return false;
// check if g is c's personal blog // check if shareableId belongs to c's personal blog
b = blogManager.getPersonalBlog(c.getAuthor()); b = blogManager.getPersonalBlog(c.getAuthor());
if (b.getId().equals(g)) return false; if (b.getId().equals(shareableId)) return false;
return super.canBeShared(txn, g, c); return super.canBeShared(txn, shareableId, c);
}
@Override
public Collection<Contact> getSharedWith(GroupId g) throws DbException {
Blog blog = blogManager.getBlog(g);
LocalAuthor author = identityManager.getLocalAuthor();
if (blog.getAuthor().equals(author)) {
// This is our personal blog. It is shared with all our contacts
return contactManager.getActiveContacts();
} else {
// This is someone else's blog. Look up who it is shared with
Collection<Contact> shared = super.getSharedWith(g);
// If the blog author is our contact, also add her to the list
boolean isContact = contactManager
.contactExists(blog.getAuthor().getId(), author.getId());
if (isContact) {
shared.add(contactManager
.getContact(blog.getAuthor().getId(), author.getId()));
}
return shared;
}
}
@Override
protected InvitationMessage createInvitationRequest(MessageId id,
BlogInvitation msg, ContactId contactId, GroupId blogId,
boolean available, boolean canBeOpened, long time, boolean local,
boolean sent, boolean seen, boolean read) {
return new BlogInvitationRequest(id, msg.getSessionId(),
msg.getGroupId(), contactId, msg.getBlogAuthorName(),
msg.getMessage(), blogId, available, canBeOpened, time, local,
sent, seen, read);
}
@Override
protected InvitationMessage createInvitationResponse(MessageId id,
SessionId sessionId, GroupId groupId, ContactId contactId,
GroupId blogId, boolean accept, long time, boolean local,
boolean sent, boolean seen, boolean read) {
return new BlogInvitationResponse(id, sessionId, groupId, contactId,
blogId, accept, time, local, sent, seen, read);
}
@Override
protected ShareableFactory<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState> getSFactory() {
return sFactory;
}
@Override
protected OldInvitationFactory<BlogInvitation, BlogSharerSessionState> getIFactory() {
return iFactory;
}
@Override
protected InviteeSessionStateFactory<Blog, BlogInviteeSessionState> getISFactory() {
return isFactory;
}
@Override
protected SharerSessionStateFactory<Blog, BlogSharerSessionState> getSSFactory() {
return ssFactory;
}
@Override
protected InvitationReceivedEventFactory<BlogInviteeSessionState, BlogInvitationRequestReceivedEvent> getIRFactory() {
return irFactory;
}
@Override
protected InvitationResponseReceivedEventFactory<BlogSharerSessionState, BlogInvitationResponseReceivedEvent> getIRRFactory() {
return irrFactory;
} }
@Override @Override
@@ -188,187 +70,4 @@ class BlogSharingManagerImpl extends
removingShareable(txn, b); removingShareable(txn, b);
} }
private static class SFactory implements
ShareableFactory<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState> {
private final AuthorFactory authorFactory;
private final BlogFactory blogFactory;
private final BlogManager blogManager;
private SFactory(AuthorFactory authorFactory, BlogFactory BlogFactory,
BlogManager BlogManager) {
this.authorFactory = authorFactory;
this.blogFactory = BlogFactory;
this.blogManager = BlogManager;
}
@Override
public BdfList encode(Blog f) {
return BdfList.of(
BdfList.of(
f.getAuthor().getName(),
f.getAuthor().getPublicKey()
)
);
}
@Override
public Blog get(Transaction txn, GroupId groupId)
throws DbException {
return blogManager.getBlog(txn, groupId);
}
@Override
public Blog parse(BdfList shareable) throws FormatException {
Author author = authorFactory
.createAuthor(shareable.getList(0).getString(0),
shareable.getList(0).getRaw(1));
return blogFactory.createBlog(author);
}
@Override
public Blog parse(BlogInvitation msg) {
Author author = authorFactory.createAuthor(msg.getBlogAuthorName(),
msg.getBlogPublicKey());
return blogFactory.createBlog(author);
}
@Override
public Blog parse(BlogInviteeSessionState state) {
Author author = authorFactory
.createAuthor(state.getBlogAuthorName(),
state.getBlogPublicKey());
return blogFactory.createBlog(author);
}
@Override
public Blog parse(BlogSharerSessionState state) {
Author author = authorFactory
.createAuthor(state.getBlogAuthorName(),
state.getBlogPublicKey());
return blogFactory.createBlog(author);
}
}
private static class IFactory implements
OldInvitationFactory<BlogInvitation, BlogSharerSessionState> {
@Override
public BlogInvitation build(GroupId groupId, BdfDictionary d)
throws FormatException {
return BlogInvitation.from(groupId, d);
}
@Override
public BlogInvitation build(BlogSharerSessionState localState,
long time) {
return new BlogInvitation(localState.getContactGroupId(),
localState.getSessionId(), localState.getBlogAuthorName(),
localState.getBlogPublicKey(), time,
localState.getMessage());
}
}
private static class ISFactory implements
InviteeSessionStateFactory<Blog, BlogInviteeSessionState> {
@Override
public BlogInviteeSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId,
GroupId blogId, BdfDictionary d) throws FormatException {
String blogAuthorName = d.getString(BLOG_AUTHOR_NAME);
byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY);
MessageId invitationId = new MessageId(d.getRaw(INVITATION_ID));
return new BlogInviteeSessionState(sessionId, storageId,
groupId, state, contactId, blogId, blogAuthorName,
blogPublicKey, invitationId);
}
@Override
public BlogInviteeSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId,
Blog blog, MessageId invitationId) {
return new BlogInviteeSessionState(sessionId, storageId,
groupId, state, contactId, blog.getId(),
blog.getAuthor().getName(), blog.getAuthor().getPublicKey(),
invitationId);
}
}
private static class SSFactory implements
SharerSessionStateFactory<Blog, BlogSharerSessionState> {
@Override
public BlogSharerSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId,
GroupId blogId, BdfDictionary d) throws FormatException {
String blogAuthorName = d.getString(BLOG_AUTHOR_NAME);
byte[] blogPublicKey = d.getRaw(BLOG_PUBLIC_KEY);
MessageId responseId = null;
byte[] responseIdBytes = d.getOptionalRaw(RESPONSE_ID);
if (responseIdBytes != null)
responseId = new MessageId(responseIdBytes);
return new BlogSharerSessionState(sessionId, storageId,
groupId, state, contactId, blogId, blogAuthorName,
blogPublicKey, responseId);
}
@Override
public BlogSharerSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId,
Blog blog) {
return new BlogSharerSessionState(sessionId, storageId,
groupId, state, contactId, blog.getId(),
blog.getAuthor().getName(), blog.getAuthor().getPublicKey(),
null);
}
}
private static class IRFactory implements
InvitationReceivedEventFactory<BlogInviteeSessionState, BlogInvitationRequestReceivedEvent> {
private final SFactory sFactory;
private IRFactory(SFactory sFactory) {
this.sFactory = sFactory;
}
@Override
public BlogInvitationRequestReceivedEvent build(
BlogInviteeSessionState localState, long time,
@Nullable String msg) {
Blog blog = sFactory.parse(localState);
ContactId contactId = localState.getContactId();
BlogInvitationRequest request =
new BlogInvitationRequest(localState.getInvitationId(),
localState.getSessionId(),
localState.getContactGroupId(), contactId,
blog.getAuthor().getName(), msg,
localState.getShareableId(), true, false, time,
false, false, false, false);
return new BlogInvitationRequestReceivedEvent(blog, contactId,
request);
}
}
private static class IRRFactory implements
InvitationResponseReceivedEventFactory<BlogSharerSessionState, BlogInvitationResponseReceivedEvent> {
@Override
public BlogInvitationResponseReceivedEvent build(
BlogSharerSessionState localState, boolean accept, long time) {
ContactId c = localState.getContactId();
MessageId responseId = localState.getResponseId();
if (responseId == null)
throw new IllegalStateException("No responseId");
BlogInvitationResponse response =
new BlogInvitationResponse(responseId,
localState.getSessionId(),
localState.getContactGroupId(),
localState.getContactId(),
localState.getShareableId(), accept, time, false,
false, false, false);
return new BlogInvitationResponseReceivedEvent(c, response);
}
}
} }

View File

@@ -1,17 +1,16 @@
package org.briarproject.briar.sharing; package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder; import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.client.BdfQueueMessageValidator; import org.briarproject.briar.api.blog.BlogFactory;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
@@ -20,73 +19,35 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_N
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.util.ValidationUtils.checkLength; import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize; import static org.briarproject.bramble.util.ValidationUtils.checkSize;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_AUTHOR_NAME;
import static org.briarproject.briar.api.blog.BlogConstants.BLOG_PUBLIC_KEY;
import static org.briarproject.briar.api.sharing.SharingConstants.INVITATION_MSG;
import static org.briarproject.briar.api.sharing.SharingConstants.LOCAL;
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
import static org.briarproject.briar.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.briar.api.sharing.SharingConstants.TIME;
import static org.briarproject.briar.api.sharing.SharingConstants.TYPE;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class BlogSharingValidator extends BdfQueueMessageValidator { class BlogSharingValidator extends SharingValidator {
private final BlogFactory blogFactory;
private final AuthorFactory authorFactory;
@Inject @Inject
BlogSharingValidator(ClientHelper clientHelper, BlogSharingValidator(MessageEncoder messageEncoder,
MetadataEncoder metadataEncoder, Clock clock) { ClientHelper clientHelper, MetadataEncoder metadataEncoder,
super(clientHelper, metadataEncoder, clock); Clock clock, BlogFactory blogFactory, AuthorFactory authorFactory) {
super(messageEncoder, clientHelper, metadataEncoder, clock);
this.blogFactory = blogFactory;
this.authorFactory = authorFactory;
} }
@Override @Override
protected BdfMessageContext validateMessage(Message m, Group g, protected GroupId validateDescriptor(BdfList descriptor)
BdfList body) throws FormatException { throws FormatException {
checkSize(descriptor, 2);
String name = descriptor.getString(0);
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = descriptor.getRaw(1);
checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH);
BdfDictionary d = new BdfDictionary(); Author author = authorFactory.createAuthor(name, publicKey);
long type = body.getLong(0); Blog blog = blogFactory.createBlog(author);
byte[] id = body.getRaw(1); return blog.getId();
checkLength(id, SessionId.LENGTH);
if (type == SHARE_MSG_TYPE_INVITATION) {
checkSize(body, 3, 4);
BdfList author = body.getList(2);
checkSize(author, 2);
String authorName = author.getString(0);
checkLength(authorName, 1, MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = author.getRaw(1);
checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH);
d.put(BLOG_AUTHOR_NAME, authorName);
d.put(BLOG_PUBLIC_KEY, publicKey);
if (body.size() > 3) {
String msg = body.getString(3);
checkLength(msg, 0, MAX_INVITATION_MESSAGE_LENGTH);
d.put(INVITATION_MSG, msg);
}
} else {
checkSize(body, 2);
if (type != SHARE_MSG_TYPE_ACCEPT &&
type != SHARE_MSG_TYPE_DECLINE &&
type != SHARE_MSG_TYPE_LEAVE &&
type != SHARE_MSG_TYPE_ABORT) {
throw new FormatException();
}
}
// Return the metadata
d.put(TYPE, type);
d.put(SESSION_ID, id);
d.put(LOCAL, false);
d.put(TIME, m.getTimestamp());
return new BdfMessageContext(d);
} }
} }

View File

@@ -1,13 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
import javax.annotation.Nullable;
@Deprecated
@NotNullByDefault
interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationRequestReceivedEvent> {
IR build(IS localState, long time, @Nullable String msg);
}

View File

@@ -1,11 +0,0 @@
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> {
IRR build(SS localState, boolean accept, long time);
}

View File

@@ -1,242 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.ProtocolEngine;
import org.briarproject.briar.api.sharing.SharingMessage.Invitation;
import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.briar.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US;
import static org.briarproject.briar.api.sharing.SharingConstants.TASK_ADD_SHARED_SHAREABLE;
import static org.briarproject.briar.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US;
import static org.briarproject.briar.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_WITH_US;
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>
implements ProtocolEngine<InviteeSessionState.Action, IS, BaseMessage> {
private static final Logger LOG =
Logger.getLogger(InviteeEngine.class.getName());
private final InvitationReceivedEventFactory<IS, IR>
invitationReceivedEventFactory;
private final Clock clock;
InviteeEngine(
InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory,
Clock clock) {
this.invitationReceivedEventFactory = invitationReceivedEventFactory;
this.clock = clock;
}
@Override
public StateUpdate<IS, BaseMessage> onLocalAction(
IS localState, InviteeSessionState.Action action) {
try {
InviteeSessionState.State currentState = localState.getState();
InviteeSessionState.State nextState = currentState.next(action);
localState.setState(nextState);
if (action == InviteeSessionState.Action.LOCAL_ABORT &&
currentState != InviteeSessionState.State.ERROR) {
return abortSession(currentState, localState);
}
if (nextState == InviteeSessionState.State.ERROR) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Error: Invalid action in state " +
currentState.name());
}
return noUpdate(localState, true);
}
List<BaseMessage> messages;
List<Event> events = Collections.emptyList();
if (action == InviteeSessionState.Action.LOCAL_ACCEPT ||
action == InviteeSessionState.Action.LOCAL_DECLINE) {
BaseMessage msg;
if (action == InviteeSessionState.Action.LOCAL_ACCEPT) {
localState.setTask(TASK_ADD_SHARED_SHAREABLE);
msg = new SimpleMessage(SHARE_MSG_TYPE_ACCEPT,
localState.getContactGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
} else {
localState.setTask(
TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US);
msg = new SimpleMessage(SHARE_MSG_TYPE_DECLINE,
localState.getContactGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
}
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
} else if (action == InviteeSessionState.Action.LOCAL_LEAVE) {
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getContactGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
} else {
throw new IllegalArgumentException("Unknown Local Action");
}
return new StateUpdate<IS, BaseMessage>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public StateUpdate<IS, BaseMessage> onMessageReceived(
IS localState, BaseMessage msg) {
try {
InviteeSessionState.State currentState = localState.getState();
InviteeSessionState.Action action =
InviteeSessionState.Action.getRemote(msg.getType());
InviteeSessionState.State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, msg.getType(), msg);
if (nextState == InviteeSessionState.State.ERROR) {
if (currentState != InviteeSessionState.State.ERROR) {
return abortSession(currentState, localState);
} else {
return noUpdate(localState, true);
}
}
List<BaseMessage> messages = Collections.emptyList();
List<Event> events = Collections.emptyList();
boolean deleteMsg = false;
if (currentState == InviteeSessionState.State.LEFT) {
// ignore and delete messages coming in while in that state
deleteMsg = true;
}
// the sharer left the forum she had shared with us
else if (action == InviteeSessionState.Action.REMOTE_LEAVE &&
currentState == InviteeSessionState.State.FINISHED) {
localState.setTask(TASK_UNSHARE_SHAREABLE_SHARED_WITH_US);
} else if (currentState == InviteeSessionState.State.FINISHED) {
// ignore and delete messages coming in while in that state
// note that LEAVE is possible, but was handled above
deleteMsg = true;
}
// the sharer left the forum before we couldn't even respond
else if (action == InviteeSessionState.Action.REMOTE_LEAVE) {
localState.setTask(
TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US);
}
// we have just received our invitation
else if (action == InviteeSessionState.Action.REMOTE_INVITATION) {
localState.setTask(TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US);
Invitation invitation = (Invitation) msg;
Event event = invitationReceivedEventFactory.build(localState,
msg.getTime(), invitation.getMessage());
events = Collections.singletonList(event);
} else {
throw new IllegalArgumentException("Bad state");
}
return new StateUpdate<IS, BaseMessage>(deleteMsg,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
private void logLocalAction(InviteeSessionState.State state,
InviteeSessionState localState, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String a = "response";
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
LOG.info("Sending " + a + " in state " + state.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + localState.getState().name()
);
}
private void logMessageReceived(InviteeSessionState.State currentState,
InviteeSessionState.State nextState,
long type, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String t = "unknown";
if (type == SHARE_MSG_TYPE_INVITATION) t = "INVITE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
@Override
public StateUpdate<IS, BaseMessage> onMessageDelivered(
IS localState, BaseMessage delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
}
private StateUpdate<IS, BaseMessage> abortSession(
InviteeSessionState.State currentState, IS localState)
throws FormatException {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Aborting protocol session " +
localState.getSessionId().hashCode() +
" in state " + currentState.name());
}
localState.setState(InviteeSessionState.State.ERROR);
BaseMessage msg =
new SimpleMessage(SHARE_MSG_TYPE_ABORT, localState.getContactGroupId(),
localState.getSessionId(), clock.currentTimeMillis());
List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<IS, BaseMessage>(false, false,
localState, messages, events);
}
private StateUpdate<IS, BaseMessage> noUpdate(
IS localState, boolean delete) throws FormatException {
return new StateUpdate<IS, BaseMessage>(delete, false,
localState, Collections.<BaseMessage>emptyList(),
Collections.<Event>emptyList());
}
}

View File

@@ -1,141 +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.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.briar.api.sharing.SharingConstants.INVITATION_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.briar.api.sharing.SharingConstants.STATE;
import static org.briarproject.briar.sharing.InviteeSessionState.Action.LOCAL_ACCEPT;
import static org.briarproject.briar.sharing.InviteeSessionState.Action.LOCAL_DECLINE;
import static org.briarproject.briar.sharing.InviteeSessionState.Action.LOCAL_LEAVE;
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 {
private State state;
private final MessageId invitationId;
public InviteeSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId,
GroupId shareableId, MessageId invitationId) {
super(sessionId, storageId, groupId, contactId, shareableId);
this.state = state;
this.invitationId = invitationId;
}
@Override
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(STATE, getState().getValue());
d.put(IS_SHARER, false);
d.put(INVITATION_ID, invitationId);
return d;
}
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public MessageId getInvitationId() {
return invitationId;
}
@Immutable
@NotNullByDefault
public enum State {
ERROR(0),
AWAIT_INVITATION(1) {
@Override
public State next(Action a) {
if (a == REMOTE_INVITATION) return AWAIT_LOCAL_RESPONSE;
return ERROR;
}
},
AWAIT_LOCAL_RESPONSE(2) {
@Override
public State next(Action a) {
if (a == LOCAL_ACCEPT || a == LOCAL_DECLINE) return FINISHED;
if (a == REMOTE_LEAVE) return LEFT;
return ERROR;
}
},
FINISHED(3) {
@Override
public State next(Action a) {
if (a == LOCAL_LEAVE || a == REMOTE_LEAVE) return LEFT;
return FINISHED;
}
},
LEFT(4) {
@Override
public State next(Action a) {
if (a == LOCAL_LEAVE) return ERROR;
return LEFT;
}
};
private final int value;
State(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static State fromValue(int value) {
for (State s : values()) {
if (s.value == value) return s;
}
throw new IllegalArgumentException();
}
public State next(Action a) {
return this;
}
}
@NotNullByDefault
public enum Action {
LOCAL_ACCEPT,
LOCAL_DECLINE,
LOCAL_LEAVE,
LOCAL_ABORT,
REMOTE_INVITATION,
REMOTE_LEAVE,
REMOTE_ABORT;
@Nullable
public static Action getRemote(long type) {
if (type == SHARE_MSG_TYPE_INVITATION) return REMOTE_INVITATION;
if (type == SHARE_MSG_TYPE_LEAVE) return REMOTE_LEAVE;
if (type == SHARE_MSG_TYPE_ABORT) return REMOTE_ABORT;
return null;
}
}
}

View File

@@ -1,21 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfDictionary;
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.sharing.Shareable;
@Deprecated
interface InviteeSessionStateFactory<S extends Shareable, IS extends InviteeSessionState> {
IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId,
GroupId shareableId, BdfDictionary d) throws FormatException;
IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId, S shareable,
MessageId invitationId);
}

View File

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

View File

@@ -20,6 +20,7 @@ import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException; import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.sharing.Shareable; import org.briarproject.briar.api.sharing.Shareable;
import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent;
import java.util.Map; import java.util.Map;
@@ -486,6 +487,14 @@ abstract class ProtocolEngineImpl<S extends Shareable>
// The dependency, if any, must be the last remote message // The dependency, if any, must be the last remote message
if (!isValidDependency(s, m.getPreviousMessageId())) if (!isValidDependency(s, m.getPreviousMessageId()))
return abort(txn, s); return abort(txn, s);
if (s.getState() == SHARING) {
// Broadcast event informing that contact left
ContactId contactId = getContactId(txn, s.getContactGroupId());
ContactLeftShareableEvent e =
new ContactLeftShareableEvent(s.getShareableId(),
contactId);
txn.attach(e);
}
// Move to the next state // Move to the next state
return new Session(nextState, s.getContactGroupId(), s.getShareableId(), return new Session(nextState, s.getContactGroupId(), s.getShareableId(),
s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(), s.getLastLocalMessageId(), m.getId(), s.getLocalTimestamp(),

View File

@@ -1,27 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.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> {
BdfList encode(S sh);
S get(Transaction txn, GroupId groupId) throws DbException;
S parse(BdfList shareable) throws FormatException;
S parse(I msg);
S parse(IS state);
S parse(SS state);
}

View File

@@ -1,240 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.client.ProtocolEngine;
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.briar.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.briar.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.briar.api.sharing.SharingConstants.TASK_SHARE_SHAREABLE;
import static org.briarproject.briar.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_BY_US;
import static org.briarproject.briar.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.briar.api.sharing.SharingMessage.Invitation;
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>
implements ProtocolEngine<SharerSessionState.Action, SS, BaseMessage> {
private static final Logger LOG =
Logger.getLogger(SharerEngine.class.getName());
private final OldInvitationFactory<I, SS> invitationFactory;
private final InvitationResponseReceivedEventFactory<SS, IRR>
invitationResponseReceivedEventFactory;
private final Clock clock;
SharerEngine(OldInvitationFactory<I, SS> invitationFactory,
InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory,
Clock clock) {
this.invitationFactory = invitationFactory;
this.invitationResponseReceivedEventFactory =
invitationResponseReceivedEventFactory;
this.clock = clock;
}
@Override
public StateUpdate<SS, BaseMessage> onLocalAction(
SS localState, SharerSessionState.Action action) {
try {
SharerSessionState.State currentState = localState.getState();
SharerSessionState.State nextState = currentState.next(action);
localState.setState(nextState);
if (action == SharerSessionState.Action.LOCAL_ABORT &&
currentState != SharerSessionState.State.ERROR) {
return abortSession(currentState, localState);
}
if (nextState == SharerSessionState.State.ERROR) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Error: Invalid action in state " +
currentState.name());
}
return noUpdate(localState, true);
}
List<BaseMessage> messages;
List<Event> events = Collections.emptyList();
if (action == SharerSessionState.Action.LOCAL_INVITATION) {
BaseMessage msg = invitationFactory.build(localState,
clock.currentTimeMillis());
messages = Collections.singletonList(msg);
logLocalAction(currentState, nextState, msg);
// remember that we offered to share this forum
localState
.setTask(TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US);
} else if (action == SharerSessionState.Action.LOCAL_LEAVE) {
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getContactGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
messages = Collections.singletonList(msg);
logLocalAction(currentState, nextState, msg);
} else {
throw new IllegalArgumentException("Unknown Local Action");
}
return new StateUpdate<SS, BaseMessage>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public StateUpdate<SS, BaseMessage> onMessageReceived(
SS localState, BaseMessage msg) {
try {
SharerSessionState.State currentState = localState.getState();
SharerSessionState.Action action =
SharerSessionState.Action.getRemote(msg.getType());
SharerSessionState.State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, msg.getType(), msg);
if (nextState == SharerSessionState.State.ERROR) {
if (currentState != SharerSessionState.State.ERROR) {
return abortSession(currentState, localState);
} else {
return noUpdate(localState, true);
}
}
List<BaseMessage> messages = Collections.emptyList();
List<Event> events = Collections.emptyList();
boolean deleteMsg = false;
if (currentState == SharerSessionState.State.LEFT) {
// ignore and delete messages coming in while in that state
deleteMsg = true;
} else if (action == SharerSessionState.Action.REMOTE_LEAVE) {
localState.setTask(TASK_UNSHARE_SHAREABLE_SHARED_BY_US);
} else if (currentState == SharerSessionState.State.FINISHED) {
// ignore and delete messages coming in while in that state
// note that LEAVE is possible, but was handled above
deleteMsg = true;
}
// we have sent our invitation and just got a response
else if (action == REMOTE_ACCEPT || action == REMOTE_DECLINE) {
if (action == REMOTE_ACCEPT) {
localState.setTask(TASK_SHARE_SHAREABLE);
} else {
// this ensures that the forum can be shared again
localState.setTask(
TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US);
}
Event event = invitationResponseReceivedEventFactory
.build(localState, action == REMOTE_ACCEPT,
msg.getTime());
events = Collections.singletonList(event);
} else {
throw new IllegalArgumentException("Bad state");
}
return new StateUpdate<SS, BaseMessage>(deleteMsg,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
private void logLocalAction(SharerSessionState.State currentState,
SharerSessionState.State nextState,
BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String a = "invitation";
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
LOG.info("Sending " + a + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
private void logMessageReceived(SharerSessionState.State currentState,
SharerSessionState.State nextState,
long type, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String t = "unknown";
if (type == SHARE_MSG_TYPE_ACCEPT) t = "ACCEPT";
else if (type == SHARE_MSG_TYPE_DECLINE) t = "DECLINE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
@Override
public StateUpdate<SS, BaseMessage> onMessageDelivered(
SS localState, BaseMessage delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
}
private StateUpdate<SS, BaseMessage> abortSession(
SharerSessionState.State currentState, SS localState)
throws FormatException {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Aborting protocol session " +
localState.getSessionId().hashCode() +
" in state " + currentState.name());
}
localState.setState(SharerSessionState.State.ERROR);
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_ABORT,
localState.getContactGroupId(), localState.getSessionId(),
clock.currentTimeMillis());
List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<SS, BaseMessage>(false, false,
localState, messages, events);
}
private StateUpdate<SS, BaseMessage> noUpdate(
SS localState, boolean delete)
throws FormatException {
return new StateUpdate<SS, BaseMessage>(delete, false,
localState, Collections.<BaseMessage>emptyList(),
Collections.<Event>emptyList());
}
}

View File

@@ -1,153 +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.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.briar.api.sharing.SharingConstants.RESPONSE_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.briar.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.briar.api.sharing.SharingConstants.STATE;
import static org.briarproject.briar.sharing.SharerSessionState.Action.LOCAL_INVITATION;
import static org.briarproject.briar.sharing.SharerSessionState.Action.LOCAL_LEAVE;
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_ACCEPT;
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_DECLINE;
import static org.briarproject.briar.sharing.SharerSessionState.Action.REMOTE_LEAVE;
@Deprecated
@NotThreadSafe
@NotNullByDefault
public abstract class SharerSessionState extends SharingSessionState {
private State state;
@Nullable
private String msg = null;
@Nullable
private MessageId responseId;
public SharerSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId,
GroupId shareableId, @Nullable MessageId responseId) {
super(sessionId, storageId, groupId, contactId, shareableId);
this.state = state;
this.responseId = responseId;
}
@Override
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(STATE, getState().getValue());
d.put(IS_SHARER, true);
if (responseId != null) d.put(RESPONSE_ID, responseId);
return d;
}
public void setState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void setMessage(String msg) {
this.msg = msg;
}
@Nullable
public String getMessage() {
return msg;
}
public void setResponseId(@Nullable MessageId responseId) {
this.responseId = responseId;
}
@Nullable
public MessageId getResponseId() {
return responseId;
}
public enum State {
ERROR(0),
PREPARE_INVITATION(1) {
@Override
public State next(Action a) {
if (a == LOCAL_INVITATION) return AWAIT_RESPONSE;
return ERROR;
}
},
AWAIT_RESPONSE(2) {
@Override
public State next(Action a) {
if (a == REMOTE_ACCEPT || a == REMOTE_DECLINE) return FINISHED;
if (a == LOCAL_LEAVE) return LEFT;
return ERROR;
}
},
FINISHED(3) {
@Override
public State next(Action a) {
if (a == LOCAL_LEAVE || a == REMOTE_LEAVE) return LEFT;
return FINISHED;
}
},
LEFT(4) {
@Override
public State next(Action a) {
if (a == LOCAL_LEAVE) return ERROR;
return LEFT;
}
};
private final int value;
State(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static State fromValue(int value) {
for (State s : values()) {
if (s.value == value) return s;
}
throw new IllegalArgumentException();
}
public State next(Action a) {
return this;
}
}
public enum Action {
LOCAL_INVITATION,
LOCAL_LEAVE,
LOCAL_ABORT,
REMOTE_ACCEPT,
REMOTE_DECLINE,
REMOTE_LEAVE,
REMOTE_ABORT;
public static Action getRemote(long type) {
if (type == SHARE_MSG_TYPE_ACCEPT) return REMOTE_ACCEPT;
if (type == SHARE_MSG_TYPE_DECLINE) return REMOTE_DECLINE;
if (type == SHARE_MSG_TYPE_LEAVE) return REMOTE_LEAVE;
if (type == SHARE_MSG_TYPE_ABORT) return REMOTE_ABORT;
return null;
}
}
}

View File

@@ -1,22 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
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 org.briarproject.briar.api.sharing.Shareable;
@Deprecated
@NotNullByDefault
interface SharerSessionStateFactory<S extends Shareable, SS extends SharerSessionState> {
SS build(SessionId sessionId, MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId,
GroupId shareableId, BdfDictionary d) throws FormatException;
SS build(SessionId sessionId, MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId, S shareable);
}

View File

@@ -23,6 +23,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.briar.api.client.MessageTracker; import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId; import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.sharing.InvitationMessage; import org.briarproject.briar.api.sharing.InvitationMessage;
import org.briarproject.briar.api.sharing.InvitationRequest; import org.briarproject.briar.api.sharing.InvitationRequest;
@@ -211,8 +212,10 @@ abstract class SharingManagerImpl<S extends Shareable>
SessionId sessionId = getSessionId(shareableId); SessionId sessionId = getSessionId(shareableId);
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
// Look up the session, if there is one
Contact contact = db.getContact(txn, contactId); Contact contact = db.getContact(txn, contactId);
if (!canBeShared(txn, shareableId, contact))
throw new ProtocolStateException();
// Look up the session, if there is one
GroupId contactGroupId = getContactGroup(contact).getId(); GroupId contactGroupId = getContactGroup(contact).getId();
StoredSession ss = getSession(txn, contactGroupId, sessionId); StoredSession ss = getSession(txn, contactGroupId, sessionId);
// Create or parse the session // Create or parse the session
@@ -400,12 +403,22 @@ abstract class SharingManagerImpl<S extends Shareable>
@Override @Override
public boolean canBeShared(GroupId g, Contact c) throws DbException { public boolean canBeShared(GroupId g, Contact c) throws DbException {
GroupId contactGroupId = getContactGroup(c).getId();
SessionId sessionId = getSessionId(g);
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
StoredSession ss = getSession(txn, contactGroupId, sessionId); boolean canBeShared = canBeShared(txn, g, c);
db.commitTransaction(txn); db.commitTransaction(txn);
return canBeShared;
} finally {
db.endTransaction(txn);
}
}
protected boolean canBeShared(Transaction txn, GroupId g, Contact c)
throws DbException {
GroupId contactGroupId = getContactGroup(c).getId();
SessionId sessionId = getSessionId(g);
try {
StoredSession ss = getSession(txn, contactGroupId, sessionId);
// If there's no session, we can share the group with the contact // If there's no session, we can share the group with the contact
if (ss == null) return true; if (ss == null) return true;
// If the session's in the right state, the contact can be invited // If the session's in the right state, the contact can be invited
@@ -414,8 +427,6 @@ abstract class SharingManagerImpl<S extends Shareable>
return session.getState().canInvite(); return session.getState().canInvite();
} catch (FormatException e) { } catch (FormatException e) {
throw new DbException(e); throw new DbException(e);
} finally {
db.endTransaction(txn);
} }
} }

View File

@@ -3,12 +3,14 @@ package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.MetadataEncoder; import org.briarproject.bramble.api.data.MetadataEncoder;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.ValidationManager; import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogFactory;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.client.MessageQueueManager;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumFactory; import org.briarproject.briar.api.forum.ForumFactory;
import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumManager;
@@ -35,17 +37,32 @@ public class SharingModule {
BlogSharingManager blogSharingManager; BlogSharingManager blogSharingManager;
} }
@Provides
MessageEncoder provideMessageEncoder(MessageEncoderImpl messageEncoder) {
return messageEncoder;
}
@Provides
SessionEncoder provideSessionEncoder(SessionEncoderImpl sessionEncoder) {
return sessionEncoder;
}
@Provides
SessionParser provideSessionParser(SessionParserImpl sessionParser) {
return sessionParser;
}
@Provides @Provides
@Singleton @Singleton
BlogSharingValidator provideBlogSharingValidator( BlogSharingValidator provideBlogSharingValidator(
MessageQueueManager messageQueueManager, ClientHelper clientHelper, ValidationManager validationManager, MessageEncoder messageEncoder,
MetadataEncoder metadataEncoder, Clock clock) { ClientHelper clientHelper, MetadataEncoder metadataEncoder,
Clock clock, BlogFactory blogFactory, AuthorFactory authorFactory) {
BlogSharingValidator validator = BlogSharingValidator validator =
new BlogSharingValidator(clientHelper, metadataEncoder, clock); new BlogSharingValidator(messageEncoder, clientHelper,
messageQueueManager.registerMessageValidator( metadataEncoder, clock, blogFactory, authorFactory);
BlogSharingManager.CLIENT_ID, validator); validationManager.registerMessageValidator(BlogSharingManager.CLIENT_ID,
validator);
return validator; return validator;
} }
@@ -53,14 +70,13 @@ public class SharingModule {
@Singleton @Singleton
BlogSharingManager provideBlogSharingManager( BlogSharingManager provideBlogSharingManager(
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
MessageQueueManager messageQueueManager, ValidationManager validationManager,
ConversationManager conversationManager, BlogManager blogManager, ConversationManager conversationManager, BlogManager blogManager,
BlogSharingManagerImpl blogSharingManager) { BlogSharingManagerImpl blogSharingManager) {
lifecycleManager.registerClient(blogSharingManager); lifecycleManager.registerClient(blogSharingManager);
contactManager.registerAddContactHook(blogSharingManager); contactManager.registerAddContactHook(blogSharingManager);
contactManager.registerRemoveContactHook(blogSharingManager); contactManager.registerRemoveContactHook(blogSharingManager);
messageQueueManager.registerIncomingMessageHook( validationManager.registerIncomingMessageHook(
BlogSharingManager.CLIENT_ID, blogSharingManager); BlogSharingManager.CLIENT_ID, blogSharingManager);
conversationManager.registerConversationClient(blogSharingManager); conversationManager.registerConversationClient(blogSharingManager);
blogManager.registerRemoveBlogHook(blogSharingManager); blogManager.registerRemoveBlogHook(blogSharingManager);
@@ -68,6 +84,24 @@ public class SharingModule {
return blogSharingManager; return blogSharingManager;
} }
@Provides
MessageParser<Blog> provideBlogMessageParser(
BlogMessageParserImpl blogMessageParser) {
return blogMessageParser;
}
@Provides
ProtocolEngine<Blog> provideBlogProtocolEngine(
BlogProtocolEngineImpl blogProtocolEngine) {
return blogProtocolEngine;
}
@Provides
InvitationFactory<Blog> provideBlogInvitationFactory(
BlogInvitationFactoryImpl blogInvitationFactory) {
return blogInvitationFactory;
}
@Provides @Provides
@Singleton @Singleton
ForumSharingValidator provideForumSharingValidator( ForumSharingValidator provideForumSharingValidator(
@@ -102,27 +136,12 @@ public class SharingModule {
return forumSharingManager; return forumSharingManager;
} }
@Provides
MessageEncoder provideMessageEncoder(MessageEncoderImpl messageEncoder) {
return messageEncoder;
}
@Provides @Provides
MessageParser<Forum> provideForumMessageParser( MessageParser<Forum> provideForumMessageParser(
ForumMessageParserImpl forumMessageParser) { ForumMessageParserImpl forumMessageParser) {
return forumMessageParser; return forumMessageParser;
} }
@Provides
SessionEncoder provideSessionEncoder(SessionEncoderImpl sessionEncoder) {
return sessionEncoder;
}
@Provides
SessionParser provideSessionParser(SessionParserImpl sessionParser) {
return sessionParser;
}
@Provides @Provides
ProtocolEngine<Forum> provideForumProtocolEngine( ProtocolEngine<Forum> provideForumProtocolEngine(
ForumProtocolEngineImpl forumProtocolEngine) { ForumProtocolEngineImpl forumProtocolEngine) {

View File

@@ -1,107 +0,0 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
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.sharing.SharingConstants.CONTACT_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.GROUP_ID;
import static org.briarproject.briar.api.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.briar.api.sharing.SharingConstants.SESSION_ID;
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 {
private final SessionId sessionId;
private final MessageId storageId;
private final GroupId groupId;
private final ContactId contactId;
private final GroupId shareableId;
private int task = -1; // TODO get rid of task, see #376
SharingSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, ContactId contactId, GroupId shareableId) {
this.sessionId = sessionId;
this.storageId = storageId;
this.groupId = groupId;
this.contactId = contactId;
this.shareableId = shareableId;
}
static SharingSessionState fromBdfDictionary(
InviteeSessionStateFactory isFactory,
SharerSessionStateFactory ssFactory, BdfDictionary d)
throws FormatException {
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
MessageId messageId = new MessageId(d.getRaw(STORAGE_ID));
GroupId groupId = new GroupId(d.getRaw(GROUP_ID));
ContactId contactId = new ContactId(d.getLong(CONTACT_ID).intValue());
GroupId forumId = new GroupId(d.getRaw(SHAREABLE_ID));
int intState = d.getLong(STATE).intValue();
if (d.getBoolean(IS_SHARER)) {
SharerSessionState.State state =
SharerSessionState.State.fromValue(intState);
return ssFactory.build(sessionId, messageId, groupId, state,
contactId, forumId, d);
} else {
InviteeSessionState.State state =
InviteeSessionState.State.fromValue(intState);
return isFactory.build(sessionId, messageId, groupId, state,
contactId, forumId, d);
}
}
public BdfDictionary toBdfDictionary() {
BdfDictionary d = new BdfDictionary();
d.put(SESSION_ID, getSessionId());
d.put(STORAGE_ID, getStorageId());
d.put(GROUP_ID, getContactGroupId());
d.put(CONTACT_ID, getContactId().getInt());
d.put(SHAREABLE_ID, getShareableId());
return d;
}
public SessionId getSessionId() {
return sessionId;
}
public MessageId getStorageId() {
return storageId;
}
public GroupId getContactGroupId() {
return groupId;
}
public ContactId getContactId() {
return contactId;
}
public GroupId getShareableId() {
return shareableId;
}
public void setTask(int task) {
this.task = task;
}
public int getTask() {
return task;
}
}

View File

@@ -4,7 +4,6 @@ import net.jodah.concurrentunit.Waiter;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -17,6 +16,7 @@ import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.blog.event.BlogInvitationRequestReceivedEvent; import org.briarproject.briar.api.blog.event.BlogInvitationRequestReceivedEvent;
import org.briarproject.briar.api.blog.event.BlogInvitationResponseReceivedEvent; import org.briarproject.briar.api.blog.event.BlogInvitationResponseReceivedEvent;
import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.sharing.InvitationMessage; import org.briarproject.briar.api.sharing.InvitationMessage;
import org.briarproject.briar.test.BriarIntegrationTest; import org.briarproject.briar.test.BriarIntegrationTest;
import org.briarproject.briar.test.BriarIntegrationTestComponent; import org.briarproject.briar.test.BriarIntegrationTestComponent;
@@ -35,7 +35,6 @@ import static org.briarproject.briar.test.BriarTestUtils.assertGroupCount;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class BlogSharingIntegrationTest public class BlogSharingIntegrationTest
extends BriarIntegrationTest<BriarIntegrationTestComponent> { extends BriarIntegrationTest<BriarIntegrationTestComponent> {
@@ -92,7 +91,7 @@ public class BlogSharingIntegrationTest
injectEagerSingletons(c2); injectEagerSingletons(c2);
} }
@Test @Test(expected = ProtocolStateException.class)
public void testPersonalBlogCannotBeSharedWithOwner() throws Exception { public void testPersonalBlogCannotBeSharedWithOwner() throws Exception {
listenToEvents(true); listenToEvents(true);
@@ -109,11 +108,6 @@ public class BlogSharingIntegrationTest
blogSharingManager0 blogSharingManager0
.sendInvitation(blog1.getId(), contactId1From0, "Hi!", .sendInvitation(blog1.getId(), contactId1From0, "Hi!",
clock.currentTimeMillis()); clock.currentTimeMillis());
// sync invitation
sync0To1(1, false);
// make sure the invitee ignored the request for their own blog
assertFalse(listener1.requestReceived);
} }
@Test @Test
@@ -293,17 +287,11 @@ public class BlogSharingIntegrationTest
assertFalse(blogSharingManager0.getSharedWith(blog2.getId()) assertFalse(blogSharingManager0.getSharedWith(blog2.getId())
.contains(contact1From0)); .contains(contact1From0));
// invitee no longer has blog shared by sharer // invitee no longer has blog shared by sharer
try { assertEquals(0,
blogSharingManager1.getSharedWith(blog2.getId()); blogSharingManager1.getSharedWith(blog2.getId()).size());
fail(); // blog can be shared again by sharer
} catch (NoSuchGroupException e) {
// expected
}
// blog can be shared again
assertTrue( assertTrue(
blogSharingManager0.canBeShared(blog2.getId(), contact1From0)); blogSharingManager0.canBeShared(blog2.getId(), contact1From0));
assertTrue(
blogSharingManager1.canBeShared(blog2.getId(), contact0From1));
} }
@Test @Test

View File

@@ -0,0 +1,185 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.briar.api.blog.Blog;
import org.jmock.Expectations;
import org.junit.Test;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.briar.api.blog.BlogConstants.MAX_BLOG_NAME_LENGTH;
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
import static org.briarproject.briar.sharing.MessageType.INVITE;
public class BlogSharingValidatorTest extends SharingValidatorTest {
private final AuthorId authorId = new AuthorId(getRandomId());
private final String authorName = TestUtils.getRandomString(42);
private final byte[] publicKey =
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
private final Author author = new Author(authorId, authorName, publicKey);
private final Blog blog = new Blog(group, author);
private final BdfList descriptor = BdfList.of(authorName, publicKey);
private final String content =
TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH);
@Override
SharingValidator getValidator() {
return new BlogSharingValidator(messageEncoder, clientHelper,
metadataEncoder, clock, blogFactory, authorFactory);
}
@Test
public void testAcceptsInvitationWithContent() throws Exception {
expectCreateBlog(authorName, publicKey);
expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, descriptor,
content));
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testAcceptsInvitationWithNullContent() throws Exception {
expectCreateBlog(authorName, publicKey);
expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, descriptor, null));
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testAcceptsInvitationWithNullPreviousMsgId() throws Exception {
expectCreateBlog(authorName, publicKey);
expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), null, descriptor, null));
assertExpectedContext(messageContext, null);
}
@Test(expected = FormatException.class)
public void testRejectsNullBlogName() throws Exception {
BdfList invalidDescriptor = BdfList.of(null, publicKey);
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
null));
}
@Test(expected = FormatException.class)
public void testRejectsNonStringBlogName() throws Exception {
BdfList invalidDescriptor = BdfList.of(123, publicKey);
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
null));
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBlogName() throws Exception {
BdfList invalidDescriptor = BdfList.of("", publicKey);
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
null));
}
@Test
public void testAcceptsMinLengthBlogName() throws Exception {
String shortBlogName = TestUtils.getRandomString(1);
BdfList validDescriptor = BdfList.of(shortBlogName, publicKey);
expectCreateBlog(shortBlogName, publicKey);
expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, validDescriptor,
null));
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBlogName() throws Exception {
String invalidBlogName =
TestUtils.getRandomString(MAX_BLOG_NAME_LENGTH + 1);
BdfList invalidDescriptor = BdfList.of(invalidBlogName, publicKey);
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
null));
}
@Test(expected = FormatException.class)
public void testRejectsNullPublicKey() throws Exception {
BdfList invalidDescriptor = BdfList.of(authorName, null);
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
null));
}
@Test(expected = FormatException.class)
public void testRejectsNonRawPublicKey() throws Exception {
BdfList invalidDescriptor = BdfList.of(authorName, 123);
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
null));
}
@Test(expected = FormatException.class)
public void testRejectsTooLongPublicKey() throws Exception {
byte[] invalidKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1);
BdfList invalidDescriptor = BdfList.of(authorName, invalidKey);
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
null));
}
@Test
public void testAcceptsMinLengthPublicKey() throws Exception {
byte[] key = TestUtils.getRandomBytes(1);
BdfList validDescriptor = BdfList.of(authorName, key);
expectCreateBlog(authorName, key);
expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, validDescriptor,
null));
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsNonStringContent() throws Exception {
expectCreateBlog(authorName, publicKey);
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, descriptor,
123));
}
@Test
public void testAcceptsMinLengthContent() throws Exception {
expectCreateBlog(authorName, publicKey);
expectEncodeMetadata(INVITE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, descriptor, "1"));
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsTooLongContent() throws Exception {
String invalidContent =
TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH + 1);
expectCreateBlog(authorName, publicKey);
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, descriptor,
invalidContent));
}
private void expectCreateBlog(final String name, final byte[] key) {
context.checking(new Expectations() {{
oneOf(authorFactory).createAuthor(name, key);
will(returnValue(author));
oneOf(blogFactory).createBlog(author);
will(returnValue(blog));
}});
}
}

View File

@@ -1,45 +1,20 @@
package org.briarproject.briar.sharing; package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.client.BdfMessageContext; import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.test.ValidatorTestCase;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumFactory;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;
import java.util.Collection;
import javax.annotation.Nullable;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH; import static org.briarproject.briar.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH; import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH; import static org.briarproject.briar.api.sharing.SharingConstants.MAX_INVITATION_MESSAGE_LENGTH;
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.INVITE;
import static org.briarproject.briar.sharing.MessageType.LEAVE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ForumSharingValidatorTest extends ValidatorTestCase { public class ForumSharingValidatorTest extends SharingValidatorTest {
private final MessageEncoder messageEncoder =
context.mock(MessageEncoder.class);
private final ForumFactory forumFactory = context.mock(ForumFactory.class);
private final ForumSharingValidator v =
new ForumSharingValidator(messageEncoder, clientHelper,
metadataEncoder, clock, forumFactory);
private final MessageId previousMsgId = new MessageId(getRandomId());
private final String forumName = private final String forumName =
TestUtils.getRandomString(MAX_FORUM_NAME_LENGTH); TestUtils.getRandomString(MAX_FORUM_NAME_LENGTH);
private final byte[] salt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH); private final byte[] salt = TestUtils.getRandomBytes(FORUM_SALT_LENGTH);
@@ -47,8 +22,12 @@ public class ForumSharingValidatorTest extends ValidatorTestCase {
private final BdfList descriptor = BdfList.of(forumName, salt); private final BdfList descriptor = BdfList.of(forumName, salt);
private final String content = private final String content =
TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH); TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH);
private final BdfDictionary meta =
BdfDictionary.of(new BdfEntry("meta", "data")); @Override
SharingValidator getValidator() {
return new ForumSharingValidator(messageEncoder, clientHelper,
metadataEncoder, clock, forumFactory);
}
@Test @Test
public void testAcceptsInvitationWithContent() throws Exception { public void testAcceptsInvitationWithContent() throws Exception {
@@ -78,107 +57,6 @@ public class ForumSharingValidatorTest extends ValidatorTestCase {
assertExpectedContext(messageContext, null); assertExpectedContext(messageContext, null);
} }
@Test
public void testAcceptsAccept() throws Exception {
expectEncodeMetadata(ACCEPT);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(ACCEPT.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testAcceptsDecline() throws Exception {
expectEncodeMetadata(DECLINE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(DECLINE.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testAcceptsLeave() throws Exception {
expectEncodeMetadata(LEAVE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(LEAVE.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testAcceptsAbort() throws Exception {
expectEncodeMetadata(ABORT);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsNullMessageType() throws Exception {
v.validateMessage(message, group,
BdfList.of(null, groupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsNonLongMessageType() throws Exception {
v.validateMessage(message, group,
BdfList.of("", groupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsInvalidMessageType() throws Exception {
int invalidMessageType = ABORT.getValue() + 1;
v.validateMessage(message, group,
BdfList.of(invalidMessageType, groupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsNullSessionId() throws Exception {
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), null, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsNonRawSessionId() throws Exception {
v.validateMessage(message, group, BdfList.of(ABORT.getValue(), 123));
}
@Test(expected = FormatException.class)
public void testRejectsTooShortSessionId() throws Exception {
byte[] invalidGroupId = TestUtils.getRandomBytes(UniqueId.LENGTH - 1);
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), invalidGroupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsTooLongSessionId() throws Exception {
byte[] invalidGroupId = TestUtils.getRandomBytes(UniqueId.LENGTH + 1);
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), invalidGroupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForAbort() throws Exception {
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), groupId));
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForAbort() throws Exception {
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), groupId, previousMsgId, 123));
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForInvitation() throws Exception {
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, descriptor));
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForInvitation() throws Exception {
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, descriptor, null,
123));
}
@Test(expected = FormatException.class) @Test(expected = FormatException.class)
public void testRejectsNullForumName() throws Exception { public void testRejectsNullForumName() throws Exception {
BdfList invalidDescriptor = BdfList.of(null, salt); BdfList invalidDescriptor = BdfList.of(null, salt);
@@ -293,25 +171,4 @@ public class ForumSharingValidatorTest extends ValidatorTestCase {
}}); }});
} }
private void expectEncodeMetadata(final MessageType type) {
context.checking(new Expectations() {{
oneOf(messageEncoder)
.encodeMetadata(type, groupId, timestamp, false, false,
false, false);
will(returnValue(meta));
}});
}
private void assertExpectedContext(BdfMessageContext messageContext,
@Nullable MessageId previousMsgId) throws FormatException {
Collection<MessageId> dependencies = messageContext.getDependencies();
if (previousMsgId == null) {
assertTrue(dependencies.isEmpty());
} else {
assertEquals(1, dependencies.size());
assertTrue(dependencies.contains(previousMsgId));
}
assertEquals(meta, messageContext.getDictionary());
}
} }

View File

@@ -0,0 +1,167 @@
package org.briarproject.briar.sharing;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.client.BdfMessageContext;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.test.ValidatorTestCase;
import org.briarproject.briar.api.blog.BlogFactory;
import org.briarproject.briar.api.forum.ForumFactory;
import org.jmock.Expectations;
import org.junit.Test;
import java.util.Collection;
import javax.annotation.Nullable;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.briar.sharing.MessageType.ABORT;
import static org.briarproject.briar.sharing.MessageType.ACCEPT;
import static org.briarproject.briar.sharing.MessageType.DECLINE;
import static org.briarproject.briar.sharing.MessageType.INVITE;
import static org.briarproject.briar.sharing.MessageType.LEAVE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public abstract class SharingValidatorTest extends ValidatorTestCase {
protected final MessageEncoder messageEncoder =
context.mock(MessageEncoder.class);
protected final ForumFactory forumFactory =
context.mock(ForumFactory.class);
protected final BlogFactory blogFactory = context.mock(BlogFactory.class);
protected final SharingValidator v = getValidator();
protected final MessageId previousMsgId = new MessageId(getRandomId());
private final BdfDictionary meta =
BdfDictionary.of(new BdfEntry("meta", "data"));
abstract SharingValidator getValidator();
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForInvitation() throws Exception {
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, descriptor));
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForInvitation() throws Exception {
v.validateMessage(message, group,
BdfList.of(INVITE.getValue(), previousMsgId, descriptor, null,
123));
}
@Test
public void testAcceptsAccept() throws Exception {
expectEncodeMetadata(ACCEPT);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(ACCEPT.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testAcceptsDecline() throws Exception {
expectEncodeMetadata(DECLINE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(DECLINE.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testAcceptsLeave() throws Exception {
expectEncodeMetadata(LEAVE);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(LEAVE.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, previousMsgId);
}
@Test
public void testAcceptsAbort() throws Exception {
expectEncodeMetadata(ABORT);
BdfMessageContext messageContext = v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), groupId, previousMsgId));
assertExpectedContext(messageContext, previousMsgId);
}
@Test(expected = FormatException.class)
public void testRejectsNullMessageType() throws Exception {
v.validateMessage(message, group,
BdfList.of(null, groupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsNonLongMessageType() throws Exception {
v.validateMessage(message, group,
BdfList.of("", groupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsInvalidMessageType() throws Exception {
int invalidMessageType = ABORT.getValue() + 1;
v.validateMessage(message, group,
BdfList.of(invalidMessageType, groupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsNullSessionId() throws Exception {
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), null, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsNonRawSessionId() throws Exception {
v.validateMessage(message, group, BdfList.of(ABORT.getValue(), 123));
}
@Test(expected = FormatException.class)
public void testRejectsTooShortSessionId() throws Exception {
byte[] invalidGroupId = TestUtils.getRandomBytes(UniqueId.LENGTH - 1);
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), invalidGroupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsTooLongSessionId() throws Exception {
byte[] invalidGroupId = TestUtils.getRandomBytes(UniqueId.LENGTH + 1);
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), invalidGroupId, previousMsgId));
}
@Test(expected = FormatException.class)
public void testRejectsTooShortBodyForAbort() throws Exception {
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), groupId));
}
@Test(expected = FormatException.class)
public void testRejectsTooLongBodyForAbort() throws Exception {
v.validateMessage(message, group,
BdfList.of(ABORT.getValue(), groupId, previousMsgId, 123));
}
protected void expectEncodeMetadata(final MessageType type) {
context.checking(new Expectations() {{
oneOf(messageEncoder)
.encodeMetadata(type, groupId, timestamp, false, false,
false, false);
will(returnValue(meta));
}});
}
protected void assertExpectedContext(BdfMessageContext messageContext,
@Nullable MessageId previousMsgId) throws FormatException {
Collection<MessageId> dependencies = messageContext.getDependencies();
if (previousMsgId == null) {
assertTrue(dependencies.isEmpty());
} else {
assertEquals(1, dependencies.size());
assertTrue(dependencies.contains(previousMsgId));
}
assertEquals(meta, messageContext.getDictionary());
}
}