Updated java.library.path.

This commit is contained in:
akwizgran
2016-11-23 14:58:42 +00:00
parent f6d23b4d1a
commit ad6016d428
1410 changed files with 15690 additions and 12924 deletions

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.briar.api.client.BaseGroup;
import org.briarproject.briar.api.sharing.Shareable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class Blog extends BaseGroup implements Shareable {
private final Author author;
public Blog(Group group, Author author) {
super(group);
this.author = author;
}
public Author getAuthor() {
return author;
}
@Override
public boolean equals(Object o) {
return o instanceof Blog && super.equals(o);
}
/**
* Returns the blog's author's name, not the name as shown in the UI.
*/
@Override
public String getName() {
return author.getName();
}
}

View File

@@ -0,0 +1,46 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.blog.MessageType.COMMENT;
import static org.briarproject.briar.api.blog.MessageType.WRAPPED_COMMENT;
@Immutable
@NotNullByDefault
public class BlogCommentHeader extends BlogPostHeader {
@Nullable
private final String comment;
private final BlogPostHeader parent;
public BlogCommentHeader(MessageType type, GroupId groupId,
@Nullable String comment, BlogPostHeader parent, MessageId id,
long timestamp, long timeReceived, Author author,
Status authorStatus, boolean read) {
super(type, groupId, id, parent.getId(), timestamp,
timeReceived, author, authorStatus, read);
if (type != COMMENT && type != WRAPPED_COMMENT)
throw new IllegalArgumentException("Incompatible Message Type");
this.comment = comment;
this.parent = parent;
}
@Nullable
public String getComment() {
return comment;
}
public BlogPostHeader getParent() {
return parent;
}
}

View File

@@ -0,0 +1,50 @@
package org.briarproject.briar.api.blog;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
public interface BlogConstants {
/**
* The maximum length of a blogs's name in UTF-8 bytes.
*/
int MAX_BLOG_TITLE_LENGTH = 100;
/**
* 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.
*/
int MAX_BLOG_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
/**
* The maximum length of a blog comment in bytes.
*/
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
String KEY_TYPE = "type";
String KEY_DESCRIPTION = "description";
String KEY_TIMESTAMP = "timestamp";
String KEY_TIME_RECEIVED = "timeReceived";
String KEY_AUTHOR_ID = "id";
String KEY_AUTHOR_NAME = "name";
String KEY_PUBLIC_KEY = "publicKey";
String KEY_AUTHOR = "author";
String KEY_READ = "read";
String KEY_COMMENT = "comment";
String KEY_ORIGINAL_MSG_ID = "originalMessageId";
String KEY_ORIGINAL_PARENT_MSG_ID = "originalParentMessageId";
/**
* This is the ID of either a message wrapped from a different group
* or of a message from the same group that therefore needed no wrapping.
*/
String KEY_PARENT_MSG_ID = "parentMessageId";
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
@NotNullByDefault
public interface BlogFactory {
/**
* Creates a personal blog for a given author.
*/
Blog createBlog(Author author);
/**
* Parses a blog with the given Group
*/
Blog parseBlog(Group g) throws FormatException;
}

View File

@@ -0,0 +1,31 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.contact.ContactId;
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.InvitationRequest;
import javax.annotation.Nullable;
@NotNullByDefault
public class BlogInvitationRequest extends InvitationRequest {
private final String blogAuthorName;
public BlogInvitationRequest(MessageId id, SessionId sessionId,
GroupId groupId, ContactId contactId, String blogAuthorName,
@Nullable String message, boolean available, long time,
boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, groupId, contactId, message, available, time,
local, sent, seen, read);
this.blogAuthorName = blogAuthorName;
}
public String getBlogAuthorName() {
return blogAuthorName;
}
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.contact.ContactId;
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.InvitationResponse;
@NotNullByDefault
public class BlogInvitationResponse extends InvitationResponse {
public BlogInvitationResponse(MessageId id, SessionId sessionId,
GroupId groupId, ContactId contactId, boolean accept, long time,
boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, groupId, contactId, accept, time, local, sent,
seen, read);
}
}

View File

@@ -0,0 +1,105 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import java.util.Collection;
import javax.annotation.Nullable;
@NotNullByDefault
public interface BlogManager {
/**
* Unique ID of the blog client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog");
/**
* Returns true if a blog can be removed.
*/
boolean canBeRemoved(GroupId g) throws DbException;
/**
* Removes and deletes a blog.
*/
void removeBlog(Blog b) throws DbException;
/**
* Stores a local blog post.
*/
void addLocalPost(BlogPost p) throws DbException;
/**
* Stores a local blog post.
*/
void addLocalPost(Transaction txn, BlogPost p) throws DbException;
/**
* Adds a comment to an existing blog post or reblogs it.
*/
void addLocalComment(LocalAuthor author, GroupId groupId,
@Nullable String comment, BlogPostHeader wHeader)
throws DbException;
/**
* Returns the blog with the given ID.
*/
Blog getBlog(GroupId g) throws DbException;
/**
* Returns the blog with the given ID.
*/
Blog getBlog(Transaction txn, GroupId g) throws DbException;
/**
* Returns all blogs owned by the given localAuthor.
*/
Collection<Blog> getBlogs(LocalAuthor localAuthor) throws DbException;
/**
* Returns only the personal blog of the given author.
*/
Blog getPersonalBlog(Author author);
/**
* Returns all blogs to which the user subscribes.
*/
Collection<Blog> getBlogs() throws DbException;
/**
* Returns the header of the blog post with the given ID.
*/
BlogPostHeader getPostHeader(GroupId g, MessageId m) throws DbException;
/**
* Returns the body of the blog post with the given ID.
*/
String getPostBody(MessageId m) throws DbException;
/**
* Returns the headers of all posts in the given blog.
*/
Collection<BlogPostHeader> getPostHeaders(GroupId g) throws DbException;
/**
* Marks a blog post as read or unread.
*/
void setReadFlag(MessageId m, boolean read) throws DbException;
/**
* Registers a hook to be called whenever a blog is removed.
*/
void registerRemoveBlogHook(RemoveBlogHook hook);
interface RemoveBlogHook {
void removingBlog(Transaction txn, Blog b) throws DbException;
}
}

View File

@@ -0,0 +1,20 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.forum.ForumPost;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class BlogPost extends ForumPost {
public BlogPost(Message message, @Nullable MessageId parent,
Author author) {
super(message, parent, author);
}
}

View File

@@ -0,0 +1,54 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import java.security.GeneralSecurityException;
import javax.annotation.Nullable;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
@NotNullByDefault
public interface BlogPostFactory {
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
String SIGNING_LABEL_COMMENT = CLIENT_ID + "/COMMENT";
BlogPost createBlogPost(GroupId groupId, long timestamp,
@Nullable MessageId parent, LocalAuthor author, String body)
throws FormatException, GeneralSecurityException;
Message createBlogComment(GroupId groupId, LocalAuthor author,
@Nullable String comment, MessageId originalId, MessageId wrappedId)
throws FormatException, GeneralSecurityException;
/**
* Wraps a blog post
*/
Message wrapPost(GroupId groupId, byte[] descriptor, long timestamp,
BdfList body) throws FormatException;
/**
* Re-wraps a previously wrapped post
*/
Message rewrapWrappedPost(GroupId groupId, BdfList body)
throws FormatException;
/**
* Wraps a blog comment
*/
Message wrapComment(GroupId groupId, byte[] descriptor, long timestamp,
BdfList body, MessageId currentId) throws FormatException;
/**
* Re-wraps a previously wrapped comment
*/
Message rewrapWrappedComment(GroupId groupId, BdfList body,
MessageId currentId) throws FormatException;
}

View File

@@ -0,0 +1,48 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
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.PostHeader;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class BlogPostHeader extends PostHeader {
private final MessageType type;
private final GroupId groupId;
private final long timeReceived;
public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
@Nullable MessageId parentId, long timestamp, long timeReceived,
Author author, Status authorStatus, boolean read) {
super(id, parentId, timestamp, author, authorStatus, read);
this.type = type;
this.groupId = groupId;
this.timeReceived = timeReceived;
}
public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
long timestamp, long timeReceived, Author author,
Status authorStatus, boolean read) {
this(type, groupId, id, null, timestamp, timeReceived, author,
authorStatus, read);
}
public MessageType getType() {
return type;
}
public GroupId getGroupId() {
return groupId;
}
public long getTimeReceived() {
return timeReceived;
}
}

View File

@@ -0,0 +1,10 @@
package org.briarproject.briar.api.blog;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.briar.api.sharing.SharingManager;
public interface BlogSharingManager extends SharingManager<Blog> {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.blog.sharing");
}

View File

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

@@ -0,0 +1,34 @@
package org.briarproject.briar.api.blog;
public enum MessageType {
POST(0),
COMMENT(1),
WRAPPED_POST(2),
WRAPPED_COMMENT(3);
int value;
MessageType(int value) {
this.value = value;
}
public static MessageType valueOf(int value) {
switch (value) {
case 0:
return POST;
case 1:
return COMMENT;
case 2:
return WRAPPED_POST;
case 3:
return WRAPPED_COMMENT;
default:
throw new IllegalArgumentException();
}
}
public int getInt() {
return value;
}
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.briar.api.blog.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.sharing.InvitationRequest;
import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class BlogInvitationRequestReceivedEvent extends
InvitationRequestReceivedEvent<Blog> {
public BlogInvitationRequestReceivedEvent(Blog blog, ContactId contactId,
InvitationRequest request) {
super(blog, contactId, request);
}
}

View File

@@ -0,0 +1,20 @@
package org.briarproject.briar.api.blog.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.blog.BlogInvitationResponse;
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class BlogInvitationResponseReceivedEvent
extends InvitationResponseReceivedEvent {
public BlogInvitationResponseReceivedEvent(ContactId contactId,
BlogInvitationResponse response) {
super(contactId, response);
}
}

View File

@@ -0,0 +1,40 @@
package org.briarproject.briar.api.blog.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.blog.BlogPostHeader;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a blog post is added to the database.
*/
@Immutable
@NotNullByDefault
public class BlogPostAddedEvent extends Event {
private final GroupId groupId;
private final BlogPostHeader header;
private final boolean local;
public BlogPostAddedEvent(GroupId groupId, BlogPostHeader header,
boolean local) {
this.groupId = groupId;
this.header = header;
this.local = local;
}
public GroupId getGroupId() {
return groupId;
}
public BlogPostHeader getHeader() {
return header;
}
public boolean isLocal() {
return local;
}
}

View File

@@ -0,0 +1,38 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public abstract class BaseGroup {
private final Group group;
public BaseGroup(Group group) {
this.group = group;
}
public GroupId getId() {
return group.getId();
}
public Group getGroup() {
return group;
}
@Override
public int hashCode() {
return group.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof BaseGroup &&
getGroup().equals(((BaseGroup) o).getGroup());
}
}

View File

@@ -0,0 +1,58 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public abstract class BaseMessageHeader {
private final MessageId id;
private final GroupId groupId;
private final long timestamp;
private final boolean local, read, sent, seen;
public BaseMessageHeader(MessageId id, GroupId groupId, long timestamp,
boolean local, boolean read, boolean sent, boolean seen) {
this.id = id;
this.groupId = groupId;
this.timestamp = timestamp;
this.local = local;
this.read = read;
this.sent = sent;
this.seen = seen;
}
public MessageId getId() {
return id;
}
public GroupId getGroupId() {
return groupId;
}
public long getTimestamp() {
return timestamp;
}
public boolean isLocal() {
return local;
}
public boolean isRead() {
return read;
}
public boolean isSent() {
return sent;
}
public boolean isSeen() {
return seen;
}
}

View File

@@ -0,0 +1,72 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.MessageContext;
@Deprecated
@NotNullByDefault
public interface MessageQueueManager {
/**
* The key used for storing the queue's state in the group metadata.
*/
String QUEUE_STATE_KEY = "queueState";
/**
* Sends a message using the given queue.
*/
QueueMessage sendMessage(Transaction txn, Group queue, long timestamp,
byte[] body, Metadata meta) throws DbException;
/**
* Sets the message validator for the given client.
*/
void registerMessageValidator(ClientId c, QueueMessageValidator v);
/**
* Sets the incoming message hook for the given client. The hook will be
* called once for each incoming message that passes validation. Messages
* are passed to the hook in order.
*/
void registerIncomingMessageHook(ClientId c, IncomingQueueMessageHook hook);
@Deprecated
interface QueueMessageValidator {
/**
* Validates the given message and returns its metadata and
* dependencies.
*/
MessageContext validateMessage(QueueMessage q, Group g)
throws InvalidMessageException;
}
@Deprecated
interface IncomingQueueMessageHook {
/**
* Called once for each incoming message that passes validation.
* Messages are passed to the hook in order.
*
* @throws DbException Should only be used for real database errors.
* If this is thrown, delivery will be attempted again at next startup,
* whereas if an InvalidMessageException is thrown,
* the message will be permanently invalidated.
* @throws InvalidMessageException for any non-database error
* that occurs while handling remotely created data.
* This includes errors that occur while handling locally created data
* in a context controlled by remotely created data
* (for example, parsing the metadata of a dependency
* of an incoming message).
* Never rethrow DbException as InvalidMessageException!
*/
void incomingMessage(Transaction txn, QueueMessage q, Metadata meta)
throws DbException, InvalidMessageException;
}
}

View File

@@ -0,0 +1,70 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
@NotNullByDefault
public interface MessageTracker {
/**
* Gets the number of visible and unread messages in the group
* as well as the timestamp of the latest message
**/
GroupCount getGroupCount(GroupId g) throws DbException;
/**
* Gets the number of visible and unread messages in the group
* as well as the timestamp of the latest message
**/
GroupCount getGroupCount(Transaction txn, GroupId g) throws DbException;
/**
* Updates the group count for the given incoming message.
*/
void trackIncomingMessage(Transaction txn, Message m) throws DbException;
/**
* Updates the group count for the given outgoing message.
*/
void trackOutgoingMessage(Transaction txn, Message m) throws DbException;
/**
* Updates the group count for the given message.
*/
void trackMessage(Transaction txn, GroupId g, long timestamp, boolean read)
throws DbException;
/**
* Marks a message as read or unread and updates the group count.
*/
void setReadFlag(GroupId g, MessageId m, boolean read) throws DbException;
class GroupCount {
private final int msgCount, unreadCount;
private final long latestMsgTime;
public GroupCount(int msgCount, int unreadCount, long latestMsgTime) {
this.msgCount = msgCount;
this.unreadCount = unreadCount;
this.latestMsgTime = latestMsgTime;
}
public int getMsgCount() {
return msgCount;
}
public int getUnreadCount() {
return unreadCount;
}
public long getLatestMsgTime() {
return latestMsgTime;
}
}
}

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import java.util.Collection;
import java.util.Comparator;
import javax.annotation.Nullable;
@NotNullByDefault
public interface MessageTree<T extends MessageTree.MessageNode> {
void add(Collection<T> nodes);
void add(T node);
void setComparator(Comparator<T> comparator);
void clear();
Collection<T> depthFirstOrder();
@NotNullByDefault
interface MessageNode {
MessageId getId();
@Nullable
MessageId getParentId();
void setLevel(int level);
void setDescendantCount(int descendantCount);
long getTimestamp();
}
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public abstract class NamedGroup extends BaseGroup {
private final String name;
private final byte[] salt;
public NamedGroup(Group group, String name, byte[] salt) {
super(group);
this.name = name;
this.salt = salt;
}
public String getName() {
return name;
}
public byte[] getSalt() {
return salt;
}
@Override
public boolean equals(Object o) {
return o instanceof NamedGroup && super.equals(o);
}
}

View File

@@ -0,0 +1,57 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public abstract class PostHeader {
private final MessageId id;
@Nullable
private final MessageId parentId;
private final long timestamp;
private final Author author;
private final Status authorStatus;
private final boolean read;
public PostHeader(MessageId id, @Nullable MessageId parentId,
long timestamp, Author author, Status authorStatus, boolean read) {
this.id = id;
this.parentId = parentId;
this.timestamp = timestamp;
this.author = author;
this.authorStatus = authorStatus;
this.read = read;
}
public MessageId getId() {
return id;
}
public Author getAuthor() {
return author;
}
public Status getAuthorStatus() {
return authorStatus;
}
public long getTimestamp() {
return timestamp;
}
public boolean isRead() {
return read;
}
@Nullable
public MessageId getParentId() {
return parentId;
}
}

View File

@@ -0,0 +1,50 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.List;
@Deprecated
@NotNullByDefault
public interface ProtocolEngine<A, S, M> {
StateUpdate<S, M> onLocalAction(S localState, A action);
StateUpdate<S, M> onMessageReceived(S localState, M received);
StateUpdate<S, M> onMessageDelivered(S localState, M delivered);
class StateUpdate<S, M> {
public final boolean deleteMessage;
public final boolean deleteState;
public final S localState;
public final List<M> toSend;
public final List<Event> toBroadcast;
/**
* This class represents an update of the local protocol state.
* It only shows how the state should be updated,
* but does not carry out the updates on its own.
*
* @param deleteMessage whether to delete the message that triggered
* the state update. This will be ignored for
* {@link ProtocolEngine#onLocalAction}.
* @param deleteState whether to delete the localState {@link S}
* @param localState the new local state
* @param toSend a list of messages to be sent as part of the
* state update
* @param toBroadcast a list of events to broadcast as result of the
* state update
*/
public StateUpdate(boolean deleteMessage, boolean deleteState,
S localState, List<M> toSend, List<Event> toBroadcast) {
this.deleteMessage = deleteMessage;
this.deleteState = deleteState;
this.localState = localState;
this.toSend = toSend;
this.toBroadcast = toBroadcast;
}
}
}

View File

@@ -0,0 +1,12 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.db.DbException;
/**
* Thrown when a database operation is attempted as part of a protocol session
* and the operation is not applicable to the current protocol state. This
* exception may occur due to concurrent updates and does not indicate a
* database error.
*/
public class ProtocolStateException extends DbException {
}

View File

@@ -0,0 +1,31 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
@Deprecated
@NotNullByDefault
public class QueueMessage extends Message {
public static final int QUEUE_MESSAGE_HEADER_LENGTH =
MESSAGE_HEADER_LENGTH + 8;
public static final int MAX_QUEUE_MESSAGE_BODY_LENGTH =
MAX_MESSAGE_BODY_LENGTH - 8;
private final long queuePosition;
public QueueMessage(MessageId id, GroupId groupId, long timestamp,
long queuePosition, byte[] raw) {
super(id, groupId, timestamp, raw);
this.queuePosition = queuePosition;
}
public long getQueuePosition() {
return queuePosition;
}
}

View File

@@ -0,0 +1,15 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
@Deprecated
@NotNullByDefault
public interface QueueMessageFactory {
QueueMessage createMessage(GroupId groupId, long timestamp,
long queuePosition, byte[] body);
QueueMessage createMessage(MessageId id, byte[] raw);
}

View File

@@ -0,0 +1,24 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.ThreadSafe;
/**
* Type-safe wrapper for a byte array that uniquely identifies a protocol
* session.
*/
@ThreadSafe
@NotNullByDefault
public class SessionId extends UniqueId {
public SessionId(byte[] id) {
super(id);
}
@Override
public boolean equals(Object o) {
return o instanceof SessionId && super.equals(o);
}
}

View File

@@ -0,0 +1,36 @@
package org.briarproject.briar.api.client;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.messaging.PrivateMessage;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public abstract class ThreadedMessage extends PrivateMessage {
@Nullable
private final MessageId parent;
private final Author author;
public ThreadedMessage(Message message, @Nullable MessageId parent,
Author author) {
super(message);
this.parent = parent;
this.author = author;
}
@Nullable
public MessageId getParent() {
return parent;
}
public Author getAuthor() {
return author;
}
}

View File

@@ -0,0 +1,137 @@
package org.briarproject.briar.api.feed;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_BLOG_GROUP_ID;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_ADDED;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_AUTHOR;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_DESC;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_LAST_ENTRY;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_TITLE;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_UPDATED;
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_URL;
@Immutable
@NotNullByDefault
public class Feed {
private final String url;
private final GroupId blogId;
@Nullable
private final String title, description, author;
private final long added, updated, lastEntryTime;
public Feed(String url, GroupId blogId, @Nullable String title,
@Nullable String description, @Nullable String author,
long added, long updated, long lastEntryTime) {
this.url = url;
this.blogId = blogId;
this.title = title;
this.description = description;
this.author = author;
this.added = added;
this.updated = updated;
this.lastEntryTime = lastEntryTime;
}
public Feed(String url, GroupId blogId, @Nullable String title,
@Nullable String description, @Nullable String author, long added) {
this(url, blogId, title, description, author, added, 0L, 0L);
}
public Feed(String url, GroupId blogId, long added) {
this(url, blogId, null, null, null, added, 0L, 0L);
}
public String getUrl() {
return url;
}
public GroupId getBlogId() {
return blogId;
}
public BdfDictionary toBdfDictionary() {
BdfDictionary d = BdfDictionary.of(
new BdfEntry(KEY_FEED_URL, url),
new BdfEntry(KEY_BLOG_GROUP_ID, blogId.getBytes()),
new BdfEntry(KEY_FEED_ADDED, added),
new BdfEntry(KEY_FEED_UPDATED, updated),
new BdfEntry(KEY_FEED_LAST_ENTRY, lastEntryTime)
);
if (title != null) d.put(KEY_FEED_TITLE, title);
if (description != null) d.put(KEY_FEED_DESC, description);
if (author != null) d.put(KEY_FEED_AUTHOR, author);
return d;
}
public static Feed from(BdfDictionary d) throws FormatException {
String url = d.getString(KEY_FEED_URL);
GroupId blogId = new GroupId(d.getRaw(KEY_BLOG_GROUP_ID));
String title = d.getOptionalString(KEY_FEED_TITLE);
String desc = d.getOptionalString(KEY_FEED_DESC);
String author = d.getOptionalString(KEY_FEED_AUTHOR);
long added = d.getLong(KEY_FEED_ADDED, 0L);
long updated = d.getLong(KEY_FEED_UPDATED, 0L);
long lastEntryTime = d.getLong(KEY_FEED_LAST_ENTRY, 0L);
return new Feed(url, blogId, title, desc, author, added, updated,
lastEntryTime);
}
@Nullable
public String getTitle() {
return title;
}
@Nullable
public String getDescription() {
return description;
}
@Nullable
public String getAuthor() {
return author;
}
public long getAdded() {
return added;
}
public long getUpdated() {
return updated;
}
public long getLastEntryTime() {
return lastEntryTime;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof Feed) {
Feed f = (Feed) o;
return url.equals(f.url) && blogId.equals(f.getBlogId()) &&
equalsWithNull(title, f.getTitle()) &&
equalsWithNull(description, f.getDescription()) &&
equalsWithNull(author, f.getAuthor()) &&
added == f.getAdded() &&
updated == f.getUpdated() &&
lastEntryTime == f.getLastEntryTime();
}
return false;
}
private boolean equalsWithNull(@Nullable Object a, @Nullable Object b) {
if (a == b) return true;
if (a == null || b == null) return false;
return a.equals(b);
}
}

View File

@@ -0,0 +1,29 @@
package org.briarproject.briar.api.feed;
import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.MINUTES;
public interface FeedConstants {
/* delay after start before fetching feed */
int FETCH_DELAY_INITIAL = 1;
/* the interval the feed should be fetched */
int FETCH_INTERVAL = 30;
/* the unit that applies to the fetch times */
TimeUnit FETCH_UNIT = MINUTES;
// group metadata keys
String KEY_FEEDS = "feeds";
String KEY_FEED_URL = "feedURL";
String KEY_BLOG_GROUP_ID = "blogGroupId";
String KEY_FEED_TITLE = "feedTitle";
String KEY_FEED_DESC = "feedDesc";
String KEY_FEED_AUTHOR = "feedAuthor";
String KEY_FEED_ADDED = "feedAdded";
String KEY_FEED_UPDATED = "feedUpdated";
String KEY_FEED_LAST_ENTRY = "feedLastEntryTime";
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.briar.api.feed;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.GroupId;
import java.io.IOException;
import java.util.List;
@NotNullByDefault
public interface FeedManager {
/**
* The unique ID of the RSS feed client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed");
/**
* Adds an RSS feed.
*/
void addFeed(String url, GroupId g) throws DbException, IOException;
/**
* Removes an RSS feed.
*/
void removeFeed(String url) throws DbException;
/**
* Returns a list of all added RSS feeds
*/
List<Feed> getFeeds() throws DbException;
}

View File

@@ -0,0 +1,23 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.briar.api.client.NamedGroup;
import org.briarproject.briar.api.sharing.Shareable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class Forum extends NamedGroup implements Shareable {
public Forum(Group group, String name, byte[] salt) {
super(group, name, salt);
}
@Override
public boolean equals(Object o) {
return o instanceof Forum && super.equals(o);
}
}

View File

@@ -0,0 +1,40 @@
package org.briarproject.briar.api.forum;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
public interface ForumConstants {
/**
* The maximum length of a forum's name in UTF-8 bytes.
*/
int MAX_FORUM_NAME_LENGTH = 100;
/**
* The length of a forum's random salt in bytes.
*/
int FORUM_SALT_LENGTH = 32;
/**
* The maximum length of a forum post's content type in UTF-8 bytes.
*/
int MAX_CONTENT_TYPE_LENGTH = 50;
/**
* The maximum length of a forum post's body in bytes.
*/
int MAX_FORUM_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
/* Forum Sharing Constants */
String FORUM_NAME = "forumName";
String FORUM_SALT = "forumSalt";
// Database keys
String KEY_TIMESTAMP = "timestamp";
String KEY_PARENT = "parent";
String KEY_ID = "id";
String KEY_NAME = "name";
String KEY_PUBLIC_NAME = "publicKey";
String KEY_AUTHOR = "author";
String KEY_LOCAL = "local";
}

View File

@@ -0,0 +1,18 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface ForumFactory {
/**
* Creates a forum with the given name.
*/
Forum createForum(String name);
/**
* Creates a forum with the given name and salt.
*/
Forum createForum(String name, byte[] salt);
}

View File

@@ -0,0 +1,33 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.contact.ContactId;
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.InvitationRequest;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ForumInvitationRequest extends InvitationRequest {
private final String forumName;
public ForumInvitationRequest(MessageId id, SessionId sessionId,
GroupId groupId, ContactId contactId, String forumName,
@Nullable String message, boolean available, long time,
boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, groupId, contactId, message, available, time,
local, sent, seen, read);
this.forumName = forumName;
}
public String getForumName() {
return forumName;
}
}

View File

@@ -0,0 +1,24 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.contact.ContactId;
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.InvitationResponse;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ForumInvitationResponse extends InvitationResponse {
public ForumInvitationResponse(MessageId id, SessionId sessionId,
GroupId groupId, ContactId contactId, boolean accept, long time,
boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, groupId, contactId, accept, time, local, sent,
seen, read);
}
}

View File

@@ -0,0 +1,90 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import java.util.Collection;
import javax.annotation.Nullable;
@NotNullByDefault
public interface ForumManager {
/**
* The unique ID of the forum client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum");
/**
* Subscribes to a forum.
*/
Forum addForum(String name) throws DbException;
/**
* Unsubscribes from a forum.
*/
void removeForum(Forum f) throws DbException;
/**
* Creates a local forum post.
*/
@CryptoExecutor
ForumPost createLocalPost(GroupId groupId, String body, long timestamp,
@Nullable MessageId parentId, LocalAuthor author);
/**
* Stores a local forum post.
*/
ForumPostHeader addLocalPost(ForumPost p) throws DbException;
/**
* Returns the forum with the given ID.
*/
Forum getForum(GroupId g) throws DbException;
/**
* Returns the forum with the given ID.
*/
Forum getForum(Transaction txn, GroupId g) throws DbException;
/**
* Returns all forums to which the user subscribes.
*/
Collection<Forum> getForums() throws DbException;
/**
* Returns the body of the forum post with the given ID.
*/
String getPostBody(MessageId m) throws DbException;
/**
* Returns the headers of all posts in the given forum.
*/
Collection<ForumPostHeader> getPostHeaders(GroupId g) throws DbException;
/**
* Registers a hook to be called whenever a forum is removed.
*/
void registerRemoveForumHook(RemoveForumHook hook);
/**
* Returns the group count for the given forum.
*/
GroupCount getGroupCount(GroupId g) throws DbException;
/**
* Marks a message as read or unread and updates the group count.
*/
void setReadFlag(GroupId g, MessageId m, boolean read) throws DbException;
interface RemoveForumHook {
void removingForum(Transaction txn, Forum f) throws DbException;
}
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.ThreadedMessage;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ForumPost extends ThreadedMessage {
public ForumPost(Message message, @Nullable MessageId parent,
Author author) {
super(message, parent, author);
}
}

View File

@@ -0,0 +1,26 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import java.security.GeneralSecurityException;
import javax.annotation.Nullable;
import static org.briarproject.briar.api.forum.ForumManager.CLIENT_ID;
@NotNullByDefault
public interface ForumPostFactory {
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
@CryptoExecutor
ForumPost createPost(GroupId groupId, long timestamp,
@Nullable MessageId parent, LocalAuthor author, String body)
throws FormatException, GeneralSecurityException;
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.PostHeader;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ForumPostHeader extends PostHeader {
public ForumPostHeader(MessageId id, @Nullable MessageId parentId,
long timestamp, Author author, Author.Status authorStatus,
boolean read) {
super(id, parentId, timestamp, author, authorStatus, read);
}
}

View File

@@ -0,0 +1,10 @@
package org.briarproject.briar.api.forum;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.briar.api.sharing.SharingManager;
public interface ForumSharingManager extends SharingManager<Forum> {
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.forum.sharing");
}

View File

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

@@ -0,0 +1,21 @@
package org.briarproject.briar.api.forum.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumInvitationRequest;
import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ForumInvitationRequestReceivedEvent extends
InvitationRequestReceivedEvent<Forum> {
public ForumInvitationRequestReceivedEvent(Forum forum, ContactId contactId,
ForumInvitationRequest request) {
super(forum, contactId, request);
}
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.briar.api.forum.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
public class ForumInvitationResponseReceivedEvent extends
InvitationResponseReceivedEvent {
private final String forumName;
public ForumInvitationResponseReceivedEvent(String forumName,
ContactId contactId, ForumInvitationResponse response) {
super(contactId, response);
this.forumName = forumName;
}
public String getForumName() {
return forumName;
}
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.briar.api.forum.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.forum.ForumPostHeader;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a new forum post is received.
*/
@Immutable
@NotNullByDefault
public class ForumPostReceivedEvent extends Event {
private final ForumPostHeader forumPostHeader;
private final GroupId groupId;
public ForumPostReceivedEvent(ForumPostHeader forumPostHeader,
GroupId groupId) {
this.forumPostHeader = forumPostHeader;
this.groupId = groupId;
}
public ForumPostHeader getForumPostHeader() {
return forumPostHeader;
}
public GroupId getGroupId() {
return groupId;
}
}

View File

@@ -0,0 +1,53 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ABORT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE;
@NotNullByDefault
public enum IntroduceeAction {
LOCAL_ACCEPT,
LOCAL_DECLINE,
LOCAL_ABORT,
REMOTE_REQUEST,
REMOTE_ACCEPT,
REMOTE_DECLINE,
REMOTE_ABORT,
ACK;
@Nullable
public static IntroduceeAction getRemote(int type, boolean accept) {
if (type == TYPE_REQUEST) return REMOTE_REQUEST;
if (type == TYPE_RESPONSE && accept) return REMOTE_ACCEPT;
if (type == TYPE_RESPONSE) return REMOTE_DECLINE;
if (type == TYPE_ACK) return ACK;
if (type == TYPE_ABORT) return REMOTE_ABORT;
return null;
}
@Nullable
public static IntroduceeAction getRemote(int type) {
return getRemote(type, true);
}
@Nullable
public static IntroduceeAction getLocal(int type, boolean accept) {
if (type == TYPE_RESPONSE && accept) return LOCAL_ACCEPT;
if (type == TYPE_RESPONSE) return LOCAL_DECLINE;
if (type == TYPE_ACK) return ACK;
if (type == TYPE_ABORT) return LOCAL_ABORT;
return null;
}
@Nullable
public static IntroduceeAction getLocal(int type) {
return getLocal(type, true);
}
}

View File

@@ -0,0 +1,82 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.introduction.IntroduceeAction.ACK;
import static org.briarproject.briar.api.introduction.IntroduceeAction.LOCAL_ACCEPT;
import static org.briarproject.briar.api.introduction.IntroduceeAction.LOCAL_DECLINE;
import static org.briarproject.briar.api.introduction.IntroduceeAction.REMOTE_ACCEPT;
import static org.briarproject.briar.api.introduction.IntroduceeAction.REMOTE_DECLINE;
import static org.briarproject.briar.api.introduction.IntroduceeAction.REMOTE_REQUEST;
@Immutable
@NotNullByDefault
public enum IntroduceeProtocolState {
ERROR(0),
AWAIT_REQUEST(1) {
@Override
public IntroduceeProtocolState next(IntroduceeAction a) {
if (a == REMOTE_REQUEST) return AWAIT_RESPONSES;
return ERROR;
}
},
AWAIT_RESPONSES(2) {
@Override
public IntroduceeProtocolState next(IntroduceeAction a) {
if (a == REMOTE_ACCEPT) return AWAIT_LOCAL_RESPONSE;
if (a == REMOTE_DECLINE) return FINISHED;
if (a == LOCAL_ACCEPT) return AWAIT_REMOTE_RESPONSE;
if (a == LOCAL_DECLINE) return FINISHED;
return ERROR;
}
},
AWAIT_REMOTE_RESPONSE(3) {
@Override
public IntroduceeProtocolState next(IntroduceeAction a) {
if (a == REMOTE_ACCEPT) return AWAIT_ACK;
if (a == REMOTE_DECLINE) return FINISHED;
return ERROR;
}
},
AWAIT_LOCAL_RESPONSE(4) {
@Override
public IntroduceeProtocolState next(IntroduceeAction a) {
if (a == LOCAL_ACCEPT) return AWAIT_ACK;
if (a == LOCAL_DECLINE) return FINISHED;
return ERROR;
}
},
AWAIT_ACK(5) {
@Override
public IntroduceeProtocolState next(IntroduceeAction a) {
if (a == ACK) return FINISHED;
return ERROR;
}
},
FINISHED(6);
private final int value;
IntroduceeProtocolState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static IntroduceeProtocolState fromValue(int value) {
for (IntroduceeProtocolState s : values()) {
if (s.value == value) return s;
}
throw new IllegalArgumentException();
}
public IntroduceeProtocolState next(IntroduceeAction a) {
return this;
}
}

View File

@@ -0,0 +1,54 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ABORT;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_ACK;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_REQUEST;
import static org.briarproject.briar.api.introduction.IntroductionConstants.TYPE_RESPONSE;
@NotNullByDefault
public enum IntroducerAction {
LOCAL_REQUEST,
LOCAL_ABORT,
REMOTE_ACCEPT_1,
REMOTE_ACCEPT_2,
REMOTE_DECLINE_1,
REMOTE_DECLINE_2,
REMOTE_ABORT,
ACK_1,
ACK_2;
@Nullable
public static IntroducerAction getLocal(int type) {
if (type == TYPE_REQUEST) return LOCAL_REQUEST;
if (type == TYPE_ABORT) return LOCAL_ABORT;
return null;
}
@Nullable
public static IntroducerAction getRemote(int type, boolean one,
boolean accept) {
if (one) {
if (type == TYPE_RESPONSE && accept) return REMOTE_ACCEPT_1;
if (type == TYPE_RESPONSE) return REMOTE_DECLINE_1;
if (type == TYPE_ACK) return ACK_1;
} else {
if (type == TYPE_RESPONSE && accept) return REMOTE_ACCEPT_2;
if (type == TYPE_RESPONSE) return REMOTE_DECLINE_2;
if (type == TYPE_ACK) return ACK_2;
}
if (type == TYPE_ABORT) return REMOTE_ABORT;
return null;
}
@Nullable
public static IntroducerAction getRemote(int type, boolean one) {
return getRemote(type, one, true);
}
}

View File

@@ -0,0 +1,108 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.introduction.IntroducerAction.ACK_1;
import static org.briarproject.briar.api.introduction.IntroducerAction.ACK_2;
import static org.briarproject.briar.api.introduction.IntroducerAction.LOCAL_REQUEST;
import static org.briarproject.briar.api.introduction.IntroducerAction.REMOTE_ABORT;
import static org.briarproject.briar.api.introduction.IntroducerAction.REMOTE_ACCEPT_1;
import static org.briarproject.briar.api.introduction.IntroducerAction.REMOTE_ACCEPT_2;
import static org.briarproject.briar.api.introduction.IntroducerAction.REMOTE_DECLINE_1;
import static org.briarproject.briar.api.introduction.IntroducerAction.REMOTE_DECLINE_2;
@Immutable
@NotNullByDefault
public enum IntroducerProtocolState {
ERROR(0),
PREPARE_REQUESTS(1) {
@Override
public IntroducerProtocolState next(IntroducerAction a) {
if (a == LOCAL_REQUEST) return AWAIT_RESPONSES;
return ERROR;
}
},
AWAIT_RESPONSES(2) {
@Override
public IntroducerProtocolState next(IntroducerAction a) {
if (a == REMOTE_ACCEPT_1) return AWAIT_RESPONSE_2;
if (a == REMOTE_ACCEPT_2) return AWAIT_RESPONSE_1;
if (a == REMOTE_DECLINE_1) return FINISHED;
if (a == REMOTE_DECLINE_2) return FINISHED;
return ERROR;
}
},
AWAIT_RESPONSE_1(3) {
@Override
public IntroducerProtocolState next(IntroducerAction a) {
if (a == REMOTE_ACCEPT_1) return AWAIT_ACKS;
if (a == REMOTE_DECLINE_1) return FINISHED;
return ERROR;
}
},
AWAIT_RESPONSE_2(4) {
@Override
public IntroducerProtocolState next(IntroducerAction a) {
if (a == REMOTE_ACCEPT_2) return AWAIT_ACKS;
if (a == REMOTE_DECLINE_2) return FINISHED;
return ERROR;
}
},
AWAIT_ACKS(5) {
@Override
public IntroducerProtocolState next(IntroducerAction a) {
if (a == ACK_1) return AWAIT_ACK_2;
if (a == ACK_2) return AWAIT_ACK_1;
return ERROR;
}
},
AWAIT_ACK_1(6) {
@Override
public IntroducerProtocolState next(IntroducerAction a) {
if (a == ACK_1) return FINISHED;
return ERROR;
}
},
AWAIT_ACK_2(7) {
@Override
public IntroducerProtocolState next(IntroducerAction a) {
if (a == ACK_2) return FINISHED;
return ERROR;
}
},
FINISHED(8) {
@Override
public IntroducerProtocolState next(IntroducerAction a) {
if (a == REMOTE_ABORT) return ERROR;
return FINISHED;
}
};
private final int value;
IntroducerProtocolState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static IntroducerProtocolState fromValue(int value) {
for (IntroducerProtocolState s : values()) {
if (s.value == value) return s;
}
throw new IllegalArgumentException();
}
public static boolean isOngoing(IntroducerProtocolState state) {
return state != FINISHED && state != ERROR;
}
public IntroducerProtocolState next(IntroducerAction a) {
return this;
}
}

View File

@@ -0,0 +1,89 @@
package org.briarproject.briar.api.introduction;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
public interface IntroductionConstants {
/* Protocol roles */
int ROLE_INTRODUCER = 0;
int ROLE_INTRODUCEE = 1;
/* Message types */
int TYPE_REQUEST = 1;
int TYPE_RESPONSE = 2;
int TYPE_ACK = 3;
int TYPE_ABORT = 4;
/* Message Constants */
String TYPE = "type";
String GROUP_ID = "groupId";
String SESSION_ID = "sessionId";
String CONTACT = "contactId";
String NAME = "name";
String PUBLIC_KEY = "publicKey";
String E_PUBLIC_KEY = "ephemeralPublicKey";
String MSG = "msg";
String ACCEPT = "accept";
String TIME = "time";
String TRANSPORT = "transport";
String MESSAGE_ID = "messageId";
String MESSAGE_TIME = "timestamp";
String MAC = "mac";
String SIGNATURE = "signature";
/* Validation Constants */
/**
* The length of the message authentication code in bytes.
*/
int MAC_LENGTH = 32;
/**
* The maximum length of the introducer's optional message to the
* introducees in UTF-8 bytes.
*/
int MAX_INTRODUCTION_MESSAGE_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
/* Introducer Local State Metadata */
String STATE = "state";
String ROLE = "role";
String GROUP_ID_1 = "groupId1";
String GROUP_ID_2 = "groupId2";
String CONTACT_1 = "contact1";
String CONTACT_2 = "contact2";
String AUTHOR_ID_1 = "authorId1";
String AUTHOR_ID_2 = "authorId2";
String CONTACT_ID_1 = "contactId1";
String CONTACT_ID_2 = "contactId2";
String RESPONSE_1 = "response1";
String RESPONSE_2 = "response2";
/* Introduction Request Action */
String PUBLIC_KEY1 = "publicKey1";
String PUBLIC_KEY2 = "publicKey2";
/* Introducee Local State Metadata (without those already defined) */
String STORAGE_ID = "storageId";
String INTRODUCER = "introducer";
String LOCAL_AUTHOR_ID = "localAuthorId";
String REMOTE_AUTHOR_ID = "remoteAuthorId";
String OUR_PUBLIC_KEY = "ourEphemeralPublicKey";
String OUR_PRIVATE_KEY = "ourEphemeralPrivateKey";
String OUR_TIME = "ourTime";
String ADDED_CONTACT_ID = "addedContactId";
String NOT_OUR_RESPONSE = "notOurResponse";
String EXISTS = "contactExists";
String REMOTE_AUTHOR_IS_US = "remoteAuthorIsUs";
String ANSWERED = "answered";
String NONCE = "nonce";
String MAC_KEY = "macKey";
String OUR_TRANSPORT = "ourTransport";
String OUR_MAC = "ourMac";
String OUR_SIGNATURE = "ourSignature";
String TASK = "task";
int TASK_ADD_CONTACT = 0;
int TASK_ACTIVATE_CONTACT = 1;
int TASK_ABORT = 2;
}

View File

@@ -0,0 +1,50 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.messaging.ConversationManager.ConversationClient;
import java.util.Collection;
import javax.annotation.Nullable;
@NotNullByDefault
public interface IntroductionManager extends ConversationClient {
/**
* The unique ID of the introduction client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.introduction");
/**
* Sends two initial introduction messages.
*/
void makeIntroduction(Contact c1, Contact c2, @Nullable String msg,
final long timestamp) throws DbException, FormatException;
/**
* Accepts an introduction.
*/
void acceptIntroduction(final ContactId contactId,
final SessionId sessionId, final long timestamp)
throws DbException, FormatException;
/**
* Declines an introduction.
*/
void declineIntroduction(final ContactId contactId,
final SessionId sessionId, final long timestamp)
throws DbException, FormatException;
/**
* Returns all introduction messages for the given contact.
*/
Collection<IntroductionMessage> getIntroductionMessages(ContactId contactId)
throws DbException;
}

View File

@@ -0,0 +1,43 @@
package org.briarproject.briar.api.introduction;
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.BaseMessageHeader;
import org.briarproject.briar.api.client.SessionId;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.briar.api.introduction.IntroductionConstants.ROLE_INTRODUCER;
@Immutable
@NotNullByDefault
public class IntroductionMessage extends BaseMessageHeader {
private final SessionId sessionId;
private final MessageId messageId;
private final int role;
IntroductionMessage(SessionId sessionId, MessageId messageId,
GroupId groupId, int role, long time, boolean local, boolean sent,
boolean seen, boolean read) {
super(messageId, groupId, time, local, read, sent, seen);
this.sessionId = sessionId;
this.messageId = messageId;
this.role = role;
}
public SessionId getSessionId() {
return sessionId;
}
public MessageId getMessageId() {
return messageId;
}
public boolean isIntroducer() {
return role == ROLE_INTRODUCER;
}
}

View File

@@ -0,0 +1,51 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.identity.AuthorId;
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;
@Immutable
@NotNullByDefault
public class IntroductionRequest extends IntroductionResponse {
@Nullable
private final String message;
private final boolean answered, exists, introducesOtherIdentity;
public IntroductionRequest(SessionId sessionId, MessageId messageId,
GroupId groupId, int role, long time, boolean local, boolean sent,
boolean seen, boolean read, AuthorId authorId, String name,
boolean accepted, @Nullable String message, boolean answered,
boolean exists, boolean introducesOtherIdentity) {
super(sessionId, messageId, groupId, role, time, local, sent, seen,
read, authorId, name, accepted);
this.message = message;
this.answered = answered;
this.exists = exists;
this.introducesOtherIdentity = introducesOtherIdentity;
}
@Nullable
public String getMessage() {
return message;
}
public boolean wasAnswered() {
return answered;
}
public boolean contactExists() {
return exists;
}
public boolean doesIntroduceOtherIdentity() {
return introducesOtherIdentity;
}
}

View File

@@ -0,0 +1,43 @@
package org.briarproject.briar.api.introduction;
import org.briarproject.bramble.api.identity.AuthorId;
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.Immutable;
@Immutable
@NotNullByDefault
public class IntroductionResponse extends IntroductionMessage {
private final AuthorId remoteAuthorId;
private final String name;
private final boolean accepted;
public IntroductionResponse(SessionId sessionId, MessageId messageId,
GroupId groupId, int role, long time, boolean local, boolean sent,
boolean seen, boolean read, AuthorId remoteAuthorId, String name,
boolean accepted) {
super(sessionId, messageId, groupId, role, time, local, sent, seen,
read);
this.remoteAuthorId = remoteAuthorId;
this.name = name;
this.accepted = accepted;
}
public String getName() {
return name;
}
public boolean wasAccepted() {
return accepted;
}
public AuthorId getRemoteAuthorId() {
return remoteAuthorId;
}
}

View File

@@ -0,0 +1,29 @@
package org.briarproject.briar.api.introduction.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.client.SessionId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class IntroductionAbortedEvent extends Event {
private final ContactId contactId;
private final SessionId sessionId;
public IntroductionAbortedEvent(ContactId contactId, SessionId sessionId) {
this.contactId = contactId;
this.sessionId = sessionId;
}
public ContactId getContactId() {
return contactId;
}
public SessionId getSessionId() {
return sessionId;
}
}

View File

@@ -0,0 +1,32 @@
package org.briarproject.briar.api.introduction.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.introduction.IntroductionRequest;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class IntroductionRequestReceivedEvent extends Event {
private final ContactId contactId;
private final IntroductionRequest introductionRequest;
public IntroductionRequestReceivedEvent(ContactId contactId,
IntroductionRequest introductionRequest) {
this.contactId = contactId;
this.introductionRequest = introductionRequest;
}
public ContactId getContactId() {
return contactId;
}
public IntroductionRequest getIntroductionRequest() {
return introductionRequest;
}
}

View File

@@ -0,0 +1,31 @@
package org.briarproject.briar.api.introduction.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.introduction.IntroductionResponse;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class IntroductionResponseReceivedEvent extends Event {
private final ContactId contactId;
private final IntroductionResponse introductionResponse;
public IntroductionResponseReceivedEvent(ContactId contactId,
IntroductionResponse introductionResponse) {
this.contactId = contactId;
this.introductionResponse = introductionResponse;
}
public ContactId getContactId() {
return contactId;
}
public IntroductionResponse getIntroductionResponse() {
return introductionResponse;
}
}

View File

@@ -0,0 +1,22 @@
package org.briarproject.briar.api.introduction.event;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class IntroductionSucceededEvent extends Event {
private final Contact contact;
public IntroductionSucceededEvent(Contact contact) {
this.contact = contact;
}
public Contact getContact() {
return contact;
}
}

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.api.messaging;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
@NotNullByDefault
public interface ConversationManager {
/**
* Clients that present messages in a private conversation need to
* register themselves here.
*/
void registerConversationClient(ConversationClient client);
/**
* Returns the unified group count for all private conversation messages.
*/
GroupCount getGroupCount(ContactId c) throws DbException;
@NotNullByDefault
interface ConversationClient {
Group getContactGroup(Contact c);
GroupCount getGroupCount(Transaction txn, ContactId c)
throws DbException;
void setReadFlag(GroupId g, MessageId m, boolean read)
throws DbException;
}
}

View File

@@ -0,0 +1,11 @@
package org.briarproject.briar.api.messaging;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
public interface MessagingConstants {
/**
* The maximum length of a private message's body in bytes.
*/
int MAX_PRIVATE_MESSAGE_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
}

View File

@@ -0,0 +1,47 @@
package org.briarproject.briar.api.messaging;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.messaging.ConversationManager.ConversationClient;
import java.util.Collection;
@NotNullByDefault
public interface MessagingManager extends ConversationClient {
/**
* The unique ID of the messaging client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.messaging");
/**
* Stores a local private message.
*/
void addLocalMessage(PrivateMessage m) throws DbException;
/**
* Returns the ID of the contact with the given private conversation.
*/
ContactId getContactId(GroupId g) throws DbException;
/**
* Returns the ID of the private conversation with the given contact.
*/
GroupId getConversationId(ContactId c) throws DbException;
/**
* Returns the headers of all messages in the given private conversation.
*/
Collection<PrivateMessageHeader> getMessageHeaders(ContactId c)
throws DbException;
/**
* Returns the body of the private message with the given ID.
*/
String getMessageBody(MessageId m) throws DbException;
}

View File

@@ -0,0 +1,22 @@
package org.briarproject.briar.api.messaging;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class PrivateMessage {
private final Message message;
public PrivateMessage(Message message) {
this.message = message;
}
public Message getMessage() {
return message;
}
}

View File

@@ -0,0 +1,13 @@
package org.briarproject.briar.api.messaging;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
@NotNullByDefault
public interface PrivateMessageFactory {
PrivateMessage createPrivateMessage(GroupId groupId, long timestamp,
String body) throws FormatException;
}

View File

@@ -0,0 +1,20 @@
package org.briarproject.briar.api.messaging;
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.BaseMessageHeader;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class PrivateMessageHeader extends BaseMessageHeader {
public PrivateMessageHeader(MessageId id, GroupId groupId, long timestamp,
boolean local, boolean read, boolean sent, boolean seen) {
super(id, groupId, timestamp, local, read, sent, seen);
}
}

View File

@@ -0,0 +1,40 @@
package org.briarproject.briar.api.messaging.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a new private message is received.
*/
@Immutable
@NotNullByDefault
public class PrivateMessageReceivedEvent extends Event {
private final PrivateMessageHeader messageHeader;
private final ContactId contactId;
private final GroupId groupId;
public PrivateMessageReceivedEvent(PrivateMessageHeader messageHeader,
ContactId contactId, GroupId groupId) {
this.messageHeader = messageHeader;
this.contactId = contactId;
this.groupId = groupId;
}
public PrivateMessageHeader getMessageHeader() {
return messageHeader;
}
public ContactId getContactId() {
return contactId;
}
public GroupId getGroupId() {
return groupId;
}
}

View File

@@ -0,0 +1,35 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class GroupMember {
private final Author author;
private final Status status;
private final Visibility visibility;
public GroupMember(Author author, Status status, Visibility visibility) {
this.author = author;
this.status = status;
this.visibility = visibility;
}
public Author getAuthor() {
return author;
}
public Status getStatus() {
return status;
}
public Visibility getVisibility() {
return visibility;
}
}

View File

@@ -0,0 +1,25 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.ThreadedMessage;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class GroupMessage extends ThreadedMessage {
public GroupMessage(Message message, @Nullable MessageId parent,
Author member) {
super(message, parent, member);
}
public Author getMember() {
return super.getAuthor();
}
}

View File

@@ -0,0 +1,63 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import javax.annotation.Nullable;
import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT_ID;
@NotNullByDefault
public interface GroupMessageFactory {
String SIGNING_LABEL_JOIN = CLIENT_ID + "/JOIN";
String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
/**
* Creates a join announcement message for the creator of a group.
*
* @param groupId The ID of the private group that is being joined
* @param timestamp The timestamp to be used in the join announcement
* @param creator The creator's identity
*/
@CryptoExecutor
GroupMessage createJoinMessage(GroupId groupId, long timestamp,
LocalAuthor creator);
/**
* Creates a join announcement message for a joining member.
*
* @param groupId The ID of the private group that is being joined
* @param timestamp The timestamp to be used in the join announcement,
* which must be greater than the timestamp of the invitation message
* @param member The member's identity
* @param inviteTimestamp The timestamp of the invitation message
* @param creatorSignature The creator's signature from the invitation
* message
*/
@CryptoExecutor
GroupMessage createJoinMessage(GroupId groupId, long timestamp,
LocalAuthor member, long inviteTimestamp, byte[] creatorSignature);
/**
* Creates a private group post.
*
* @param groupId The ID of the private group
* @param timestamp Must be greater than the timestamps of the parent
* post, if any, and the member's previous message
* @param parentId The ID of the parent post, or null if the post has no
* parent
* @param author The author of the post
* @param body The content of the post
* @param previousMsgId The ID of the author's previous message
* in this group
*/
@CryptoExecutor
GroupMessage createGroupMessage(GroupId groupId, long timestamp,
@Nullable MessageId parentId, LocalAuthor author, String body,
MessageId previousMsgId);
}

View File

@@ -0,0 +1,30 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status;
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.PostHeader;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class GroupMessageHeader extends PostHeader {
private final GroupId groupId;
public GroupMessageHeader(GroupId groupId, MessageId id,
@Nullable MessageId parentId, long timestamp,
Author author, Status authorStatus, boolean read) {
super(id, parentId, timestamp, author, authorStatus, read);
this.groupId = groupId;
}
public GroupId getGroupId() {
return groupId;
}
}

View File

@@ -0,0 +1,30 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class JoinMessageHeader extends GroupMessageHeader {
private final Visibility visibility;
private final boolean isInitial;
public JoinMessageHeader(GroupMessageHeader h, Visibility visibility,
boolean isInitial) {
super(h.getGroupId(), h.getId(), h.getParentId(), h.getTimestamp(),
h.getAuthor(), h.getAuthorStatus(), h.isRead());
this.visibility = visibility;
this.isInitial = isInitial;
}
public Visibility getVisibility() {
return visibility;
}
public boolean isInitial() {
return isInitial;
}
}

View File

@@ -0,0 +1,28 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public enum MessageType {
JOIN(0),
POST(1);
private final int value;
MessageType(int value) {
this.value = value;
}
public static MessageType valueOf(int value) {
for (MessageType m : values()) if (m.value == value) return m;
throw new IllegalArgumentException();
}
public int getInt() {
return value;
}
}

View File

@@ -0,0 +1,31 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.briar.api.client.NamedGroup;
import org.briarproject.briar.api.sharing.Shareable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class PrivateGroup extends NamedGroup implements Shareable {
private final Author creator;
public PrivateGroup(Group group, String name, Author creator, byte[] salt) {
super(group, name, salt);
this.creator = creator;
}
public Author getCreator() {
return creator;
}
@Override
public boolean equals(Object o) {
return o instanceof PrivateGroup && super.equals(o);
}
}

View File

@@ -0,0 +1,27 @@
package org.briarproject.briar.api.privategroup;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
public interface PrivateGroupConstants {
/**
* The maximum length of a group's name in UTF-8 bytes.
*/
int MAX_GROUP_NAME_LENGTH = 100;
/**
* The length of a group's random salt in bytes.
*/
int GROUP_SALT_LENGTH = 32;
/**
* The maximum length of a group post's body in bytes.
*/
int MAX_GROUP_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
/**
* The maximum length of a group invitation message in bytes.
*/
int MAX_GROUP_INVITATION_MSG_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
}

View File

@@ -0,0 +1,26 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
@NotNullByDefault
public interface PrivateGroupFactory {
/**
* Creates a private group with the given name and author.
*/
PrivateGroup createPrivateGroup(String name, Author author);
/**
* Creates a private group with the given name, author and salt.
*/
PrivateGroup createPrivateGroup(String name, Author author, byte[] salt);
/**
* Parses a group and returns the corresponding PrivateGroup.
*/
PrivateGroup parsePrivateGroup(Group group) throws FormatException;
}

View File

@@ -0,0 +1,140 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import java.util.Collection;
@NotNullByDefault
public interface PrivateGroupManager {
/**
* The unique ID of the private group client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.privategroup");
/**
* Adds a new private group and joins it.
*
* @param group The private group to add
* @param joinMsg The new member's join message
* @param creator True if the group is added by its creator
*/
void addPrivateGroup(PrivateGroup group, GroupMessage joinMsg,
boolean creator) throws DbException;
/**
* Adds a new private group and joins it.
*
* @param group The private group to add
* @param joinMsg The new member's join message
* @param creator True if the group is added by its creator
*/
void addPrivateGroup(Transaction txn, PrivateGroup group,
GroupMessage joinMsg, boolean creator) throws DbException;
/**
* Removes a dissolved private group.
*/
void removePrivateGroup(GroupId g) throws DbException;
/**
* Returns the ID of the user's previous message sent to the group
*/
MessageId getPreviousMsgId(GroupId g) throws DbException;
/**
* Marks the given private group as dissolved.
*/
void markGroupDissolved(Transaction txn, GroupId g) throws DbException;
/**
* Returns true if the given private group has been dissolved.
*/
boolean isDissolved(GroupId g) throws DbException;
/**
* Stores and sends a local private group message.
*/
GroupMessageHeader addLocalMessage(GroupMessage p) throws DbException;
/**
* Returns the private group with the given ID.
*/
PrivateGroup getPrivateGroup(GroupId g) throws DbException;
/**
* Returns the private group with the given ID.
*/
PrivateGroup getPrivateGroup(Transaction txn, GroupId g) throws DbException;
/**
* Returns all private groups the user is a member of.
*/
Collection<PrivateGroup> getPrivateGroups() throws DbException;
/**
* Returns the body of the private group message with the given ID.
*/
String getMessageBody(MessageId m) throws DbException;
/**
* Returns the headers of all messages in the given private group.
*/
Collection<GroupMessageHeader> getHeaders(GroupId g) throws DbException;
/**
* Returns all members of the given private group.
*/
Collection<GroupMember> getMembers(GroupId g) throws DbException;
/**
* Returns true if the given author is a member of the given private group.
*/
boolean isMember(Transaction txn, GroupId g, Author a) throws DbException;
/**
* Returns the group count for the given private group.
*/
GroupCount getGroupCount(GroupId g) throws DbException;
/**
* Marks a message as read or unread and updates the group count.
*/
void setReadFlag(GroupId g, MessageId m, boolean read) throws DbException;
/**
* Called when a contact relationship has been revealed between the user
* and the given author in the given private group.
*
* @param byContact True if the contact revealed the relationship first,
* otherwise false.
*/
void relationshipRevealed(Transaction txn, GroupId g, AuthorId a,
boolean byContact) throws FormatException, DbException;
/**
* Registers a hook to be called when members are added or private groups
* are removed.
*/
void registerPrivateGroupHook(PrivateGroupHook hook);
@NotNullByDefault
interface PrivateGroupHook {
void addingMember(Transaction txn, GroupId g, Author a)
throws DbException;
void removingGroup(Transaction txn, GroupId g) throws DbException;
}
}

View File

@@ -0,0 +1,32 @@
package org.briarproject.briar.api.privategroup;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public enum Visibility {
INVISIBLE(0),
VISIBLE(1),
REVEALED_BY_US(2),
REVEALED_BY_CONTACT(3);
private final int value;
Visibility(int value) {
this.value = value;
}
public static Visibility valueOf(int value) throws FormatException {
for (Visibility v : values()) if (v.value == value) return v;
throw new FormatException();
}
public int getInt() {
return value;
}
}

View File

@@ -0,0 +1,38 @@
package org.briarproject.briar.api.privategroup.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.privategroup.Visibility;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ContactRelationshipRevealedEvent extends Event {
private final GroupId groupId;
private final AuthorId memberId;
private final Visibility visibility;
public ContactRelationshipRevealedEvent(GroupId groupId, AuthorId memberId,
Visibility visibility) {
this.groupId = groupId;
this.memberId = memberId;
this.visibility = visibility;
}
public GroupId getGroupId() {
return groupId;
}
public AuthorId getMemberId() {
return memberId;
}
public Visibility getVisibility() {
return visibility;
}
}

View File

@@ -0,0 +1,27 @@
package org.briarproject.briar.api.privategroup.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a private group is dissolved by a remote
* creator.
*/
@Immutable
@NotNullByDefault
public class GroupDissolvedEvent extends Event {
private final GroupId groupId;
public GroupDissolvedEvent(GroupId groupId) {
this.groupId = groupId;
}
public GroupId getGroupId() {
return groupId;
}
}

View File

@@ -0,0 +1,21 @@
package org.briarproject.briar.api.privategroup.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class GroupInvitationRequestReceivedEvent extends
InvitationRequestReceivedEvent<PrivateGroup> {
public GroupInvitationRequestReceivedEvent(PrivateGroup group,
ContactId contactId, GroupInvitationRequest request) {
super(group, contactId, request);
}
}

View File

@@ -0,0 +1,19 @@
package org.briarproject.briar.api.privategroup.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.sharing.InvitationResponse;
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class GroupInvitationResponseReceivedEvent
extends InvitationResponseReceivedEvent {
public GroupInvitationResponseReceivedEvent(ContactId contactId,
InvitationResponse response) {
super(contactId, response);
}
}

View File

@@ -0,0 +1,42 @@
package org.briarproject.briar.api.privategroup.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a private group message was added
* to the database.
*/
@Immutable
@NotNullByDefault
public class GroupMessageAddedEvent extends Event {
private final GroupId groupId;
private final GroupMessageHeader header;
private final boolean local;
public GroupMessageAddedEvent(GroupId groupId, GroupMessageHeader header,
boolean local) {
this.groupId = groupId;
this.header = header;
this.local = local;
}
public GroupId getGroupId() {
return groupId;
}
public GroupMessageHeader getHeader() {
return header;
}
public boolean isLocal() {
return local;
}
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.briar.api.privategroup.invitation;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import static org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
@NotNullByDefault
public interface GroupInvitationFactory {
String SIGNING_LABEL_INVITE = CLIENT_ID + "/INVITE";
/**
* Returns a signature to include when inviting a member to join a private
* group. If the member accepts the invitation, the signature will be
* included in the member's join message.
*/
@CryptoExecutor
byte[] signInvitation(Contact c, GroupId privateGroupId, long timestamp,
byte[] privateKey);
/**
* Returns a token to be signed by the creator when inviting a member to
* join a private group. If the member accepts the invitation, the
* signature will be included in the member's join message.
*/
BdfList createInviteToken(AuthorId creatorId, AuthorId memberId,
GroupId privateGroupId, long timestamp);
}

View File

@@ -0,0 +1,25 @@
package org.briarproject.briar.api.privategroup.invitation;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.sharing.InvitationItem;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class GroupInvitationItem extends InvitationItem<PrivateGroup> {
private final Contact creator;
public GroupInvitationItem(PrivateGroup privateGroup, Contact creator) {
super(privateGroup, false);
this.creator = creator;
}
public Contact getCreator() {
return creator;
}
}

View File

@@ -0,0 +1,83 @@
package org.briarproject.briar.api.privategroup.invitation;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.messaging.ConversationManager.ConversationClient;
import org.briarproject.briar.api.privategroup.PrivateGroup;
import org.briarproject.briar.api.sharing.InvitationMessage;
import java.util.Collection;
import javax.annotation.Nullable;
@NotNullByDefault
public interface GroupInvitationManager extends ConversationClient {
/**
* The unique ID of the private group invitation client.
*/
ClientId CLIENT_ID =
new ClientId("org.briarproject.briar.privategroup.invitation");
/**
* Sends an invitation to share the given private group with the given
* contact, including an optional message.
*
* @throws ProtocolStateException if the group is no longer eligible to be
* shared with the contact, for example because an invitation is already
* pending.
*/
void sendInvitation(GroupId g, ContactId c, @Nullable String message,
long timestamp, byte[] signature) throws DbException;
/**
* Responds to a pending private group invitation from the given contact.
*
* @throws ProtocolStateException if the invitation is no longer pending,
* for example because the group has been dissolved.
*/
void respondToInvitation(ContactId c, PrivateGroup g, boolean accept)
throws DbException;
/**
* Responds to a pending private group invitation from the given contact.
*
* @throws ProtocolStateException if the invitation is no longer pending,
* for example because the group has been dissolved.
*/
void respondToInvitation(ContactId c, SessionId s, boolean accept)
throws DbException;
/**
* Makes the user's relationship with the given contact visible to the
* given private group.
*
* @throws ProtocolStateException if the relationship is no longer eligible
* to be revealed, for example because the contact has revealed it.
*/
void revealRelationship(ContactId c, GroupId g) throws DbException;
/**
* Returns all private group invitation messages related to the given
* contact.
*/
Collection<InvitationMessage> getInvitationMessages(ContactId c)
throws DbException;
/**
* Returns all private groups to which the user has been invited.
*/
Collection<GroupInvitationItem> getInvitations() throws DbException;
/**
* Returns true if the given contact can be invited to the given private
* group.
*/
boolean isInvitationAllowed(Contact c, GroupId g) throws DbException;
}

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.api.privategroup.invitation;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.sharing.InvitationRequest;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class GroupInvitationRequest extends InvitationRequest {
private final String groupName;
private final Author creator;
public GroupInvitationRequest(MessageId id, SessionId sessionId,
GroupId groupId, ContactId contactId, @Nullable String message,
String groupName, Author creator, boolean available, long time,
boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, groupId, contactId, message, available, time,
local, sent, seen, read);
this.groupName = groupName;
this.creator = creator;
}
public String getGroupName() {
return groupName;
}
public Author getCreator() {
return creator;
}
}

View File

@@ -0,0 +1,22 @@
package org.briarproject.briar.api.privategroup.invitation;
import org.briarproject.bramble.api.contact.ContactId;
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.InvitationResponse;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class GroupInvitationResponse extends InvitationResponse {
public GroupInvitationResponse(MessageId id, SessionId sessionId,
GroupId groupId, ContactId contactId, boolean accept, long time,
boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, groupId, contactId, accept, time, local, sent,
seen, read);
}
}

View File

@@ -0,0 +1,10 @@
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;
public interface InvitationFactory<I extends SharingMessage.Invitation> {
I build(GroupId groupId, BdfDictionary d) throws FormatException;
}

View File

@@ -0,0 +1,36 @@
package org.briarproject.briar.api.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public abstract class InvitationItem<S extends Shareable> {
private final S shareable;
private final boolean subscribed;
public InvitationItem(S shareable, boolean subscribed) {
this.shareable = shareable;
this.subscribed = subscribed;
}
public S getShareable() {
return shareable;
}
public GroupId getId() {
return shareable.getId();
}
public String getName() {
return shareable.getName();
}
public boolean isSubscribed() {
return subscribed;
}
}

View File

@@ -0,0 +1,36 @@
package org.briarproject.briar.api.sharing;
import org.briarproject.bramble.api.contact.ContactId;
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.BaseMessageHeader;
import org.briarproject.briar.api.client.SessionId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public abstract class InvitationMessage extends BaseMessageHeader {
private final SessionId sessionId;
private final ContactId contactId;
public InvitationMessage(MessageId id, SessionId sessionId, GroupId groupId,
ContactId contactId, long time, boolean local, boolean sent,
boolean seen, boolean read) {
super(id, groupId, time, local, read, sent, seen);
this.sessionId = sessionId;
this.contactId = contactId;
}
public SessionId getSessionId() {
return sessionId;
}
public ContactId getContactId() {
return contactId;
}
}

View File

@@ -0,0 +1,39 @@
package org.briarproject.briar.api.sharing;
import org.briarproject.bramble.api.contact.ContactId;
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;
@Immutable
@NotNullByDefault
public abstract class InvitationRequest extends InvitationMessage {
@Nullable
private final String message;
private final boolean available;
public InvitationRequest(MessageId id, SessionId sessionId, GroupId groupId,
ContactId contactId, @Nullable String message, boolean available,
long time, boolean local, boolean sent, boolean seen,
boolean read) {
super(id, sessionId, groupId, contactId, time, local, sent, seen, read);
this.message = message;
this.available = available;
}
@Nullable
public String getMessage() {
return message;
}
public boolean isAvailable() {
return available;
}
}

View File

@@ -0,0 +1,28 @@
package org.briarproject.briar.api.sharing;
import org.briarproject.bramble.api.contact.ContactId;
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.Immutable;
@Immutable
@NotNullByDefault
public abstract class InvitationResponse extends InvitationMessage {
private final boolean accept;
public InvitationResponse(MessageId id, SessionId sessionId,
GroupId groupId, ContactId contactId, boolean accept, long time,
boolean local, boolean sent, boolean seen, boolean read) {
super(id, sessionId, groupId, contactId, time, local, sent, seen, read);
this.accept = accept;
}
public boolean wasAccepted() {
return accept;
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.briar.api.sharing;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId;
@NotNullByDefault
public interface Shareable {
GroupId getId();
Group getGroup();
String getName();
}

View File

@@ -0,0 +1,48 @@
package org.briarproject.briar.api.sharing;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
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
* invitee in UTF-8 bytes.
*/
int MAX_INVITATION_MESSAGE_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
String CONTACT_ID = "contactId";
String GROUP_ID = "groupId";
String TO_BE_SHARED_BY_US = "toBeSharedByUs";
String SHARED_BY_US = "sharedByUs";
String SHARED_WITH_US = "sharedWithUs";
String TYPE = "type";
String SESSION_ID = "sessionId";
String STORAGE_ID = "storageId";
String STATE = "state";
String LOCAL = "local";
String TIME = "time";
String IS_SHARER = "isSharer";
String SHAREABLE_ID = "shareableId";
String INVITATION_MSG = "invitationMsg";
String INVITATION_ID = "invitationId";
String RESPONSE_ID = "responseId";
int SHARE_MSG_TYPE_INVITATION = 1;
int SHARE_MSG_TYPE_ACCEPT = 2;
int SHARE_MSG_TYPE_DECLINE = 3;
int SHARE_MSG_TYPE_LEAVE = 4;
int SHARE_MSG_TYPE_ABORT = 5;
int TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US = 0;
int TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US = 1;
int TASK_ADD_SHARED_SHAREABLE = 2;
int TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US = 3;
int TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US = 4;
int TASK_SHARE_SHAREABLE = 5;
int TASK_UNSHARE_SHAREABLE_SHARED_BY_US = 6;
int TASK_UNSHARE_SHAREABLE_SHARED_WITH_US = 7;
}

View File

@@ -0,0 +1,27 @@
package org.briarproject.briar.api.sharing;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class SharingInvitationItem extends InvitationItem<Shareable> {
private final Collection<Contact> newSharers;
public SharingInvitationItem(Shareable shareable, boolean subscribed,
Collection<Contact> newSharers) {
super(shareable, subscribed);
this.newSharers = newSharers;
}
public Collection<Contact> getNewSharers() {
return newSharers;
}
}

View File

@@ -0,0 +1,65 @@
package org.briarproject.briar.api.sharing;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
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.messaging.ConversationManager.ConversationClient;
import java.util.Collection;
import javax.annotation.Nullable;
@NotNullByDefault
public interface SharingManager<S extends Shareable>
extends ConversationClient {
/**
* Sends an invitation to share the given group with the given contact
* and sends an optional message along with it.
*/
void sendInvitation(GroupId groupId, ContactId contactId,
@Nullable String message) throws DbException;
/**
* Responds to a pending group invitation
*/
void respondToInvitation(S s, Contact c, boolean accept)
throws DbException;
/**
* Responds to a pending group invitation
*/
void respondToInvitation(SessionId id, boolean accept)
throws DbException;
/**
* Returns all group sharing messages sent by the Contact
* identified by contactId.
*/
Collection<InvitationMessage> getInvitationMessages(
ContactId contactId) throws DbException;
/**
* Returns all invitations to groups.
*/
Collection<SharingInvitationItem> getInvitations() throws DbException;
/**
* Returns all contacts who are sharing the given group with us.
*/
Collection<Contact> getSharedBy(GroupId g) throws DbException;
/**
* Returns all contacts with whom the given group is shared.
*/
Collection<Contact> getSharedWith(GroupId g) throws DbException;
/**
* Returns true if the group not already shared and no invitation is open
*/
boolean canBeShared(GroupId g, Contact c) throws DbException;
}

View File

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

@@ -0,0 +1,38 @@
package org.briarproject.briar.api.sharing.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.sharing.InvitationRequest;
import org.briarproject.briar.api.sharing.Shareable;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public abstract class InvitationRequestReceivedEvent<S extends Shareable>
extends Event {
private final S shareable;
private final ContactId contactId;
private final InvitationRequest request;
protected InvitationRequestReceivedEvent(S shareable, ContactId contactId,
InvitationRequest request) {
this.shareable = shareable;
this.contactId = contactId;
this.request = request;
}
public ContactId getContactId() {
return contactId;
}
public InvitationRequest getRequest() {
return request;
}
public S getShareable() {
return shareable;
}
}

View File

@@ -0,0 +1,30 @@
package org.briarproject.briar.api.sharing.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.sharing.InvitationResponse;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public abstract class InvitationResponseReceivedEvent extends Event {
private final ContactId contactId;
private final InvitationResponse response;
public InvitationResponseReceivedEvent(ContactId contactId,
InvitationResponse response) {
this.contactId = contactId;
this.response = response;
}
public ContactId getContactId() {
return contactId;
}
public InvitationResponse getResponse() {
return response;
}
}