mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +01:00
Merge branch '494-implement-backend-for-reblogging-and-blog-comments' into 'master'
Add support for comments and reblogging to Blog Client Comments and reblogs need to depend on the post they refer to. Since message dependencies are limited to one group, the post and also the comments need to be wrapped when commented on or reblogged to another blog (and group). For this reason, in addition to comments, two new wrapping message types are introduced. They retain all data of the original messages and allow for reconstruction and signature verification. This MR breaks backwards compatibility with old blog posts. It removes the content type, title and parent ID from the post. Furthermore, it includes one commit that replaces the `Message` in `MessageSharedEvent` with a `MessageId`. Closes #494 See merge request !285
This commit is contained in:
@@ -59,7 +59,7 @@ class BlogFactoryImpl implements BlogFactory {
|
||||
|
||||
byte[] descriptor = g.getDescriptor();
|
||||
// Blog Name, Author Name, Public Key
|
||||
BdfList blog = clientHelper.toList(descriptor, 0, descriptor.length);
|
||||
BdfList blog = clientHelper.toList(descriptor);
|
||||
String name = blog.getString(0);
|
||||
Author a =
|
||||
authorFactory.createAuthor(blog.getString(1), blog.getRaw(2));
|
||||
|
||||
@@ -2,10 +2,13 @@ package org.briarproject.blogs;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogCommentHeader;
|
||||
import org.briarproject.api.blogs.BlogFactory;
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.blogs.BlogPost;
|
||||
import org.briarproject.api.blogs.BlogPostFactory;
|
||||
import org.briarproject.api.blogs.BlogPostHeader;
|
||||
import org.briarproject.api.blogs.MessageType;
|
||||
import org.briarproject.api.clients.Client;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
@@ -33,10 +36,14 @@ import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.clients.BdfIncomingMessageHook;
|
||||
import org.briarproject.util.StringUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -45,19 +52,26 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_COMMENT;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_DESCRIPTION;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_PARENT_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TIME_RECEIVED;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TYPE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT_MSG_ID;
|
||||
import static org.briarproject.api.blogs.MessageType.COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.POST;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_POST;
|
||||
import static org.briarproject.api.contact.ContactManager.AddContactHook;
|
||||
import static org.briarproject.api.contact.ContactManager.RemoveContactHook;
|
||||
import static org.briarproject.blogs.BlogPostValidator.authorToBdfDictionary;
|
||||
|
||||
class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
AddContactHook, RemoveContactHook, Client,
|
||||
@@ -74,18 +88,21 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
private final IdentityManager identityManager;
|
||||
private final ContactManager contactManager;
|
||||
private final BlogFactory blogFactory;
|
||||
private final BlogPostFactory blogPostFactory;
|
||||
private final List<RemoveBlogHook> removeHooks;
|
||||
|
||||
@Inject
|
||||
BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager,
|
||||
ClientHelper clientHelper, MetadataParser metadataParser,
|
||||
ContactManager contactManager, BlogFactory blogFactory) {
|
||||
ContactManager contactManager, BlogFactory blogFactory,
|
||||
BlogPostFactory blogPostFactory) {
|
||||
super(clientHelper, metadataParser);
|
||||
|
||||
this.db = db;
|
||||
this.identityManager = identityManager;
|
||||
this.contactManager = contactManager;
|
||||
this.blogFactory = blogFactory;
|
||||
this.blogPostFactory = blogPostFactory;
|
||||
removeHooks = new CopyOnWriteArrayList<RemoveBlogHook>();
|
||||
}
|
||||
|
||||
@@ -160,13 +177,43 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
protected void incomingMessage(Transaction txn, Message m, BdfList list,
|
||||
BdfDictionary meta) throws DbException, FormatException {
|
||||
|
||||
clientHelper.setMessageShared(txn, m, true);
|
||||
|
||||
GroupId groupId = m.getGroupId();
|
||||
BlogPostHeader h = getPostHeaderFromMetadata(txn, m.getId(), meta);
|
||||
BlogPostAddedEvent event =
|
||||
new BlogPostAddedEvent(groupId, h, false);
|
||||
txn.attach(event);
|
||||
MessageType type = getMessageType(meta);
|
||||
|
||||
if (type == POST || type == COMMENT) {
|
||||
BlogPostHeader h =
|
||||
getPostHeaderFromMetadata(txn, groupId, m.getId(), meta);
|
||||
|
||||
// check that original message IDs match
|
||||
if (type == COMMENT) {
|
||||
BdfDictionary d = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, h.getParentId());
|
||||
byte[] original1 = d.getRaw(KEY_ORIGINAL_MSG_ID);
|
||||
byte[] original2 = meta.getRaw(KEY_ORIGINAL_PARENT_MSG_ID);
|
||||
if (!Arrays.equals(original1, original2)) {
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
// share dependencies recursively - TODO remove with #598
|
||||
share(txn, h);
|
||||
|
||||
// broadcast event about new post or comment
|
||||
BlogPostAddedEvent event =
|
||||
new BlogPostAddedEvent(groupId, h, false);
|
||||
txn.attach(event);
|
||||
} else if (type == WRAPPED_COMMENT) {
|
||||
// Check that the original message ID in the dependency's metadata
|
||||
// matches the original parent ID of the wrapped comment
|
||||
MessageId dependencyId =
|
||||
new MessageId(meta.getRaw(KEY_PARENT_MSG_ID));
|
||||
BdfDictionary d = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, dependencyId);
|
||||
byte[] original1 = d.getRaw(KEY_ORIGINAL_MSG_ID);
|
||||
byte[] original2 = meta.getRaw(KEY_ORIGINAL_PARENT_MSG_ID);
|
||||
if (!Arrays.equals(original1, original2)) {
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -247,17 +294,9 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
public void addLocalPost(Transaction txn, BlogPost p) throws DbException {
|
||||
try {
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
if (p.getTitle() != null) meta.put(KEY_TITLE, p.getTitle());
|
||||
meta.put(KEY_TYPE, POST.getInt());
|
||||
meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
|
||||
|
||||
Author a = p.getAuthor();
|
||||
BdfDictionary authorMeta = new BdfDictionary();
|
||||
authorMeta.put(KEY_AUTHOR_ID, a.getId());
|
||||
authorMeta.put(KEY_AUTHOR_NAME, a.getName());
|
||||
authorMeta.put(KEY_PUBLIC_KEY, a.getPublicKey());
|
||||
meta.put(KEY_AUTHOR, authorMeta);
|
||||
|
||||
meta.put(KEY_CONTENT_TYPE, p.getContentType());
|
||||
meta.put(KEY_AUTHOR, authorToBdfDictionary(p.getAuthor()));
|
||||
meta.put(KEY_READ, true);
|
||||
clientHelper.addLocalMessage(txn, p.getMessage(), CLIENT_ID, meta,
|
||||
true);
|
||||
@@ -265,7 +304,8 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
// broadcast event about new post
|
||||
GroupId groupId = p.getMessage().getGroupId();
|
||||
MessageId postId = p.getMessage().getId();
|
||||
BlogPostHeader h = getPostHeaderFromMetadata(txn, postId, meta);
|
||||
BlogPostHeader h =
|
||||
getPostHeaderFromMetadata(txn, groupId, postId, meta);
|
||||
BlogPostAddedEvent event =
|
||||
new BlogPostAddedEvent(groupId, h, true);
|
||||
txn.attach(event);
|
||||
@@ -274,6 +314,125 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalComment(LocalAuthor author, GroupId groupId,
|
||||
@Nullable String comment, BlogPostHeader pOriginalHeader)
|
||||
throws DbException {
|
||||
|
||||
MessageType type = pOriginalHeader.getType();
|
||||
if (type != POST && type != COMMENT)
|
||||
throw new IllegalArgumentException("Comment on unknown type!");
|
||||
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
// Wrap post that we are commenting on
|
||||
MessageId parentId = wrapMessage(txn, groupId, pOriginalHeader);
|
||||
|
||||
// Get ID of new parent's original message.
|
||||
// Assumes that pOriginalHeader is a POST or COMMENT
|
||||
MessageId pOriginalId = pOriginalHeader.getId();
|
||||
|
||||
// Create actual comment
|
||||
Message message = blogPostFactory
|
||||
.createBlogComment(groupId, author, comment, pOriginalId,
|
||||
parentId);
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(KEY_TYPE, COMMENT.getInt());
|
||||
if (comment != null) meta.put(KEY_COMMENT, comment);
|
||||
meta.put(KEY_TIMESTAMP, message.getTimestamp());
|
||||
meta.put(KEY_ORIGINAL_MSG_ID, message.getId());
|
||||
meta.put(KEY_ORIGINAL_PARENT_MSG_ID, pOriginalId);
|
||||
meta.put(KEY_PARENT_MSG_ID, parentId);
|
||||
meta.put(KEY_AUTHOR, authorToBdfDictionary(author));
|
||||
|
||||
// Send comment
|
||||
clientHelper.addLocalMessage(txn, message, CLIENT_ID, meta, true);
|
||||
|
||||
// broadcast event
|
||||
BlogPostHeader h =
|
||||
getPostHeaderFromMetadata(txn, groupId, message.getId(),
|
||||
meta);
|
||||
BlogPostAddedEvent event = new BlogPostAddedEvent(groupId, h, true);
|
||||
txn.attach(event);
|
||||
txn.setComplete();
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new IllegalArgumentException("Invalid key of author", e);
|
||||
} finally {
|
||||
//noinspection ThrowFromFinallyBlock
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
private MessageId wrapMessage(Transaction txn, GroupId groupId,
|
||||
BlogPostHeader pOriginalHeader)
|
||||
throws DbException, FormatException {
|
||||
|
||||
if (groupId.equals(pOriginalHeader.getGroupId())) {
|
||||
// We are trying to wrap a post that is already in our group.
|
||||
// This is unnecessary, so just return the post's MessageId
|
||||
return pOriginalHeader.getId();
|
||||
}
|
||||
|
||||
// Get body of message to be wrapped
|
||||
BdfList body =
|
||||
clientHelper.getMessageAsList(txn, pOriginalHeader.getId());
|
||||
long wTimestamp = pOriginalHeader.getTimestamp();
|
||||
Message wMessage;
|
||||
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
MessageType type = pOriginalHeader.getType();
|
||||
if (type == POST) {
|
||||
Group wGroup = db.getGroup(txn, pOriginalHeader.getGroupId());
|
||||
byte[] wDescriptor = wGroup.getDescriptor();
|
||||
// Wrap post
|
||||
wMessage = blogPostFactory
|
||||
.wrapPost(groupId, wDescriptor, wTimestamp, body);
|
||||
meta.put(KEY_TYPE, WRAPPED_POST.getInt());
|
||||
} else if (type == COMMENT) {
|
||||
Group wGroup = db.getGroup(txn, pOriginalHeader.getGroupId());
|
||||
byte[] wDescriptor = wGroup.getDescriptor();
|
||||
BlogCommentHeader wComment = (BlogCommentHeader) pOriginalHeader;
|
||||
MessageId wrappedId =
|
||||
wrapMessage(txn, groupId, wComment.getParent());
|
||||
// Wrap comment
|
||||
wMessage = blogPostFactory
|
||||
.wrapComment(groupId, wDescriptor, wTimestamp,
|
||||
body, wrappedId);
|
||||
meta.put(KEY_TYPE, WRAPPED_COMMENT.getInt());
|
||||
if(wComment.getComment() != null)
|
||||
meta.put(KEY_COMMENT, wComment.getComment());
|
||||
meta.put(KEY_PARENT_MSG_ID, wrappedId);
|
||||
} else if (type == WRAPPED_POST) {
|
||||
// Re-wrap wrapped post without adding another wrapping layer
|
||||
wMessage = blogPostFactory.rewrapWrappedPost(groupId, body);
|
||||
meta.put(KEY_TYPE, WRAPPED_POST.getInt());
|
||||
} else if (type == WRAPPED_COMMENT) {
|
||||
BlogCommentHeader wComment = (BlogCommentHeader) pOriginalHeader;
|
||||
MessageId wrappedId =
|
||||
wrapMessage(txn, groupId, wComment.getParent());
|
||||
// Re-wrap wrapped comment
|
||||
wMessage = blogPostFactory
|
||||
.rewrapWrappedComment(groupId, body, wrappedId);
|
||||
meta.put(KEY_TYPE, WRAPPED_COMMENT.getInt());
|
||||
if(wComment.getComment() != null)
|
||||
meta.put(KEY_COMMENT, wComment.getComment());
|
||||
meta.put(KEY_PARENT_MSG_ID, wrappedId);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown Message Type: " + type);
|
||||
}
|
||||
meta.put(KEY_ORIGINAL_MSG_ID, pOriginalHeader.getId());
|
||||
meta.put(KEY_AUTHOR, authorToBdfDictionary(pOriginalHeader.getAuthor()));
|
||||
meta.put(KEY_TIMESTAMP, pOriginalHeader.getTimestamp());
|
||||
meta.put(KEY_TIME_RECEIVED, pOriginalHeader.getTimeReceived());
|
||||
|
||||
// Send wrapped message and store metadata
|
||||
clientHelper.addLocalMessage(txn, wMessage, CLIENT_ID, meta, true);
|
||||
return wMessage.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blog getBlog(GroupId g) throws DbException {
|
||||
Blog blog;
|
||||
@@ -340,12 +499,13 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlogPostHeader getPostHeader(MessageId m) throws DbException {
|
||||
public BlogPostHeader getPostHeader(GroupId g, MessageId m)
|
||||
throws DbException {
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
BdfDictionary meta =
|
||||
clientHelper.getMessageMetadataAsDictionary(txn, m);
|
||||
BlogPostHeader h = getPostHeaderFromMetadata(txn, m, meta);
|
||||
BlogPostHeader h = getPostHeaderFromMetadata(txn, g, m, meta);
|
||||
txn.setComplete();
|
||||
return h;
|
||||
} catch (FormatException e) {
|
||||
@@ -366,25 +526,50 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO directly return String (#598)
|
||||
private byte[] getPostBody(BdfList message) throws FormatException {
|
||||
// content, signature
|
||||
// content: parent, contentType, title, body, attachments
|
||||
BdfList content = message.getList(0);
|
||||
return content.getRaw(3);
|
||||
MessageType type = MessageType.valueOf(message.getLong(0).intValue());
|
||||
if (type == POST) {
|
||||
// type, body, signature
|
||||
return StringUtils.toUtf8(message.getString(1));
|
||||
} else if (type == WRAPPED_POST) {
|
||||
// type, p_group descriptor, p_timestamp, p_content, p_signature
|
||||
return StringUtils.toUtf8(message.getString(3));
|
||||
} else {
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<BlogPostHeader> getPostHeaders(GroupId g)
|
||||
throws DbException {
|
||||
|
||||
// Query for posts and comments only
|
||||
BdfDictionary query1 = BdfDictionary.of(
|
||||
new BdfEntry(KEY_TYPE, POST.getInt())
|
||||
);
|
||||
BdfDictionary query2 = BdfDictionary.of(
|
||||
new BdfEntry(KEY_TYPE, COMMENT.getInt())
|
||||
);
|
||||
|
||||
// TODO this could be optimized by looking up author status once (#625)
|
||||
|
||||
Collection<BlogPostHeader> headers = new ArrayList<BlogPostHeader>();
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
Map<MessageId, BdfDictionary> metadata1 =
|
||||
clientHelper.getMessageMetadataAsDictionary(txn, g, query1);
|
||||
Map<MessageId, BdfDictionary> metadata2 =
|
||||
clientHelper.getMessageMetadataAsDictionary(txn, g, query2);
|
||||
Map<MessageId, BdfDictionary> metadata =
|
||||
clientHelper.getMessageMetadataAsDictionary(txn, g);
|
||||
List<BlogPostHeader> headers = new ArrayList<BlogPostHeader>();
|
||||
new HashMap<MessageId, BdfDictionary>(
|
||||
metadata1.size() + metadata2.size());
|
||||
metadata.putAll(metadata1);
|
||||
metadata.putAll(metadata2);
|
||||
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
||||
BlogPostHeader h = getPostHeaderFromMetadata(txn,
|
||||
entry.getKey(), entry.getValue());
|
||||
BdfDictionary meta = entry.getValue();
|
||||
BlogPostHeader h =
|
||||
getPostHeaderFromMetadata(txn, g, entry.getKey(), meta);
|
||||
headers.add(h);
|
||||
}
|
||||
txn.setComplete();
|
||||
@@ -419,10 +604,18 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
}
|
||||
|
||||
private BlogPostHeader getPostHeaderFromMetadata(Transaction txn,
|
||||
MessageId id, BdfDictionary meta)
|
||||
GroupId groupId, MessageId id) throws DbException, FormatException {
|
||||
BdfDictionary meta =
|
||||
clientHelper.getMessageMetadataAsDictionary(txn, id);
|
||||
return getPostHeaderFromMetadata(txn, groupId, id, meta);
|
||||
}
|
||||
|
||||
private BlogPostHeader getPostHeaderFromMetadata(Transaction txn,
|
||||
GroupId groupId, MessageId id, BdfDictionary meta)
|
||||
throws DbException, FormatException {
|
||||
|
||||
String title = meta.getOptionalString(KEY_TITLE);
|
||||
MessageType type = getMessageType(meta);
|
||||
|
||||
long timestamp = meta.getLong(KEY_TIMESTAMP);
|
||||
long timeReceived = meta.getLong(KEY_TIME_RECEIVED, timestamp);
|
||||
|
||||
@@ -431,12 +624,35 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||
String name = d.getString(KEY_AUTHOR_NAME);
|
||||
byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
|
||||
Author author = new Author(authorId, name, publicKey);
|
||||
Status authorStatus;
|
||||
authorStatus = identityManager.getAuthorStatus(txn, authorId);
|
||||
Status authorStatus = identityManager.getAuthorStatus(txn, authorId);
|
||||
|
||||
String contentType = meta.getString(KEY_CONTENT_TYPE);
|
||||
boolean read = meta.getBoolean(KEY_READ);
|
||||
return new BlogPostHeader(title, id, timestamp, timeReceived, author,
|
||||
authorStatus, contentType, read);
|
||||
boolean read = meta.getBoolean(KEY_READ, false);
|
||||
|
||||
if (type == COMMENT || type == WRAPPED_COMMENT) {
|
||||
String comment = meta.getOptionalString(KEY_COMMENT);
|
||||
MessageId parentId = new MessageId(meta.getRaw(KEY_PARENT_MSG_ID));
|
||||
BlogPostHeader parent =
|
||||
getPostHeaderFromMetadata(txn, groupId, parentId);
|
||||
return new BlogCommentHeader(type, groupId, comment, parent, id,
|
||||
timestamp, timeReceived, author, authorStatus, read);
|
||||
} else {
|
||||
return new BlogPostHeader(type, groupId, id, timestamp,
|
||||
timeReceived, author, authorStatus, read);
|
||||
}
|
||||
}
|
||||
|
||||
private MessageType getMessageType(BdfDictionary d) throws FormatException {
|
||||
Long longType = d.getLong(KEY_TYPE);
|
||||
return MessageType.valueOf(longType.intValue());
|
||||
}
|
||||
|
||||
// TODO remove when implementing #589
|
||||
@Deprecated
|
||||
private void share(Transaction txn, BlogPostHeader h) throws DbException {
|
||||
clientHelper.setMessageShared(txn, h.getId(), true);
|
||||
if (h instanceof BlogCommentHeader) {
|
||||
BlogPostHeader h2 = ((BlogCommentHeader) h).getParent();
|
||||
share(txn, h2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,14 @@ package org.briarproject.blogs;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.blogs.BlogPost;
|
||||
import org.briarproject.api.blogs.BlogPostFactory;
|
||||
import org.briarproject.api.blogs.MessageType;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.KeyParser;
|
||||
import org.briarproject.api.crypto.PrivateKey;
|
||||
import org.briarproject.api.crypto.Signature;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.util.StringUtils;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -22,52 +19,143 @@ import java.security.GeneralSecurityException;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_LENGTH;
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||
import static org.briarproject.api.blogs.MessageType.COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.POST;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_POST;
|
||||
|
||||
class BlogPostFactoryImpl implements BlogPostFactory {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final ClientHelper clientHelper;
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
BlogPostFactoryImpl(CryptoComponent crypto, ClientHelper clientHelper) {
|
||||
this.crypto = crypto;
|
||||
BlogPostFactoryImpl(ClientHelper clientHelper, Clock clock) {
|
||||
this.clientHelper = clientHelper;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlogPost createBlogPost(@NotNull GroupId groupId,
|
||||
@Nullable String title, long timestamp,
|
||||
@Nullable MessageId parent, @NotNull LocalAuthor author,
|
||||
@NotNull String contentType, @NotNull byte[] body)
|
||||
public BlogPost createBlogPost(@NotNull GroupId groupId, long timestamp,
|
||||
@Nullable MessageId parent, @NotNull LocalAuthor author,
|
||||
@NotNull String body)
|
||||
throws FormatException, GeneralSecurityException {
|
||||
|
||||
// Validate the arguments
|
||||
if (title != null &&
|
||||
StringUtils.toUtf8(title).length > MAX_BLOG_POST_TITLE_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
if (body.length > MAX_BLOG_POST_BODY_LENGTH)
|
||||
if (body.length() > MAX_BLOG_POST_BODY_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
// Serialise the data to be signed
|
||||
BdfList content = BdfList.of(parent, contentType, title, body, null);
|
||||
BdfList signed = BdfList.of(groupId, timestamp, content);
|
||||
BdfList signed = BdfList.of(groupId, timestamp, body);
|
||||
|
||||
// Generate the signature
|
||||
Signature signature = crypto.getSignature();
|
||||
KeyParser keyParser = crypto.getSignatureKeyParser();
|
||||
PrivateKey privateKey =
|
||||
keyParser.parsePrivateKey(author.getPrivateKey());
|
||||
signature.initSign(privateKey);
|
||||
signature.update(clientHelper.toByteArray(signed));
|
||||
byte[] sig = signature.sign();
|
||||
byte[] sig = clientHelper.sign(signed, author.getPrivateKey());
|
||||
|
||||
// Serialise the signed message
|
||||
BdfList message = BdfList.of(content, sig);
|
||||
BdfList message = BdfList.of(POST.getInt(), body, sig);
|
||||
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
||||
return new BlogPost(title, m, parent, author, contentType);
|
||||
return new BlogPost(m, parent, author);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message createBlogComment(GroupId groupId, LocalAuthor author,
|
||||
@Nullable String comment, MessageId pOriginalId, MessageId parentId)
|
||||
throws FormatException, GeneralSecurityException {
|
||||
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
|
||||
// Generate the signature
|
||||
BdfList signed =
|
||||
BdfList.of(groupId, timestamp, comment, pOriginalId, parentId);
|
||||
byte[] sig = clientHelper.sign(signed, author.getPrivateKey());
|
||||
|
||||
// Serialise the signed message
|
||||
BdfList message =
|
||||
BdfList.of(COMMENT.getInt(), comment, pOriginalId, parentId,
|
||||
sig);
|
||||
return clientHelper.createMessage(groupId, timestamp, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message wrapPost(GroupId groupId, byte[] descriptor,
|
||||
long timestamp, BdfList body)
|
||||
throws FormatException {
|
||||
|
||||
if (getType(body) != POST)
|
||||
throw new IllegalArgumentException("Needs to wrap a POST");
|
||||
|
||||
// Serialise the message
|
||||
String content = body.getString(1);
|
||||
byte[] signature = body.getRaw(2);
|
||||
BdfList message =
|
||||
BdfList.of(WRAPPED_POST.getInt(), descriptor, timestamp,
|
||||
content, signature);
|
||||
return clientHelper
|
||||
.createMessage(groupId, clock.currentTimeMillis(), message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message rewrapWrappedPost(GroupId groupId, BdfList body)
|
||||
throws FormatException {
|
||||
|
||||
if (getType(body) != WRAPPED_POST)
|
||||
throw new IllegalArgumentException("Needs to wrap a WRAPPED_POST");
|
||||
|
||||
// Serialise the message
|
||||
byte[] descriptor = body.getRaw(1);
|
||||
long timestamp = body.getLong(2);
|
||||
String content = body.getString(3);
|
||||
byte[] signature = body.getRaw(4);
|
||||
BdfList message =
|
||||
BdfList.of(WRAPPED_POST.getInt(), descriptor, timestamp,
|
||||
content, signature);
|
||||
return clientHelper
|
||||
.createMessage(groupId, clock.currentTimeMillis(), message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message wrapComment(GroupId groupId, byte[] descriptor,
|
||||
long timestamp, BdfList body, MessageId parentId)
|
||||
throws FormatException {
|
||||
|
||||
if (getType(body) != COMMENT)
|
||||
throw new IllegalArgumentException("Needs to wrap a COMMENT");
|
||||
|
||||
// Serialise the message
|
||||
String comment = body.getOptionalString(1);
|
||||
byte[] pOriginalId = body.getRaw(2);
|
||||
byte[] oldParentId = body.getRaw(3);
|
||||
byte[] signature = body.getRaw(4);
|
||||
BdfList message =
|
||||
BdfList.of(WRAPPED_COMMENT.getInt(), descriptor, timestamp,
|
||||
comment, pOriginalId, oldParentId, signature, parentId);
|
||||
return clientHelper
|
||||
.createMessage(groupId, clock.currentTimeMillis(), message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message rewrapWrappedComment(GroupId groupId, BdfList body,
|
||||
MessageId parentId) throws FormatException {
|
||||
|
||||
if (getType(body) != WRAPPED_COMMENT)
|
||||
throw new IllegalArgumentException(
|
||||
"Needs to wrap a WRAPPED_COMMENT");
|
||||
|
||||
// Serialise the message
|
||||
byte[] descriptor = body.getRaw(1);
|
||||
long timestamp = body.getLong(2);
|
||||
String comment = body.getOptionalString(3);
|
||||
byte[] pOriginalId = body.getRaw(4);
|
||||
byte[] oldParentId = body.getRaw(5);
|
||||
byte[] signature = body.getRaw(6);
|
||||
BdfList message =
|
||||
BdfList.of(WRAPPED_COMMENT.getInt(), descriptor, timestamp,
|
||||
comment, pOriginalId, oldParentId, signature, parentId);
|
||||
return clientHelper
|
||||
.createMessage(groupId, clock.currentTimeMillis(), message);
|
||||
}
|
||||
|
||||
private MessageType getType(BdfList body) throws FormatException {
|
||||
return MessageType.valueOf(body.getLong(0).intValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.blogs;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.UniqueId;
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogFactory;
|
||||
import org.briarproject.api.blogs.MessageType;
|
||||
@@ -33,18 +32,17 @@ import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_COMMENT;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CURRENT_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_PARENT_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_MSG_ID;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TIME_RECEIVED;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TYPE;
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_LENGTH;
|
||||
import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||
import static org.briarproject.api.blogs.MessageType.COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.POST;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||
|
||||
class BlogPostValidator extends BdfMessageValidator {
|
||||
@@ -72,13 +70,6 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
|
||||
BdfMessageContext c;
|
||||
|
||||
// TODO Remove! For Temporary Backwards Compatibility only!
|
||||
if (body.get(0) instanceof BdfList) {
|
||||
c = validatePost(m, g, body);
|
||||
addMessageMetadata(c, m.getTimestamp());
|
||||
return c;
|
||||
}
|
||||
|
||||
int type = body.getLong(0).intValue();
|
||||
body.removeElementAt(0);
|
||||
switch (MessageType.valueOf(type)) {
|
||||
@@ -108,83 +99,64 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
|
||||
// Content, Signature
|
||||
checkSize(body, 2);
|
||||
BdfList content = body.getList(0);
|
||||
|
||||
// Content: content type, title (optional), post body,
|
||||
// attachments (optional)
|
||||
checkSize(content, 5);
|
||||
// Parent ID is optional
|
||||
// TODO remove when breaking backwards compatibility
|
||||
byte[] parent = content.getOptionalRaw(0);
|
||||
checkLength(parent, UniqueId.LENGTH);
|
||||
// Content type
|
||||
String contentType = content.getString(1);
|
||||
checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
|
||||
if (!contentType.equals("text/plain"))
|
||||
throw new InvalidMessageException("Invalid content type");
|
||||
// Blog post title is optional
|
||||
String title = content.getOptionalString(2);
|
||||
checkLength(contentType, 0, MAX_BLOG_POST_TITLE_LENGTH);
|
||||
// Blog post body
|
||||
byte[] postBody = content.getRaw(3);
|
||||
String postBody = body.getString(0);
|
||||
checkLength(postBody, 0, MAX_BLOG_POST_BODY_LENGTH);
|
||||
// Attachments
|
||||
content.getOptionalDictionary(4);
|
||||
|
||||
// Verify Signature
|
||||
byte[] sig = body.getRaw(1);
|
||||
checkLength(sig, 1, MAX_SIGNATURE_LENGTH);
|
||||
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), content);
|
||||
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), postBody);
|
||||
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
|
||||
Author a = b.getAuthor();
|
||||
verifySignature(sig, a.getPublicKey(), signed);
|
||||
|
||||
// Return the metadata and dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
if (title != null) meta.put(KEY_TITLE, title);
|
||||
meta.put(KEY_ORIGINAL_MSG_ID, m.getId());
|
||||
meta.put(KEY_AUTHOR, authorToBdfDictionary(a));
|
||||
meta.put(KEY_CONTENT_TYPE, contentType);
|
||||
return new BdfMessageContext(meta, null);
|
||||
}
|
||||
|
||||
private BdfMessageContext validateComment(Message m, Group g, BdfList body)
|
||||
throws InvalidMessageException, FormatException {
|
||||
|
||||
// comment, parent_original_id, signature, parent_current_id
|
||||
// comment, parent_original_id, parent_id, signature
|
||||
checkSize(body, 4);
|
||||
|
||||
// Comment
|
||||
String comment = body.getOptionalString(0);
|
||||
checkLength(comment, 0, MAX_BLOG_POST_BODY_LENGTH);
|
||||
checkLength(comment, 1, MAX_BLOG_POST_BODY_LENGTH);
|
||||
|
||||
// parent_original_id
|
||||
// The ID of a post or comment in this group or another group
|
||||
byte[] originalIdBytes = body.getRaw(1);
|
||||
checkLength(originalIdBytes, MessageId.LENGTH);
|
||||
MessageId originalId = new MessageId(originalIdBytes);
|
||||
byte[] pOriginalIdBytes = body.getRaw(1);
|
||||
checkLength(pOriginalIdBytes, MessageId.LENGTH);
|
||||
MessageId pOriginalId = new MessageId(pOriginalIdBytes);
|
||||
|
||||
// parent_id
|
||||
// The ID of a post, comment, wrapped post or wrapped comment in this
|
||||
// group, which had the ID parent_original_id in the group
|
||||
// where it was originally posted
|
||||
byte[] currentIdBytes = body.getRaw(2);
|
||||
checkLength(currentIdBytes, MessageId.LENGTH);
|
||||
MessageId currentId = new MessageId(currentIdBytes);
|
||||
|
||||
// Signature
|
||||
byte[] sig = body.getRaw(2);
|
||||
byte[] sig = body.getRaw(3);
|
||||
checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
|
||||
BdfList signed =
|
||||
BdfList.of(g.getId(), m.getTimestamp(), comment, originalId);
|
||||
BdfList.of(g.getId(), m.getTimestamp(), comment, pOriginalId,
|
||||
currentId);
|
||||
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
|
||||
Author a = b.getAuthor();
|
||||
verifySignature(sig, a.getPublicKey(), signed);
|
||||
|
||||
// parent_current_id
|
||||
// The ID of a post, comment, wrapped post or wrapped comment in this
|
||||
// group, which had the ID parent_original_id in the group
|
||||
// where it was originally posted
|
||||
byte[] currentIdBytes = body.getRaw(3);
|
||||
checkLength(currentIdBytes, MessageId.LENGTH);
|
||||
MessageId currentId = new MessageId(currentIdBytes);
|
||||
|
||||
// Return the metadata and dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
if (comment != null) meta.put(KEY_COMMENT, comment);
|
||||
meta.put(KEY_ORIGINAL_MSG_ID, originalId);
|
||||
meta.put(KEY_CURRENT_MSG_ID, currentId);
|
||||
meta.put(KEY_ORIGINAL_MSG_ID, m.getId());
|
||||
meta.put(KEY_ORIGINAL_PARENT_MSG_ID, pOriginalId);
|
||||
meta.put(KEY_PARENT_MSG_ID, currentId);
|
||||
meta.put(KEY_AUTHOR, authorToBdfDictionary(a));
|
||||
Collection<MessageId> dependencies = Collections.singleton(currentId);
|
||||
return new BdfMessageContext(meta, dependencies);
|
||||
@@ -193,7 +165,7 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
private BdfMessageContext validateWrappedPost(Message m, Group g,
|
||||
BdfList body) throws InvalidMessageException, FormatException {
|
||||
|
||||
// group descriptor, timestamp, content, signature
|
||||
// p_group descriptor, p_timestamp, p_content, p_signature
|
||||
checkSize(body, 4);
|
||||
|
||||
// Group Descriptor
|
||||
@@ -201,9 +173,10 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
|
||||
// Timestamp of Wrapped Post
|
||||
long wTimestamp = body.getLong(1);
|
||||
if (wTimestamp < 0) throw new FormatException();
|
||||
|
||||
// Content of Wrapped Post
|
||||
BdfList content = body.getList(2);
|
||||
String content = body.getString(2);
|
||||
|
||||
// Signature of Wrapped Post
|
||||
byte[] signature = body.getRaw(3);
|
||||
@@ -212,71 +185,81 @@ class BlogPostValidator extends BdfMessageValidator {
|
||||
// Get and Validate the Wrapped Message
|
||||
Group wGroup = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
BdfList wBodyList = BdfList.of(content, signature);
|
||||
BdfList wBodyList = BdfList.of(POST.getInt(), content, signature);
|
||||
byte[] wBody = clientHelper.toByteArray(wBodyList);
|
||||
Message wMessage =
|
||||
messageFactory.createMessage(wGroup.getId(), wTimestamp, wBody);
|
||||
wBodyList.remove(0);
|
||||
BdfMessageContext c = validatePost(wMessage, wGroup, wBodyList);
|
||||
|
||||
// Return the metadata and dependencies
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(KEY_ORIGINAL_MSG_ID, wMessage.getId());
|
||||
meta.put(KEY_TIMESTAMP, wTimestamp);
|
||||
meta.put(KEY_AUTHOR, c.getDictionary().getDictionary(KEY_AUTHOR));
|
||||
meta.put(KEY_CONTENT_TYPE,
|
||||
c.getDictionary().getString(KEY_CONTENT_TYPE));
|
||||
return new BdfMessageContext(meta, null);
|
||||
}
|
||||
|
||||
private BdfMessageContext validateWrappedComment(Message m, Group g,
|
||||
BdfList body) throws InvalidMessageException, FormatException {
|
||||
|
||||
// group descriptor, timestamp, comment, parent_original_id, signature,
|
||||
// parent_current_id
|
||||
checkSize(body, 6);
|
||||
// c_group descriptor, c_timestamp, c_comment, c_parent_original_id,
|
||||
// c_parent_id, c_signature, parent_id
|
||||
checkSize(body, 7);
|
||||
|
||||
// Group Descriptor
|
||||
byte[] descriptor = body.getRaw(0);
|
||||
|
||||
// Timestamp of Wrapped Comment
|
||||
long wTimestamp = body.getLong(1);
|
||||
if (wTimestamp < 0) throw new FormatException();
|
||||
|
||||
// Body of Wrapped Comment
|
||||
String comment = body.getOptionalString(2);
|
||||
checkLength(comment, 1, MAX_BLOG_POST_BODY_LENGTH);
|
||||
|
||||
// parent_original_id
|
||||
// c_parent_original_id
|
||||
// Taken from the original comment
|
||||
byte[] originalIdBytes = body.getRaw(3);
|
||||
checkLength(originalIdBytes, MessageId.LENGTH);
|
||||
MessageId originalId = new MessageId(originalIdBytes);
|
||||
byte[] pOriginalIdBytes = body.getRaw(3);
|
||||
checkLength(pOriginalIdBytes, MessageId.LENGTH);
|
||||
MessageId pOriginalId = new MessageId(pOriginalIdBytes);
|
||||
|
||||
// signature
|
||||
// c_parent_id
|
||||
// Taken from the original comment
|
||||
byte[] signature = body.getRaw(4);
|
||||
byte[] oldIdBytes = body.getRaw(4);
|
||||
checkLength(oldIdBytes, MessageId.LENGTH);
|
||||
MessageId oldId = new MessageId(oldIdBytes);
|
||||
|
||||
// c_signature
|
||||
// Taken from the original comment
|
||||
byte[] signature = body.getRaw(5);
|
||||
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
|
||||
|
||||
// parent_current_id
|
||||
// parent_id
|
||||
// The ID of a post, comment, wrapped post or wrapped comment in this
|
||||
// group, which had the ID parent_original_id in the group
|
||||
// group, which had the ID c_parent_original_id in the group
|
||||
// where it was originally posted
|
||||
byte[] currentIdBytes = body.getRaw(5);
|
||||
checkLength(currentIdBytes, MessageId.LENGTH);
|
||||
MessageId currentId = new MessageId(currentIdBytes);
|
||||
byte[] parentIdBytes = body.getRaw(6);
|
||||
checkLength(parentIdBytes, MessageId.LENGTH);
|
||||
MessageId parentId = new MessageId(parentIdBytes);
|
||||
|
||||
// Get and Validate the Wrapped Comment
|
||||
Group wGroup = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
BdfList wBodyList = BdfList.of(comment, originalId, signature,
|
||||
currentId);
|
||||
BdfList wBodyList = BdfList.of(COMMENT.getInt(), comment, pOriginalId,
|
||||
oldId, signature);
|
||||
byte[] wBody = clientHelper.toByteArray(wBodyList);
|
||||
Message wMessage =
|
||||
messageFactory.createMessage(wGroup.getId(), wTimestamp, wBody);
|
||||
wBodyList.remove(0);
|
||||
BdfMessageContext c = validateComment(wMessage, wGroup, wBodyList);
|
||||
|
||||
// Return the metadata and dependencies
|
||||
Collection<MessageId> dependencies = Collections.singleton(currentId);
|
||||
Collection<MessageId> dependencies = Collections.singleton(parentId);
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(KEY_ORIGINAL_MSG_ID, wMessage.getId());
|
||||
meta.put(KEY_CURRENT_MSG_ID, currentId);
|
||||
meta.put(KEY_ORIGINAL_PARENT_MSG_ID, pOriginalId);
|
||||
meta.put(KEY_PARENT_MSG_ID, parentId);
|
||||
meta.put(KEY_TIMESTAMP, wTimestamp);
|
||||
if (comment != null) meta.put(KEY_COMMENT, comment);
|
||||
meta.put(KEY_AUTHOR, c.getDictionary().getDictionary(KEY_AUTHOR));
|
||||
|
||||
@@ -50,9 +50,9 @@ public class BlogsModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
BlogPostFactory provideBlogPostFactory(CryptoComponent crypto,
|
||||
ClientHelper clientHelper) {
|
||||
return new BlogPostFactoryImpl(crypto, clientHelper);
|
||||
BlogPostFactory provideBlogPostFactory(ClientHelper clientHelper,
|
||||
Clock clock) {
|
||||
return new BlogPostFactoryImpl(clientHelper, clock);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -2,6 +2,10 @@ package org.briarproject.clients;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.KeyParser;
|
||||
import org.briarproject.api.crypto.PrivateKey;
|
||||
import org.briarproject.api.crypto.Signature;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.data.BdfReader;
|
||||
@@ -23,6 +27,7 @@ import org.briarproject.api.sync.MessageId;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -40,18 +45,20 @@ class ClientHelperImpl implements ClientHelper {
|
||||
private final BdfWriterFactory bdfWriterFactory;
|
||||
private final MetadataParser metadataParser;
|
||||
private final MetadataEncoder metadataEncoder;
|
||||
private final CryptoComponent cryptoComponent;
|
||||
|
||||
@Inject
|
||||
ClientHelperImpl(DatabaseComponent db, MessageFactory messageFactory,
|
||||
BdfReaderFactory bdfReaderFactory,
|
||||
BdfWriterFactory bdfWriterFactory, MetadataParser metadataParser,
|
||||
MetadataEncoder metadataEncoder) {
|
||||
MetadataEncoder metadataEncoder, CryptoComponent cryptoComponent) {
|
||||
this.db = db;
|
||||
this.messageFactory = messageFactory;
|
||||
this.bdfReaderFactory = bdfReaderFactory;
|
||||
this.bdfWriterFactory = bdfWriterFactory;
|
||||
this.metadataParser = metadataParser;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
this.cryptoComponent = cryptoComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -240,7 +247,7 @@ class ClientHelperImpl implements ClientHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageShared(Transaction txn, Message m, boolean shared)
|
||||
public void setMessageShared(Transaction txn, MessageId m, boolean shared)
|
||||
throws DbException {
|
||||
db.setMessageShared(txn, m, shared);
|
||||
}
|
||||
@@ -303,4 +310,21 @@ class ClientHelperImpl implements ClientHelper {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList toList(byte[] b) throws FormatException {
|
||||
return toList(b, 0, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign(BdfList toSign, byte[] privateKey)
|
||||
throws FormatException, GeneralSecurityException {
|
||||
Signature signature = cryptoComponent.getSignature();
|
||||
KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
|
||||
PrivateKey key =
|
||||
keyParser.parsePrivateKey(privateKey);
|
||||
signature.initSign(key);
|
||||
signature.update(toByteArray(toSign));
|
||||
return signature.sign();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,10 @@ public class ClientsModule {
|
||||
ClientHelper provideClientHelper(DatabaseComponent db,
|
||||
MessageFactory messageFactory, BdfReaderFactory bdfReaderFactory,
|
||||
BdfWriterFactory bdfWriterFactory, MetadataParser metadataParser,
|
||||
MetadataEncoder metadataEncoder) {
|
||||
MetadataEncoder metadataEncoder, CryptoComponent cryptoComponent) {
|
||||
return new ClientHelperImpl(db, messageFactory, bdfReaderFactory,
|
||||
bdfWriterFactory, metadataParser, metadataEncoder);
|
||||
bdfWriterFactory, metadataParser, metadataEncoder,
|
||||
cryptoComponent);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -199,7 +199,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
transaction.attach(new MessageAddedEvent(m, null));
|
||||
transaction.attach(new MessageStateChangedEvent(m, c, true,
|
||||
DELIVERED));
|
||||
if (shared) transaction.attach(new MessageSharedEvent(m));
|
||||
if (shared) transaction.attach(new MessageSharedEvent(m.getId()));
|
||||
}
|
||||
db.mergeMessageMetadata(txn, m.getId(), meta);
|
||||
}
|
||||
@@ -704,13 +704,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
transaction.attach(new ContactStatusChangedEvent(c, active));
|
||||
}
|
||||
|
||||
public void setMessageShared(Transaction transaction, Message m,
|
||||
public void setMessageShared(Transaction transaction, MessageId m,
|
||||
boolean shared) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m.getId()))
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
db.setMessageShared(txn, m.getId(), shared);
|
||||
db.setMessageShared(txn, m, shared);
|
||||
if (shared) transaction.attach(new MessageSharedEvent(m));
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.feed.Feed;
|
||||
import org.briarproject.api.feed.FeedManager;
|
||||
import org.briarproject.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.api.lifecycle.Service;
|
||||
import org.briarproject.api.lifecycle.ServiceException;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
@@ -41,7 +41,6 @@ import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
@@ -440,15 +439,14 @@ class FeedManagerImpl implements FeedManager, Service, Client {
|
||||
// get other information for post
|
||||
GroupId groupId = feed.getBlogId();
|
||||
long time = clock.currentTimeMillis();
|
||||
byte[] body = getPostBody(b.toString());
|
||||
String body = getPostBody(b.toString());
|
||||
try {
|
||||
// create and store post
|
||||
Blog blog = blogManager.getBlog(txn, groupId);
|
||||
AuthorId authorId = blog.getAuthor().getId();
|
||||
LocalAuthor author = identityManager.getLocalAuthor(txn, authorId);
|
||||
BlogPost post = blogPostFactory
|
||||
.createBlogPost(groupId, null, time, null, author,
|
||||
"text/plain", body);
|
||||
.createBlogPost(groupId, time, null, author, body);
|
||||
blogManager.addLocalPost(txn, post);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
@@ -472,10 +470,9 @@ class FeedManagerImpl implements FeedManager, Service, Client {
|
||||
return StringUtils.trim(s.replaceAll("<(?s).*?>", ""));
|
||||
}
|
||||
|
||||
private byte[] getPostBody(String text) {
|
||||
byte[] body = StringUtils.toUtf8(text);
|
||||
if (body.length <= MAX_BLOG_POST_BODY_LENGTH) return body;
|
||||
else return Arrays.copyOfRange(body, 0, MAX_BLOG_POST_BODY_LENGTH - 1);
|
||||
private String getPostBody(String text) {
|
||||
if (text.length() <= MAX_BLOG_POST_BODY_LENGTH) return text;
|
||||
else return text.substring(0, MAX_BLOG_POST_BODY_LENGTH);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,6 @@ import java.util.logging.Logger;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.KEY_AUTHOR;
|
||||
import static org.briarproject.api.forum.ForumConstants.KEY_CONTENT_TYPE;
|
||||
import static org.briarproject.api.forum.ForumConstants.KEY_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.KEY_LOCAL;
|
||||
import static org.briarproject.api.forum.ForumConstants.KEY_NAME;
|
||||
@@ -78,7 +77,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
||||
protected void incomingMessage(Transaction txn, Message m, BdfList body,
|
||||
BdfDictionary meta) throws DbException, FormatException {
|
||||
|
||||
clientHelper.setMessageShared(txn, m, true);
|
||||
clientHelper.setMessageShared(txn, m.getId(), true);
|
||||
|
||||
ForumPostHeader post = getForumPostHeader(txn, m.getId(), meta);
|
||||
ForumPostReceivedEvent event =
|
||||
@@ -132,7 +131,6 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
||||
authorMeta.put(KEY_PUBLIC_NAME, a.getPublicKey());
|
||||
meta.put(KEY_AUTHOR, authorMeta);
|
||||
}
|
||||
meta.put(KEY_CONTENT_TYPE, p.getContentType());
|
||||
meta.put(KEY_LOCAL, true);
|
||||
meta.put(KEY_READ, true);
|
||||
clientHelper.addLocalMessage(p.getMessage(), CLIENT_ID, meta, true);
|
||||
@@ -236,7 +234,7 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
||||
private Forum parseForum(Group g) throws FormatException {
|
||||
byte[] descriptor = g.getDescriptor();
|
||||
// Name, salt
|
||||
BdfList forum = clientHelper.toList(descriptor, 0, descriptor.length);
|
||||
BdfList forum = clientHelper.toList(descriptor);
|
||||
return new Forum(g, forum.getString(0), forum.getRaw(1));
|
||||
}
|
||||
|
||||
@@ -262,11 +260,10 @@ class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
|
||||
identityManager.getAuthorStatus(txn, author.getId());
|
||||
}
|
||||
}
|
||||
String contentType = meta.getString(KEY_CONTENT_TYPE);
|
||||
boolean read = meta.getBoolean(KEY_READ);
|
||||
|
||||
return new ForumPostHeader(id, parentId, timestamp, author,
|
||||
authorStatus, contentType, read);
|
||||
authorStatus, read);
|
||||
}
|
||||
|
||||
private ForumPostHeader getForumPostHeader(MessageId id,
|
||||
|
||||
@@ -44,7 +44,7 @@ class ForumPostFactoryImpl implements ForumPostFactory {
|
||||
// Serialise the message
|
||||
BdfList message = BdfList.of(parent, null, contentType, body, null);
|
||||
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
||||
return new ForumPost(m, parent, null, contentType);
|
||||
return new ForumPost(m, parent, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,6 +71,6 @@ class ForumPostFactoryImpl implements ForumPostFactory {
|
||||
BdfList message = BdfList.of(parent, authorList, contentType, body,
|
||||
sig);
|
||||
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
||||
return new ForumPost(m, parent, author, contentType);
|
||||
return new ForumPost(m, parent, author);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user