mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 20:59:54 +01:00
Merge branch '436-automatic-personal-blogs-backend' into 'master'
Backend for Automatic Micro Blogs This MR introduces automatic personal blogs to the Blog client. When a contact is added, her personal blog will also be added automatically. Also, when a new identity is added, a personal blog for that identity is created. The first commit changes the blog paradigm to short-form blogs and introduces other things that will be useful later in the UI (!214) such as a BlogPostAdded event and the possibility to delete/remove blogs (not the personal ones). This MR is based on !224 to prevent crashes that are fixed by it. So please review and merge !224 first. See merge request !223
This commit is contained in:
@@ -11,13 +11,16 @@ public class Blog extends Forum {
|
|||||||
private final String description;
|
private final String description;
|
||||||
@NotNull
|
@NotNull
|
||||||
private final Author author;
|
private final Author author;
|
||||||
|
private final boolean permanent;
|
||||||
|
|
||||||
public Blog(@NotNull Group group, @NotNull String name,
|
public Blog(@NotNull Group group, @NotNull String name,
|
||||||
@NotNull String description, @NotNull Author author) {
|
@NotNull String description, @NotNull Author author,
|
||||||
|
boolean permanent) {
|
||||||
super(group, name, null);
|
super(group, name, null);
|
||||||
|
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.author = author;
|
this.author = author;
|
||||||
|
this.permanent = permanent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -29,4 +32,8 @@ public class Blog extends Forum {
|
|||||||
public Author getAuthor() {
|
public Author getAuthor() {
|
||||||
return author;
|
return author;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPermanent() {
|
||||||
|
return permanent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ public interface BlogConstants {
|
|||||||
/** The length of a blog post's title in UTF-8 bytes. */
|
/** The length of a blog post's title in UTF-8 bytes. */
|
||||||
int MAX_BLOG_POST_TITLE_LENGTH = 100;
|
int MAX_BLOG_POST_TITLE_LENGTH = 100;
|
||||||
|
|
||||||
/** The length of a blog post's teaser in UTF-8 bytes. */
|
|
||||||
int MAX_BLOG_POST_TEASER_LENGTH = 240;
|
|
||||||
|
|
||||||
/** The maximum length of a blog post's body in bytes. */
|
/** The maximum length of a blog post's body in bytes. */
|
||||||
int MAX_BLOG_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
|
int MAX_BLOG_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
|
||||||
|
|
||||||
|
/** The internal name of personal blogs that are created automatically */
|
||||||
|
String PERSONAL_BLOG_NAME = "briar.PERSONAL_BLOG_NAME";
|
||||||
|
|
||||||
/* Blog Sharing Constants */
|
/* Blog Sharing Constants */
|
||||||
String BLOG_TITLE = "blogTitle";
|
String BLOG_TITLE = "blogTitle";
|
||||||
String BLOG_DESC = "blogDescription";
|
String BLOG_DESC = "blogDescription";
|
||||||
@@ -31,9 +31,8 @@ public interface BlogConstants {
|
|||||||
// Metadata keys
|
// Metadata keys
|
||||||
String KEY_DESCRIPTION = "description";
|
String KEY_DESCRIPTION = "description";
|
||||||
String KEY_TITLE = "title";
|
String KEY_TITLE = "title";
|
||||||
String KEY_TEASER = "teaser";
|
|
||||||
String KEY_HAS_BODY = "hasBody";
|
|
||||||
String KEY_TIMESTAMP = "timestamp";
|
String KEY_TIMESTAMP = "timestamp";
|
||||||
|
String KEY_TIME_RECEIVED = "timeReceived";
|
||||||
String KEY_PARENT = "parent";
|
String KEY_PARENT = "parent";
|
||||||
String KEY_AUTHOR_ID = "id";
|
String KEY_AUTHOR_ID = "id";
|
||||||
String KEY_AUTHOR_NAME = "name";
|
String KEY_AUTHOR_NAME = "name";
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.api.blogs;
|
package org.briarproject.api.blogs;
|
||||||
|
|
||||||
import org.briarproject.api.FormatException;
|
import org.briarproject.api.FormatException;
|
||||||
|
import org.briarproject.api.contact.Contact;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.sync.Group;
|
import org.briarproject.api.sync.Group;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -11,6 +12,9 @@ public interface BlogFactory {
|
|||||||
Blog createBlog(@NotNull String name, @NotNull String description,
|
Blog createBlog(@NotNull String name, @NotNull String description,
|
||||||
@NotNull Author author);
|
@NotNull Author author);
|
||||||
|
|
||||||
|
/** Creates a personal blog for a given author. */
|
||||||
|
Blog createPersonalBlog(@NotNull Author author);
|
||||||
|
|
||||||
/** Parses a blog with the given Group and description */
|
/** Parses a blog with the given Group and description */
|
||||||
Blog parseBlog(@NotNull Group g, @NotNull String description)
|
Blog parseBlog(@NotNull Group g, @NotNull String description)
|
||||||
throws FormatException;
|
throws FormatException;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package org.briarproject.api.blogs;
|
package org.briarproject.api.blogs;
|
||||||
|
|
||||||
import org.briarproject.api.FormatException;
|
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.Transaction;
|
import org.briarproject.api.db.Transaction;
|
||||||
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.identity.LocalAuthor;
|
import org.briarproject.api.identity.LocalAuthor;
|
||||||
import org.briarproject.api.sync.ClientId;
|
import org.briarproject.api.sync.ClientId;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
@@ -20,6 +20,9 @@ public interface BlogManager {
|
|||||||
Blog addBlog(LocalAuthor localAuthor, String name, String description)
|
Blog addBlog(LocalAuthor localAuthor, String name, String description)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/** Removes and deletes a blog. */
|
||||||
|
void removeBlog(Blog b) throws DbException;
|
||||||
|
|
||||||
/** Stores a local blog post. */
|
/** Stores a local blog post. */
|
||||||
void addLocalPost(BlogPost p) throws DbException;
|
void addLocalPost(BlogPost p) throws DbException;
|
||||||
|
|
||||||
@@ -29,9 +32,12 @@ public interface BlogManager {
|
|||||||
/** Returns the blog with the given ID. */
|
/** Returns the blog with the given ID. */
|
||||||
Blog getBlog(Transaction txn, GroupId g) throws DbException;
|
Blog getBlog(Transaction txn, GroupId g) throws DbException;
|
||||||
|
|
||||||
/** Returns all blogs to which the localAuthor created. */
|
/** Returns all blogs owned by the given localAuthor. */
|
||||||
Collection<Blog> getBlogs(LocalAuthor localAuthor) throws DbException;
|
Collection<Blog> getBlogs(LocalAuthor localAuthor) throws DbException;
|
||||||
|
|
||||||
|
/** Returns only the personal blog of the given author. */
|
||||||
|
Blog getPersonalBlog(Author author) throws DbException;
|
||||||
|
|
||||||
/** Returns all blogs to which the user subscribes. */
|
/** Returns all blogs to which the user subscribes. */
|
||||||
Collection<Blog> getBlogs() throws DbException;
|
Collection<Blog> getBlogs() throws DbException;
|
||||||
|
|
||||||
@@ -45,4 +51,11 @@ public interface BlogManager {
|
|||||||
/** Marks a blog post as read or unread. */
|
/** Marks a blog post as read or unread. */
|
||||||
void setReadFlag(MessageId m, boolean read) throws DbException;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,27 @@
|
|||||||
package org.briarproject.api.blogs;
|
package org.briarproject.api.blogs;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import org.briarproject.api.forum.ForumPost;
|
import org.briarproject.api.forum.ForumPost;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.sync.Message;
|
import org.briarproject.api.sync.Message;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class BlogPost extends ForumPost {
|
public class BlogPost extends ForumPost {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String title;
|
private final String title;
|
||||||
@NotNull
|
|
||||||
private final String teaser;
|
|
||||||
private final boolean hasBody;
|
|
||||||
|
|
||||||
public BlogPost(@Nullable String title, @NotNull String teaser,
|
public BlogPost(@Nullable String title, @NotNull Message message,
|
||||||
boolean hasBody, @NotNull Message message,
|
@Nullable MessageId parent, @NotNull Author author,
|
||||||
@Nullable MessageId parent, @NotNull Author author,
|
|
||||||
@NotNull String contentType) {
|
@NotNull String contentType) {
|
||||||
super(message, parent, author, contentType);
|
super(message, parent, author, contentType);
|
||||||
|
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.teaser = teaser;
|
|
||||||
this.hasBody = hasBody;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getTitle() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getTeaser() {
|
|
||||||
return teaser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasBody() {
|
|
||||||
return hasBody;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import java.security.GeneralSecurityException;
|
|||||||
public interface BlogPostFactory {
|
public interface BlogPostFactory {
|
||||||
|
|
||||||
BlogPost createBlogPost(@NotNull GroupId groupId, @Nullable String title,
|
BlogPost createBlogPost(@NotNull GroupId groupId, @Nullable String title,
|
||||||
@NotNull String teaser, long timestamp, @Nullable MessageId parent,
|
long timestamp, @Nullable MessageId parent,
|
||||||
@NotNull LocalAuthor author, @NotNull String contentType,
|
@NotNull LocalAuthor author, @NotNull String contentType,
|
||||||
@Nullable byte[] body)
|
@NotNull byte[] body)
|
||||||
throws FormatException, GeneralSecurityException;
|
throws FormatException, GeneralSecurityException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,20 +11,16 @@ public class BlogPostHeader extends PostHeader {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String title;
|
private final String title;
|
||||||
@NotNull
|
private final long timeReceived;
|
||||||
private final String teaser;
|
|
||||||
private final boolean hasBody;
|
|
||||||
|
|
||||||
public BlogPostHeader(@Nullable String title, @NotNull String teaser,
|
public BlogPostHeader(@Nullable String title, @NotNull MessageId id,
|
||||||
boolean hasBody, @NotNull MessageId id,
|
@Nullable MessageId parentId, long timestamp, long timeReceived,
|
||||||
@Nullable MessageId parentId, long timestamp,
|
|
||||||
@NotNull Author author, @NotNull Status authorStatus,
|
@NotNull Author author, @NotNull Status authorStatus,
|
||||||
@NotNull String contentType, boolean read) {
|
@NotNull String contentType, boolean read) {
|
||||||
super(id, parentId, timestamp, author, authorStatus, contentType, read);
|
super(id, parentId, timestamp, author, authorStatus, contentType, read);
|
||||||
|
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.teaser = teaser;
|
this.timeReceived = timeReceived;
|
||||||
this.hasBody = hasBody;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -32,12 +28,8 @@ public class BlogPostHeader extends PostHeader {
|
|||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
public long getTimeReceived() {
|
||||||
public String getTeaser() {
|
return timeReceived;
|
||||||
return teaser;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasBody() {
|
|
||||||
return hasBody;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.briarproject.api.event;
|
||||||
|
|
||||||
|
import org.briarproject.api.blogs.BlogPostHeader;
|
||||||
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
|
||||||
|
/** An event that is broadcast when a blog post was added to the database. */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,9 @@ public interface IdentityManager {
|
|||||||
/** Returns the trust-level status of the author */
|
/** Returns the trust-level status of the author */
|
||||||
Status getAuthorStatus(AuthorId a) throws DbException;
|
Status getAuthorStatus(AuthorId a) throws DbException;
|
||||||
|
|
||||||
|
/** Returns the trust-level status of the author */
|
||||||
|
Status getAuthorStatus(Transaction txn, AuthorId a) throws DbException;
|
||||||
|
|
||||||
interface AddIdentityHook {
|
interface AddIdentityHook {
|
||||||
void addingIdentity(Transaction txn, LocalAuthor a) throws DbException;
|
void addingIdentity(Transaction txn, LocalAuthor a) throws DbException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import org.briarproject.api.FormatException;
|
|||||||
import org.briarproject.api.blogs.Blog;
|
import org.briarproject.api.blogs.Blog;
|
||||||
import org.briarproject.api.blogs.BlogFactory;
|
import org.briarproject.api.blogs.BlogFactory;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
|
import org.briarproject.api.contact.Contact;
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.identity.AuthorFactory;
|
import org.briarproject.api.identity.AuthorFactory;
|
||||||
@@ -13,6 +14,8 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.api.blogs.BlogConstants.PERSONAL_BLOG_NAME;
|
||||||
|
|
||||||
class BlogFactoryImpl implements BlogFactory {
|
class BlogFactoryImpl implements BlogFactory {
|
||||||
|
|
||||||
private final GroupFactory groupFactory;
|
private final GroupFactory groupFactory;
|
||||||
@@ -31,6 +34,16 @@ class BlogFactoryImpl implements BlogFactory {
|
|||||||
@Override
|
@Override
|
||||||
public Blog createBlog(@NotNull String name, @NotNull String description,
|
public Blog createBlog(@NotNull String name, @NotNull String description,
|
||||||
@NotNull Author author) {
|
@NotNull Author author) {
|
||||||
|
return createBlog(name, description, author, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Blog createPersonalBlog(@NotNull Author a) {
|
||||||
|
return createBlog(PERSONAL_BLOG_NAME, "", a, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Blog createBlog(@NotNull String name, @NotNull String description,
|
||||||
|
@NotNull Author author, boolean permanent) {
|
||||||
try {
|
try {
|
||||||
BdfList blog = BdfList.of(
|
BdfList blog = BdfList.of(
|
||||||
name,
|
name,
|
||||||
@@ -40,7 +53,7 @@ class BlogFactoryImpl implements BlogFactory {
|
|||||||
byte[] descriptor = clientHelper.toByteArray(blog);
|
byte[] descriptor = clientHelper.toByteArray(blog);
|
||||||
Group g = groupFactory
|
Group g = groupFactory
|
||||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||||
return new Blog(g, name, description, author);
|
return new Blog(g, name, description, author, permanent);
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -55,7 +68,9 @@ class BlogFactoryImpl implements BlogFactory {
|
|||||||
BdfList blog = clientHelper.toList(descriptor, 0, descriptor.length);
|
BdfList blog = clientHelper.toList(descriptor, 0, descriptor.length);
|
||||||
Author a =
|
Author a =
|
||||||
authorFactory.createAuthor(blog.getString(1), blog.getRaw(2));
|
authorFactory.createAuthor(blog.getString(1), blog.getRaw(2));
|
||||||
return new Blog(g, blog.getString(0), description, a);
|
// TODO change permanent depending on how this will be used
|
||||||
|
boolean permanent = false;
|
||||||
|
return new Blog(g, blog.getString(0), description, a, permanent);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,22 +6,31 @@ import org.briarproject.api.blogs.BlogFactory;
|
|||||||
import org.briarproject.api.blogs.BlogManager;
|
import org.briarproject.api.blogs.BlogManager;
|
||||||
import org.briarproject.api.blogs.BlogPost;
|
import org.briarproject.api.blogs.BlogPost;
|
||||||
import org.briarproject.api.blogs.BlogPostHeader;
|
import org.briarproject.api.blogs.BlogPostHeader;
|
||||||
|
import org.briarproject.api.clients.Client;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
|
import org.briarproject.api.contact.Contact;
|
||||||
|
import org.briarproject.api.contact.ContactId;
|
||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
import org.briarproject.api.data.BdfEntry;
|
import org.briarproject.api.data.BdfEntry;
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
|
import org.briarproject.api.data.MetadataParser;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
import org.briarproject.api.db.DatabaseComponent;
|
||||||
import org.briarproject.api.db.DbException;
|
import org.briarproject.api.db.DbException;
|
||||||
import org.briarproject.api.db.Transaction;
|
import org.briarproject.api.db.Transaction;
|
||||||
|
import org.briarproject.api.event.BlogPostAddedEvent;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
import org.briarproject.api.identity.Author.Status;
|
import org.briarproject.api.identity.Author.Status;
|
||||||
import org.briarproject.api.identity.AuthorId;
|
import org.briarproject.api.identity.AuthorId;
|
||||||
import org.briarproject.api.identity.IdentityManager;
|
import org.briarproject.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.api.identity.IdentityManager.AddIdentityHook;
|
||||||
|
import org.briarproject.api.identity.IdentityManager.RemoveIdentityHook;
|
||||||
import org.briarproject.api.identity.LocalAuthor;
|
import org.briarproject.api.identity.LocalAuthor;
|
||||||
import org.briarproject.api.sync.ClientId;
|
import org.briarproject.api.sync.ClientId;
|
||||||
import org.briarproject.api.sync.Group;
|
import org.briarproject.api.sync.Group;
|
||||||
import org.briarproject.api.sync.GroupId;
|
import org.briarproject.api.sync.GroupId;
|
||||||
|
import org.briarproject.api.sync.Message;
|
||||||
import org.briarproject.api.sync.MessageId;
|
import org.briarproject.api.sync.MessageId;
|
||||||
|
import org.briarproject.clients.BdfIncomingMessageHook;
|
||||||
import org.briarproject.util.StringUtils;
|
import org.briarproject.util.StringUtils;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -31,24 +40,29 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
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;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
|
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_AUTHOR_NAME;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
|
import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_DESCRIPTION;
|
import static org.briarproject.api.blogs.BlogConstants.KEY_DESCRIPTION;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_HAS_BODY;
|
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
|
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
|
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_READ;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TEASER;
|
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
|
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_TITLE;
|
||||||
|
import static org.briarproject.api.contact.ContactManager.AddContactHook;
|
||||||
|
import static org.briarproject.api.contact.ContactManager.RemoveContactHook;
|
||||||
|
|
||||||
class BlogManagerImpl implements BlogManager {
|
class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
|
||||||
|
AddContactHook, RemoveContactHook, Client,
|
||||||
|
AddIdentityHook, RemoveIdentityHook {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(BlogManagerImpl.class.getName());
|
Logger.getLogger(BlogManagerImpl.class.getName());
|
||||||
@@ -59,17 +73,19 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
private final IdentityManager identityManager;
|
private final IdentityManager identityManager;
|
||||||
private final ClientHelper clientHelper;
|
|
||||||
private final BlogFactory blogFactory;
|
private final BlogFactory blogFactory;
|
||||||
|
private final List<RemoveBlogHook> removeHooks;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager,
|
BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager,
|
||||||
ClientHelper clientHelper, BlogFactory blogFactory) {
|
ClientHelper clientHelper, MetadataParser metadataParser,
|
||||||
|
BlogFactory blogFactory) {
|
||||||
|
super(clientHelper, metadataParser);
|
||||||
|
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.identityManager = identityManager;
|
this.identityManager = identityManager;
|
||||||
this.clientHelper = clientHelper;
|
|
||||||
this.blogFactory = blogFactory;
|
this.blogFactory = blogFactory;
|
||||||
|
removeHooks = new CopyOnWriteArrayList<RemoveBlogHook>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -77,6 +93,79 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
return CLIENT_ID;
|
return CLIENT_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createLocalState(Transaction txn) throws DbException {
|
||||||
|
// Ensure every identity does have its own personal blog
|
||||||
|
// TODO this can probably be removed once #446 is resolved and all users migrated to a new version
|
||||||
|
for (LocalAuthor a : db.getLocalAuthors(txn)) {
|
||||||
|
Blog b = blogFactory.createPersonalBlog(a);
|
||||||
|
Group g = b.getGroup();
|
||||||
|
if (!db.containsGroup(txn, g.getId())) {
|
||||||
|
db.addGroup(txn, g);
|
||||||
|
for (ContactId c : db.getContacts(txn, a.getId())) {
|
||||||
|
db.setVisibleToContact(txn, c, g.getId(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Ensure that we have the personal blogs of all pre-existing contacts
|
||||||
|
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addingContact(Transaction txn, Contact c) throws DbException {
|
||||||
|
// get personal blog of the contact
|
||||||
|
Blog b = blogFactory.createPersonalBlog(c.getAuthor());
|
||||||
|
Group g = b.getGroup();
|
||||||
|
if (!db.containsGroup(txn, g.getId())) {
|
||||||
|
// add the personal blog of the contact
|
||||||
|
db.addGroup(txn, g);
|
||||||
|
db.setVisibleToContact(txn, c.getId(), g.getId(), true);
|
||||||
|
|
||||||
|
// share our personal blog with the new contact
|
||||||
|
LocalAuthor a = db.getLocalAuthor(txn, c.getLocalAuthorId());
|
||||||
|
Blog b2 = blogFactory.createPersonalBlog(a);
|
||||||
|
db.setVisibleToContact(txn, c.getId(), b2.getId(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removingContact(Transaction txn, Contact c) throws DbException {
|
||||||
|
if (c != null) {
|
||||||
|
Blog b = blogFactory.createPersonalBlog(c.getAuthor());
|
||||||
|
db.removeGroup(txn, b.getGroup());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addingIdentity(Transaction txn, LocalAuthor a)
|
||||||
|
throws DbException {
|
||||||
|
|
||||||
|
// add a personal blog for the new identity
|
||||||
|
LOG.info("New Personal Blog Added.");
|
||||||
|
Blog b = blogFactory.createPersonalBlog(a);
|
||||||
|
db.addGroup(txn, b.getGroup());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removingIdentity(Transaction txn, LocalAuthor a)
|
||||||
|
throws DbException {
|
||||||
|
|
||||||
|
// remove the personal blog of that identity
|
||||||
|
Blog b = blogFactory.createPersonalBlog(a);
|
||||||
|
db.removeGroup(txn, b.getGroup());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void incomingMessage(Transaction txn, Message m, BdfList list,
|
||||||
|
BdfDictionary meta) throws DbException, FormatException {
|
||||||
|
|
||||||
|
GroupId groupId = m.getGroupId();
|
||||||
|
BlogPostHeader h = getPostHeaderFromMetadata(txn, m.getId(), meta);
|
||||||
|
BlogPostAddedEvent event =
|
||||||
|
new BlogPostAddedEvent(groupId, h, false);
|
||||||
|
txn.attach(event);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Blog addBlog(LocalAuthor localAuthor, String name,
|
public Blog addBlog(LocalAuthor localAuthor, String name,
|
||||||
String description) throws DbException {
|
String description) throws DbException {
|
||||||
@@ -101,13 +190,25 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addLocalPost(BlogPost p) throws DbException {
|
public void removeBlog(Blog b) throws DbException {
|
||||||
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
BdfDictionary meta = new BdfDictionary();
|
for (RemoveBlogHook hook : removeHooks)
|
||||||
|
hook.removingBlog(txn, b);
|
||||||
|
db.removeGroup(txn, b.getGroup());
|
||||||
|
txn.setComplete();
|
||||||
|
} finally {
|
||||||
|
db.endTransaction(txn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLocalPost(BlogPost p) throws DbException {
|
||||||
|
BdfDictionary meta;
|
||||||
|
try {
|
||||||
|
meta = new BdfDictionary();
|
||||||
if (p.getTitle() != null) meta.put(KEY_TITLE, p.getTitle());
|
if (p.getTitle() != null) meta.put(KEY_TITLE, p.getTitle());
|
||||||
meta.put(KEY_TEASER, p.getTeaser());
|
|
||||||
meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
|
meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
|
||||||
meta.put(KEY_HAS_BODY, p.hasBody());
|
|
||||||
if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
|
if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
|
||||||
|
|
||||||
Author a = p.getAuthor();
|
Author a = p.getAuthor();
|
||||||
@@ -123,6 +224,22 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// broadcast event about new post
|
||||||
|
Transaction txn = db.startTransaction(true);
|
||||||
|
try {
|
||||||
|
GroupId groupId = p.getMessage().getGroupId();
|
||||||
|
MessageId postId = p.getMessage().getId();
|
||||||
|
BlogPostHeader h = getPostHeaderFromMetadata(txn, postId, meta);
|
||||||
|
BlogPostAddedEvent event =
|
||||||
|
new BlogPostAddedEvent(groupId, h, true);
|
||||||
|
txn.attach(event);
|
||||||
|
txn.setComplete();
|
||||||
|
} catch (FormatException e) {
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
|
} finally {
|
||||||
|
db.endTransaction(txn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -163,6 +280,11 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
return Collections.unmodifiableList(blogs);
|
return Collections.unmodifiableList(blogs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Blog getPersonalBlog(Author author) throws DbException {
|
||||||
|
return blogFactory.createPersonalBlog(author);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Blog> getBlogs() throws DbException {
|
public Collection<Blog> getBlogs() throws DbException {
|
||||||
try {
|
try {
|
||||||
@@ -189,16 +311,20 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public byte[] getPostBody(MessageId m) throws DbException {
|
public byte[] getPostBody(MessageId m) throws DbException {
|
||||||
try {
|
try {
|
||||||
// content, signature
|
|
||||||
// content: parent, contentType, title, teaser, body, attachments
|
|
||||||
BdfList message = clientHelper.getMessageAsList(m);
|
BdfList message = clientHelper.getMessageAsList(m);
|
||||||
BdfList content = message.getList(0);
|
return getPostBody(message);
|
||||||
return content.getRaw(4);
|
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private byte[] getPostBody(BdfList message) throws FormatException {
|
||||||
|
// content, signature
|
||||||
|
// content: parent, contentType, title, body, attachments
|
||||||
|
BdfList content = message.getList(0);
|
||||||
|
return content.getRaw(3);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<BlogPostHeader> getPostHeaders(GroupId g)
|
public Collection<BlogPostHeader> getPostHeaders(GroupId g)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
@@ -213,26 +339,9 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
||||||
try {
|
try {
|
||||||
BdfDictionary meta = entry.getValue();
|
BdfDictionary meta = entry.getValue();
|
||||||
String title = meta.getOptionalString(KEY_TITLE);
|
BlogPostHeader h =
|
||||||
String teaser = meta.getString(KEY_TEASER);
|
getPostHeaderFromMetadata(null, entry.getKey(), meta);
|
||||||
boolean hasBody = meta.getBoolean(KEY_HAS_BODY);
|
headers.add(h);
|
||||||
long timestamp = meta.getLong(KEY_TIMESTAMP);
|
|
||||||
MessageId parentId = null;
|
|
||||||
if (meta.containsKey(KEY_PARENT))
|
|
||||||
parentId = new MessageId(meta.getRaw(KEY_PARENT));
|
|
||||||
|
|
||||||
BdfDictionary d = meta.getDictionary(KEY_AUTHOR);
|
|
||||||
AuthorId authorId = new AuthorId(d.getRaw(KEY_AUTHOR_ID));
|
|
||||||
String name = d.getString(KEY_AUTHOR_NAME);
|
|
||||||
byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
|
|
||||||
Author author = new Author(authorId, name, publicKey);
|
|
||||||
Status authorStatus = identityManager.getAuthorStatus(authorId);
|
|
||||||
|
|
||||||
String contentType = meta.getString(KEY_CONTENT_TYPE);
|
|
||||||
boolean read = meta.getBoolean(KEY_READ);
|
|
||||||
headers.add(new BlogPostHeader(title, teaser, hasBody,
|
|
||||||
entry.getKey(), parentId, timestamp, author,
|
|
||||||
authorStatus, contentType, read));
|
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
throw new DbException(e);
|
throw new DbException(e);
|
||||||
}
|
}
|
||||||
@@ -251,10 +360,43 @@ class BlogManagerImpl implements BlogManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerRemoveBlogHook(RemoveBlogHook hook) {
|
||||||
|
removeHooks.add(hook);
|
||||||
|
}
|
||||||
|
|
||||||
private String getBlogDescription(Transaction txn, GroupId g)
|
private String getBlogDescription(Transaction txn, GroupId g)
|
||||||
throws DbException, FormatException {
|
throws DbException, FormatException {
|
||||||
BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
|
BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
|
||||||
return d.getString(KEY_DESCRIPTION);
|
return d.getString(KEY_DESCRIPTION, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BlogPostHeader getPostHeaderFromMetadata(@Nullable Transaction txn,
|
||||||
|
MessageId id, BdfDictionary meta)
|
||||||
|
throws DbException, FormatException {
|
||||||
|
|
||||||
|
String title = meta.getOptionalString(KEY_TITLE);
|
||||||
|
long timestamp = meta.getLong(KEY_TIMESTAMP);
|
||||||
|
long timeReceived = meta.getLong(KEY_TIME_RECEIVED, timestamp);
|
||||||
|
MessageId parentId = null;
|
||||||
|
if (meta.containsKey(KEY_PARENT))
|
||||||
|
parentId = new MessageId(meta.getRaw(KEY_PARENT));
|
||||||
|
|
||||||
|
BdfDictionary d = meta.getDictionary(KEY_AUTHOR);
|
||||||
|
AuthorId authorId = new AuthorId(d.getRaw(KEY_AUTHOR_ID));
|
||||||
|
String name = d.getString(KEY_AUTHOR_NAME);
|
||||||
|
byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
|
||||||
|
Author author = new Author(authorId, name, publicKey);
|
||||||
|
Status authorStatus;
|
||||||
|
if (txn == null)
|
||||||
|
authorStatus = identityManager.getAuthorStatus(authorId);
|
||||||
|
else {
|
||||||
|
authorStatus = identityManager.getAuthorStatus(txn, authorId);
|
||||||
|
}
|
||||||
|
|
||||||
|
String contentType = meta.getString(KEY_CONTENT_TYPE);
|
||||||
|
boolean read = meta.getBoolean(KEY_READ);
|
||||||
|
return new BlogPostHeader(title, id, parentId, timestamp, timeReceived,
|
||||||
|
author, authorStatus, contentType, read);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import java.security.GeneralSecurityException;
|
|||||||
import javax.inject.Inject;
|
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_BODY_LENGTH;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TEASER_LENGTH;
|
|
||||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_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.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||||
|
|
||||||
@@ -39,25 +38,22 @@ class BlogPostFactoryImpl implements BlogPostFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlogPost createBlogPost(@NotNull GroupId groupId,
|
public BlogPost createBlogPost(@NotNull GroupId groupId,
|
||||||
@Nullable String title, @NotNull String teaser, long timestamp,
|
@Nullable String title, long timestamp,
|
||||||
@Nullable MessageId parent, @NotNull LocalAuthor author,
|
@Nullable MessageId parent, @NotNull LocalAuthor author,
|
||||||
@NotNull String contentType, @Nullable byte[] body)
|
@NotNull String contentType, @NotNull byte[] body)
|
||||||
throws FormatException, GeneralSecurityException {
|
throws FormatException, GeneralSecurityException {
|
||||||
|
|
||||||
// Validate the arguments
|
// Validate the arguments
|
||||||
if (title != null &&
|
if (title != null &&
|
||||||
StringUtils.toUtf8(title).length > MAX_BLOG_POST_TITLE_LENGTH)
|
StringUtils.toUtf8(title).length > MAX_BLOG_POST_TITLE_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (StringUtils.toUtf8(teaser).length > MAX_BLOG_POST_TEASER_LENGTH)
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH)
|
if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
if (body != null && body.length > MAX_BLOG_POST_BODY_LENGTH)
|
if (body.length > MAX_BLOG_POST_BODY_LENGTH)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
|
|
||||||
// Serialise the data to be signed
|
// Serialise the data to be signed
|
||||||
BdfList content =
|
BdfList content = BdfList.of(parent, contentType, title, body, null);
|
||||||
BdfList.of(parent, contentType, title, teaser, body, null);
|
|
||||||
BdfList signed = BdfList.of(groupId, timestamp, content);
|
BdfList signed = BdfList.of(groupId, timestamp, content);
|
||||||
|
|
||||||
// Generate the signature
|
// Generate the signature
|
||||||
@@ -72,7 +68,6 @@ class BlogPostFactoryImpl implements BlogPostFactory {
|
|||||||
// Serialise the signed message
|
// Serialise the signed message
|
||||||
BdfList message = BdfList.of(content, sig);
|
BdfList message = BdfList.of(content, sig);
|
||||||
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
||||||
return new BlogPost(title, teaser, body != null, m, parent, author,
|
return new BlogPost(title, m, parent, author, contentType);
|
||||||
contentType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.briarproject.api.crypto.KeyParser;
|
|||||||
import org.briarproject.api.crypto.PublicKey;
|
import org.briarproject.api.crypto.PublicKey;
|
||||||
import org.briarproject.api.crypto.Signature;
|
import org.briarproject.api.crypto.Signature;
|
||||||
import org.briarproject.api.data.BdfDictionary;
|
import org.briarproject.api.data.BdfDictionary;
|
||||||
|
import org.briarproject.api.data.BdfEntry;
|
||||||
import org.briarproject.api.data.BdfList;
|
import org.briarproject.api.data.BdfList;
|
||||||
import org.briarproject.api.data.MetadataEncoder;
|
import org.briarproject.api.data.MetadataEncoder;
|
||||||
import org.briarproject.api.identity.Author;
|
import org.briarproject.api.identity.Author;
|
||||||
@@ -25,15 +26,17 @@ import java.security.GeneralSecurityException;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
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_CONTENT_TYPE;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_HAS_BODY;
|
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
|
import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
|
||||||
|
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_READ;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TEASER;
|
|
||||||
import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
|
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_TITLE;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
|
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
|
||||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TEASER_LENGTH;
|
|
||||||
import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_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.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||||
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||||
@@ -60,37 +63,36 @@ class BlogPostValidator extends BdfMessageValidator {
|
|||||||
checkSize(body, 2);
|
checkSize(body, 2);
|
||||||
BdfList content = body.getList(0);
|
BdfList content = body.getList(0);
|
||||||
|
|
||||||
// Content: Parent ID, content type, title (optional), teaser,
|
// Content: Parent ID, content type, title (optional), post body,
|
||||||
// post body (optional), attachments (optional)
|
// attachments (optional)
|
||||||
checkSize(body, 6);
|
checkSize(content, 5);
|
||||||
// Parent ID is optional
|
// Parent ID is optional
|
||||||
byte[] parent = content.getOptionalRaw(0);
|
byte[] parent = content.getOptionalRaw(0);
|
||||||
checkLength(parent, UniqueId.LENGTH);
|
checkLength(parent, UniqueId.LENGTH);
|
||||||
// Content type
|
// Content type
|
||||||
String contentType = content.getString(1);
|
String contentType = content.getString(1);
|
||||||
checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
|
checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
|
||||||
|
if (!contentType.equals("text/plain"))
|
||||||
|
throw new InvalidMessageException("Invalid content type");
|
||||||
// Blog post title is optional
|
// Blog post title is optional
|
||||||
String title = content.getOptionalString(2);
|
String title = content.getOptionalString(2);
|
||||||
checkLength(contentType, 0, MAX_BLOG_POST_TITLE_LENGTH);
|
checkLength(contentType, 0, MAX_BLOG_POST_TITLE_LENGTH);
|
||||||
// Blog teaser
|
// Blog post body
|
||||||
String teaser = content.getString(3);
|
byte[] postBody = content.getRaw(3);
|
||||||
// TODO make sure that there is only text in the teaser
|
|
||||||
checkLength(contentType, 0, MAX_BLOG_POST_TEASER_LENGTH);
|
|
||||||
// Blog post body is optional
|
|
||||||
byte[] postBody = content.getOptionalRaw(4);
|
|
||||||
checkLength(postBody, 0, MAX_BLOG_POST_BODY_LENGTH);
|
checkLength(postBody, 0, MAX_BLOG_POST_BODY_LENGTH);
|
||||||
// Attachments
|
// Attachments
|
||||||
BdfDictionary attachments = content.getOptionalDictionary(5);
|
BdfDictionary attachments = content.getOptionalDictionary(4);
|
||||||
// TODO handle attachments somehow
|
// TODO handle attachments somehow
|
||||||
|
|
||||||
// Signature
|
// Signature
|
||||||
byte[] sig = body.getRaw(1);
|
byte[] sig = body.getRaw(1);
|
||||||
checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
|
checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
|
||||||
// Verify the signature
|
// Verify the signature
|
||||||
|
Author a;
|
||||||
try {
|
try {
|
||||||
// Get the blog author
|
// Get the blog author
|
||||||
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
|
Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
|
||||||
Author a = b.getAuthor();
|
a = b.getAuthor();
|
||||||
// Parse the public key
|
// Parse the public key
|
||||||
KeyParser keyParser = crypto.getSignatureKeyParser();
|
KeyParser keyParser = crypto.getSignatureKeyParser();
|
||||||
PublicKey key = keyParser.parsePublicKey(a.getPublicKey());
|
PublicKey key = keyParser.parsePublicKey(a.getPublicKey());
|
||||||
@@ -111,9 +113,14 @@ class BlogPostValidator extends BdfMessageValidator {
|
|||||||
BdfDictionary meta = new BdfDictionary();
|
BdfDictionary meta = new BdfDictionary();
|
||||||
Collection<MessageId> dependencies = null;
|
Collection<MessageId> dependencies = null;
|
||||||
if (title != null) meta.put(KEY_TITLE, title);
|
if (title != null) meta.put(KEY_TITLE, title);
|
||||||
meta.put(KEY_TEASER, teaser);
|
BdfDictionary author = BdfDictionary.of(
|
||||||
meta.put(KEY_HAS_BODY, postBody != null);
|
new BdfEntry(KEY_AUTHOR_ID, a.getId()),
|
||||||
|
new BdfEntry(KEY_AUTHOR_NAME, a.getName()),
|
||||||
|
new BdfEntry(KEY_PUBLIC_KEY, a.getPublicKey())
|
||||||
|
);
|
||||||
|
meta.put(KEY_AUTHOR, author);
|
||||||
meta.put(KEY_TIMESTAMP, m.getTimestamp());
|
meta.put(KEY_TIMESTAMP, m.getTimestamp());
|
||||||
|
meta.put(KEY_TIME_RECEIVED, clock.currentTimeMillis());
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
meta.put(KEY_PARENT, parent);
|
meta.put(KEY_PARENT, parent);
|
||||||
dependencies = Collections.singletonList(new MessageId(parent));
|
dependencies = Collections.singletonList(new MessageId(parent));
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ import org.briarproject.api.blogs.BlogFactory;
|
|||||||
import org.briarproject.api.blogs.BlogManager;
|
import org.briarproject.api.blogs.BlogManager;
|
||||||
import org.briarproject.api.blogs.BlogPostFactory;
|
import org.briarproject.api.blogs.BlogPostFactory;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
|
import org.briarproject.api.contact.ContactManager;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.api.data.MetadataEncoder;
|
import org.briarproject.api.data.MetadataEncoder;
|
||||||
import org.briarproject.api.db.DatabaseComponent;
|
|
||||||
import org.briarproject.api.identity.AuthorFactory;
|
import org.briarproject.api.identity.AuthorFactory;
|
||||||
import org.briarproject.api.identity.IdentityManager;
|
import org.briarproject.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.api.sync.GroupFactory;
|
import org.briarproject.api.sync.GroupFactory;
|
||||||
import org.briarproject.api.sync.ValidationManager;
|
import org.briarproject.api.sync.ValidationManager;
|
||||||
import org.briarproject.api.system.Clock;
|
import org.briarproject.api.system.Clock;
|
||||||
@@ -19,17 +20,31 @@ import javax.inject.Singleton;
|
|||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
|
import static org.briarproject.blogs.BlogManagerImpl.CLIENT_ID;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class BlogsModule {
|
public class BlogsModule {
|
||||||
|
|
||||||
public static class EagerSingletons {
|
public static class EagerSingletons {
|
||||||
@Inject
|
@Inject
|
||||||
BlogPostValidator blogPostValidator;
|
BlogPostValidator blogPostValidator;
|
||||||
|
@Inject
|
||||||
|
BlogManager blogManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
BlogManager provideBlogManager(BlogManagerImpl blogManager) {
|
BlogManager provideBlogManager(BlogManagerImpl blogManager,
|
||||||
|
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||||
|
IdentityManager identityManager,
|
||||||
|
ValidationManager validationManager) {
|
||||||
|
|
||||||
|
lifecycleManager.registerClient(blogManager);
|
||||||
|
contactManager.registerAddContactHook(blogManager);
|
||||||
|
contactManager.registerRemoveContactHook(blogManager);
|
||||||
|
identityManager.registerAddIdentityHook(blogManager);
|
||||||
|
identityManager.registerRemoveIdentityHook(blogManager);
|
||||||
|
validationManager.registerIncomingMessageHook(CLIENT_ID, blogManager);
|
||||||
return blogManager;
|
return blogManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,8 +69,7 @@ public class BlogsModule {
|
|||||||
|
|
||||||
BlogPostValidator validator = new BlogPostValidator(crypto,
|
BlogPostValidator validator = new BlogPostValidator(crypto,
|
||||||
blogFactory, clientHelper, metadataEncoder, clock);
|
blogFactory, clientHelper, metadataEncoder, clock);
|
||||||
validationManager.registerMessageValidator(
|
validationManager.registerMessageValidator(CLIENT_ID, validator);
|
||||||
BlogManagerImpl.CLIENT_ID, validator);
|
|
||||||
|
|
||||||
return validator;
|
return validator;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,17 +99,24 @@ class IdentityManagerImpl implements IdentityManager {
|
|||||||
public Status getAuthorStatus(AuthorId authorId) throws DbException {
|
public Status getAuthorStatus(AuthorId authorId) throws DbException {
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
// Compare to the IDs of the user's identities
|
return getAuthorStatus(txn, authorId);
|
||||||
for (LocalAuthor a : db.getLocalAuthors(txn))
|
|
||||||
if (a.getId().equals(authorId)) return VERIFIED;
|
|
||||||
// Compare to the IDs of contacts' identities
|
|
||||||
for (Contact c : db.getContacts(txn))
|
|
||||||
if (c.getAuthor().getId().equals(authorId)) return VERIFIED;
|
|
||||||
|
|
||||||
// TODO also handle UNVERIFIED when #261 is implemented
|
|
||||||
return UNKNOWN;
|
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Status getAuthorStatus(Transaction txn, AuthorId authorId)
|
||||||
|
throws DbException {
|
||||||
|
// Compare to the IDs of the user's identities
|
||||||
|
for (LocalAuthor a : db.getLocalAuthors(txn))
|
||||||
|
if (a.getId().equals(authorId)) return VERIFIED;
|
||||||
|
// Compare to the IDs of contacts' identities
|
||||||
|
for (Contact c : db.getContacts(txn))
|
||||||
|
if (c.getAuthor().getId().equals(authorId)) return VERIFIED;
|
||||||
|
|
||||||
|
// TODO also handle UNVERIFIED when #261 is implemented
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import org.briarproject.api.blogs.Blog;
|
|||||||
import org.briarproject.api.blogs.BlogFactory;
|
import org.briarproject.api.blogs.BlogFactory;
|
||||||
import org.briarproject.api.blogs.BlogInvitationMessage;
|
import org.briarproject.api.blogs.BlogInvitationMessage;
|
||||||
import org.briarproject.api.blogs.BlogManager;
|
import org.briarproject.api.blogs.BlogManager;
|
||||||
|
import org.briarproject.api.blogs.BlogManager.RemoveBlogHook;
|
||||||
import org.briarproject.api.blogs.BlogSharingManager;
|
import org.briarproject.api.blogs.BlogSharingManager;
|
||||||
import org.briarproject.api.blogs.BlogSharingMessage.BlogInvitation;
|
import org.briarproject.api.blogs.BlogSharingMessage.BlogInvitation;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
@@ -40,7 +41,7 @@ import static org.briarproject.api.blogs.BlogConstants.BLOG_TITLE;
|
|||||||
|
|
||||||
class BlogSharingManagerImpl extends
|
class BlogSharingManagerImpl extends
|
||||||
SharingManagerImpl<Blog, BlogInvitation, BlogInvitationMessage, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent>
|
SharingManagerImpl<Blog, BlogInvitation, BlogInvitationMessage, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationReceivedEvent, BlogInvitationResponseReceivedEvent>
|
||||||
implements BlogSharingManager {
|
implements BlogSharingManager, RemoveBlogHook {
|
||||||
|
|
||||||
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
||||||
"bee438b5de0b3a685badc4e49d76e72d"
|
"bee438b5de0b3a685badc4e49d76e72d"
|
||||||
@@ -124,6 +125,11 @@ class BlogSharingManagerImpl extends
|
|||||||
return irrFactory;
|
return irrFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removingBlog(Transaction txn, Blog b) throws DbException {
|
||||||
|
removingShareable(txn, b);
|
||||||
|
}
|
||||||
|
|
||||||
static class SFactory implements
|
static class SFactory implements
|
||||||
ShareableFactory<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState> {
|
ShareableFactory<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState> {
|
||||||
|
|
||||||
|
|||||||
@@ -792,6 +792,8 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM
|
|||||||
} else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US) {
|
} else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US) {
|
||||||
removeFromList(txn, groupId, SHARED_WITH_US, f);
|
removeFromList(txn, groupId, SHARED_WITH_US, f);
|
||||||
} else if (task == TASK_ADD_SHARED_SHAREABLE) {
|
} else if (task == TASK_ADD_SHARED_SHAREABLE) {
|
||||||
|
// TODO we might want to call the add() method of the respective
|
||||||
|
// manager here, because blogs add a description for example
|
||||||
db.addGroup(txn, f.getGroup());
|
db.addGroup(txn, f.getGroup());
|
||||||
db.setVisibleToContact(txn, contactId, f.getId(), true);
|
db.setVisibleToContact(txn, contactId, f.getId(), true);
|
||||||
} else if (task == TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US) {
|
} else if (task == TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.sharing;
|
package org.briarproject.sharing;
|
||||||
|
|
||||||
|
import org.briarproject.api.blogs.BlogManager;
|
||||||
import org.briarproject.api.blogs.BlogSharingManager;
|
import org.briarproject.api.blogs.BlogSharingManager;
|
||||||
import org.briarproject.api.clients.ClientHelper;
|
import org.briarproject.api.clients.ClientHelper;
|
||||||
import org.briarproject.api.clients.MessageQueueManager;
|
import org.briarproject.api.clients.MessageQueueManager;
|
||||||
@@ -49,6 +50,7 @@ public class SharingModule {
|
|||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager,
|
||||||
ContactManager contactManager,
|
ContactManager contactManager,
|
||||||
MessageQueueManager messageQueueManager,
|
MessageQueueManager messageQueueManager,
|
||||||
|
BlogManager blogManager,
|
||||||
BlogSharingManagerImpl blogSharingManager) {
|
BlogSharingManagerImpl blogSharingManager) {
|
||||||
|
|
||||||
lifecycleManager.registerClient(blogSharingManager);
|
lifecycleManager.registerClient(blogSharingManager);
|
||||||
@@ -56,6 +58,7 @@ public class SharingModule {
|
|||||||
contactManager.registerRemoveContactHook(blogSharingManager);
|
contactManager.registerRemoveContactHook(blogSharingManager);
|
||||||
messageQueueManager.registerIncomingMessageHook(
|
messageQueueManager.registerIncomingMessageHook(
|
||||||
BlogSharingManagerImpl.CLIENT_ID, blogSharingManager);
|
BlogSharingManagerImpl.CLIENT_ID, blogSharingManager);
|
||||||
|
blogManager.registerRemoveBlogHook(blogSharingManager);
|
||||||
|
|
||||||
return blogSharingManager;
|
return blogSharingManager;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user