diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java index 7e89a7a7b..b7c208ff2 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java @@ -13,7 +13,9 @@ import javax.annotation.concurrent.Immutable; @NotNullByDefault public class Author { - public enum Status {ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES} + public enum Status { + NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES + } private final AuthorId id; private final String name; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java index 54238266b..7ae01fe47 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java @@ -68,8 +68,8 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry; @NotNullByDefault abstract class JdbcDatabase implements Database { - private static final int SCHEMA_VERSION = 29; - private static final int MIN_SCHEMA_VERSION = 29; + private static final int SCHEMA_VERSION = 30; + private static final int MIN_SCHEMA_VERSION = 30; private static final String CREATE_SETTINGS = "CREATE TABLE settings" diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java new file mode 100644 index 000000000..e142a2479 --- /dev/null +++ b/bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java @@ -0,0 +1,16 @@ +package org.briarproject.bramble.test; + +import javax.net.SocketFactory; + +import dagger.Module; +import dagger.Provides; + +@Module +public class TestSocksModule { + + @Provides + SocketFactory provideSocketFactory() { + return SocketFactory.getDefault(); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java index d8561ad4d..546f9ac18 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java @@ -48,6 +48,10 @@ public class BlogPostItem implements Comparable { return body; } + public boolean isRssFeed() { + return header.isRssFeed(); + } + public boolean isRead() { return read; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java index ba1d11e68..76232fc57 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java @@ -108,7 +108,8 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder { author.setAuthor(a); author.setAuthorStatus(post.getAuthorStatus()); author.setDate(post.getTimestamp()); - author.setPersona(AuthorView.NORMAL); + author.setPersona( + item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL); // TODO make author clickable more often #624 if (item.getHeader().getType() == POST) { author.setBlogLink(post.getGroupId()); @@ -168,7 +169,9 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder { reblogger.setVisibility(VISIBLE); reblogger.setPersona(AuthorView.REBLOGGER); - author.setPersona(AuthorView.COMMENTER); + author.setPersona(item.getHeader().getRootPost().isRssFeed() ? + AuthorView.RSS_FEED_REBLOGGED : + AuthorView.COMMENTER); // comments for (BlogCommentHeader c : item.getComments()) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java index 82a432091..c7a64659a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java @@ -179,7 +179,6 @@ public class FeedFragment extends BaseFragment implements case R.id.action_rss_feeds_import: Intent i2 = new Intent(getActivity(), RssFeedImportActivity.class); - i2.putExtra(GROUP_ID, personalBlog.getId().getBytes()); startActivity(i2); return true; case R.id.action_rss_feeds_manage: diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java index 3052e3694..e4ed107b6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java @@ -6,7 +6,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.ImageView; +import android.widget.ImageButton; import android.widget.TextView; import org.briarproject.briar.R; @@ -39,12 +39,7 @@ class RssFeedAdapter extends BriarAdapter { if (item == null) return; // Feed Title - if (item.getTitle() != null) { - ui.title.setText(item.getTitle()); - ui.title.setVisibility(VISIBLE); - } else { - ui.title.setVisibility(GONE); - } + ui.title.setText(item.getTitle()); // Delete Button ui.delete.setOnClickListener(new OnClickListener() { @@ -75,6 +70,14 @@ class RssFeedAdapter extends BriarAdapter { } else { ui.description.setVisibility(GONE); } + + // Open feed's blog when clicked + ui.layout.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + listener.onFeedClick(item); + } + }); } @Override @@ -99,8 +102,9 @@ class RssFeedAdapter extends BriarAdapter { } static class FeedViewHolder extends RecyclerView.ViewHolder { + private final View layout; private final TextView title; - private final ImageView delete; + private final ImageButton delete; private final TextView imported; private final TextView updated; private final TextView author; @@ -110,8 +114,9 @@ class RssFeedAdapter extends BriarAdapter { private FeedViewHolder(View v) { super(v); + layout = v; title = (TextView) v.findViewById(R.id.titleView); - delete = (ImageView) v.findViewById(R.id.deleteButton); + delete = (ImageButton) v.findViewById(R.id.deleteButton); imported = (TextView) v.findViewById(R.id.importedView); updated = (TextView) v.findViewById(R.id.updatedView); author = (TextView) v.findViewById(R.id.authorView); @@ -121,6 +126,7 @@ class RssFeedAdapter extends BriarAdapter { } interface RssFeedListener { + void onFeedClick(Feed feed); void onDeleteClick(Feed feed); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java index 7e2085818..1d09ad2c4 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java @@ -1,7 +1,6 @@ package org.briarproject.briar.android.blog; import android.content.DialogInterface; -import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AlertDialog; import android.text.Editable; @@ -15,7 +14,6 @@ import android.widget.ProgressBar; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.lifecycle.IoExecutor; -import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; @@ -44,9 +42,6 @@ public class RssFeedImportActivity extends BriarActivity { @IoExecutor Executor ioExecutor; - // Fields that are accessed from background threads must be volatile - private volatile GroupId groupId = null; - @Inject @SuppressWarnings("WeakerAccess") volatile FeedManager feedManager; @@ -55,12 +50,6 @@ public class RssFeedImportActivity extends BriarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // GroupId from Intent - Intent i = getIntent(); - byte[] b = i.getByteArrayExtra(GROUP_ID); - if (b == null) throw new IllegalStateException("No Group in intent."); - groupId = new GroupId(b); - setContentView(R.layout.activity_rss_feed_import); urlInput = (EditText) findViewById(R.id.urlInput); @@ -128,7 +117,7 @@ public class RssFeedImportActivity extends BriarActivity { @Override public void run() { try { - feedManager.addFeed(url, groupId); + feedManager.addFeed(url); feedImported(); } catch (DbException | IOException e) { if (LOG.isLoggable(WARNING)) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java index 10e37d6f2..8525f1f65 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java @@ -1,15 +1,16 @@ package org.briarproject.briar.android.blog; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.support.design.widget.Snackbar; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.LinearLayoutManager; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import org.briarproject.bramble.api.db.DbException; -import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; @@ -23,6 +24,7 @@ import java.util.logging.Logger; import javax.inject.Inject; +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.support.design.widget.Snackbar.LENGTH_LONG; import static java.util.logging.Level.WARNING; @@ -34,7 +36,6 @@ public class RssFeedManageActivity extends BriarActivity private BriarRecyclerView list; private RssFeedAdapter adapter; - private GroupId groupId; @Inject @SuppressWarnings("WeakerAccess") @@ -44,12 +45,6 @@ public class RssFeedManageActivity extends BriarActivity public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // GroupId from Intent - Intent i = getIntent(); - byte[] b = i.getByteArrayExtra(GROUP_ID); - if (b == null) throw new IllegalStateException("No Group in intent."); - groupId = new GroupId(b); - setContentView(R.layout.activity_rss_feed_manage); adapter = new RssFeedAdapter(this, this); @@ -87,7 +82,6 @@ public class RssFeedManageActivity extends BriarActivity return true; case R.id.action_rss_feeds_import: Intent i = new Intent(this, RssFeedImportActivity.class); - i.putExtra(GROUP_ID, groupId.getBytes()); startActivity(i); return true; default: @@ -100,21 +94,32 @@ public class RssFeedManageActivity extends BriarActivity component.inject(this); } + @Override + public void onFeedClick(Feed feed) { + Intent i = new Intent(this, BlogActivity.class); + i.putExtra(GROUP_ID, feed.getBlogId().getBytes()); + i.setFlags(FLAG_ACTIVITY_CLEAR_TOP); + startActivity(i); + } + @Override public void onDeleteClick(final Feed feed) { - runOnDbThread(new Runnable() { - @Override - public void run() { - try { - feedManager.removeFeed(feed.getUrl()); - onFeedDeleted(feed); - } catch (DbException e) { - if (LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - onDeleteError(); - } - } - }); + DialogInterface.OnClickListener okListener = + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + deleteFeed(feed); + } + }; + AlertDialog.Builder builder = new AlertDialog.Builder(this, + R.style.BriarDialogTheme); + builder.setTitle(getString(R.string.blogs_rss_remove_feed)); + builder.setMessage( + getString(R.string.blogs_rss_remove_feed_dialog_message)); + builder.setPositiveButton(R.string.cancel, null); + builder.setNegativeButton(R.string.blogs_rss_remove_feed_ok, + okListener); + builder.show(); } private void loadFeeds() { @@ -149,6 +154,22 @@ public class RssFeedManageActivity extends BriarActivity }); } + private void deleteFeed(final Feed feed) { + runOnDbThread(new Runnable() { + @Override + public void run() { + try { + feedManager.removeFeed(feed); + onFeedDeleted(feed); + } catch (DbException e) { + if (LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + onDeleteError(); + } + } + }); + } + private void onLoadError() { runOnUiThreadUnlessDestroyed(new Runnable() { @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java index 82442185e..f03a88ecd 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java @@ -30,6 +30,7 @@ import static android.content.Context.LAYOUT_INFLATER_SERVICE; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.graphics.Typeface.BOLD; import static android.util.TypedValue.COMPLEX_UNIT_PX; +import static org.briarproject.bramble.api.identity.Author.Status.NONE; import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; @@ -40,6 +41,8 @@ public class AuthorView extends RelativeLayout { public static final int REBLOGGER = 1; public static final int COMMENTER = 2; public static final int LIST = 3; + public static final int RSS_FEED = 4; + public static final int RSS_FEED_REBLOGGED = 5; private final CircleImageView avatar; private final ImageView avatarIcon; @@ -83,7 +86,13 @@ public class AuthorView extends RelativeLayout { } public void setAuthorStatus(Status status) { - trustIndicator.setTrustLevel(status); + if (status != NONE) { + trustIndicator.setTrustLevel(status); + trustIndicator.setVisibility(VISIBLE); + } else { + trustIndicator.setVisibility(GONE); + } + if (status == OURSELVES) { authorName.setTypeface(authorNameTypeface, BOLD); } else { @@ -124,10 +133,17 @@ public class AuthorView extends RelativeLayout { setOnClickListener(null); } + /** + * Styles this view for a different persona. + * + * Attention: RSS_FEED and RSS_FEED_REBLOGGED change the avatar + * and override the one set by + * {@link AuthorView#setAuthor(Author)}. + */ public void setPersona(int persona) { switch (persona) { case NORMAL: - avatarIcon.setVisibility(VISIBLE); + avatarIcon.setVisibility(INVISIBLE); date.setVisibility(VISIBLE); setAvatarSize(R.dimen.blogs_avatar_normal_size); setTextSize(authorName, R.dimen.text_size_small); @@ -158,6 +174,24 @@ public class AuthorView extends RelativeLayout { setCenterVertical(authorName, true); setCenterVertical(trustIndicator, true); break; + case RSS_FEED: + avatarIcon.setVisibility(INVISIBLE); + date.setVisibility(VISIBLE); + avatar.setImageResource(R.drawable.ic_rss_feed); + setAvatarSize(R.dimen.blogs_avatar_normal_size); + setTextSize(authorName, R.dimen.text_size_small); + setCenterVertical(authorName, false); + setCenterVertical(trustIndicator, false); + break; + case RSS_FEED_REBLOGGED: + avatarIcon.setVisibility(INVISIBLE); + date.setVisibility(VISIBLE); + avatar.setImageResource(R.drawable.ic_rss_feed); + setAvatarSize(R.dimen.blogs_avatar_comment_size); + setTextSize(authorName, R.dimen.text_size_tiny); + setCenterVertical(authorName, false); + setCenterVertical(trustIndicator, false); + break; } } diff --git a/briar-android/src/main/res/drawable/ic_rss_feed.xml b/briar-android/src/main/res/drawable/ic_rss_feed.xml new file mode 100644 index 000000000..6d441fa06 --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_rss_feed.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/layout/list_item_rss_feed.xml b/briar-android/src/main/res/layout/list_item_rss_feed.xml index 76e0978a7..183dfc3b0 100644 --- a/briar-android/src/main/res/layout/list_item_rss_feed.xml +++ b/briar-android/src/main/res/layout/list_item_rss_feed.xml @@ -19,7 +19,7 @@ android:textSize="@dimen/text_size_medium" tools:text="This is a name of a RSS Feed"/> - + + diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 99e86b60e..3c8a44883 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -304,6 +304,9 @@ Imported: Author: Last Updated: + Remove Feed + Are you sure you want to remove this feed and all its posts?\nAny posts you have shared will not be removed from other people\'s devices. + Remove Feed The feed could not be deleted! You haven\'t imported any RSS feeds.\n\nWhy don\'t you click the plus in the top right screen corner to add your first? There was a problem loading your feeds. Please try again later. diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java index b7e1d240f..a0e4fff02 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java @@ -13,16 +13,22 @@ import javax.annotation.concurrent.Immutable; public class Blog extends BaseGroup implements Shareable { private final Author author; + private final boolean rssFeed; - public Blog(Group group, Author author) { + public Blog(Group group, Author author, boolean rssFeed) { super(group); this.author = author; + this.rssFeed = rssFeed; } public Author getAuthor() { return author; } + public boolean isRssFeed() { + return rssFeed; + } + @Override public boolean equals(Object o) { return o instanceof Blog && super.equals(o); diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java index 70fded281..f7914c9d5 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java @@ -26,7 +26,7 @@ public class BlogCommentHeader extends BlogPostHeader { Status authorStatus, boolean read) { super(type, groupId, id, parent.getId(), timestamp, - timeReceived, author, authorStatus, read); + timeReceived, author, authorStatus, false, read); if (type != COMMENT && type != WRAPPED_COMMENT) throw new IllegalArgumentException("Incompatible Message Type"); @@ -43,4 +43,11 @@ public class BlogCommentHeader extends BlogPostHeader { public BlogPostHeader getParent() { return parent; } + + public BlogPostHeader getRootPost() { + if (parent instanceof BlogCommentHeader) + return ((BlogCommentHeader) parent).getRootPost(); + return parent; + } + } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java index 99c4514b2..4c8ff25d1 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java @@ -28,6 +28,7 @@ public interface BlogConstants { String KEY_AUTHOR_NAME = "name"; String KEY_PUBLIC_KEY = "publicKey"; String KEY_AUTHOR = "author"; + String KEY_RSS_FEED = "rssFeed"; String KEY_READ = "read"; String KEY_COMMENT = "comment"; String KEY_ORIGINAL_MSG_ID = "originalMessageId"; diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java index 4b0a89cf3..a9d057501 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java @@ -13,6 +13,11 @@ public interface BlogFactory { */ Blog createBlog(Author author); + /** + * Creates a RSS feed blog for a given author. + */ + Blog createFeedBlog(Author author); + /** * Parses a blog with the given Group */ diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java index 20ffb991c..041d474a4 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java @@ -41,6 +41,11 @@ public interface BlogManager { */ void removeBlog(Blog b) throws DbException; + /** + * Removes and deletes a blog with the given {@link Transaction}. + */ + void removeBlog(Transaction txn, Blog b) throws DbException; + /** * Stores a local blog post. */ diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java index cf01dfc57..57a0ef58a 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java @@ -17,21 +17,23 @@ public class BlogPostHeader extends PostHeader { private final MessageType type; private final GroupId groupId; private final long timeReceived; + private final boolean rssFeed; public BlogPostHeader(MessageType type, GroupId groupId, MessageId id, @Nullable MessageId parentId, long timestamp, long timeReceived, - Author author, Status authorStatus, boolean read) { + Author author, Status authorStatus, boolean rssFeed, boolean read) { super(id, parentId, timestamp, author, authorStatus, read); this.type = type; this.groupId = groupId; this.timeReceived = timeReceived; + this.rssFeed = rssFeed; } public BlogPostHeader(MessageType type, GroupId groupId, MessageId id, long timestamp, long timeReceived, Author author, - Status authorStatus, boolean read) { + Status authorStatus, boolean rssFeed, boolean read) { this(type, groupId, id, null, timestamp, timeReceived, author, - authorStatus, read); + authorStatus, rssFeed, read); } public MessageType getType() { @@ -45,4 +47,9 @@ public class BlogPostHeader extends PostHeader { public long getTimeReceived() { return timeReceived; } + + public boolean isRssFeed() { + return rssFeed; + } + } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java index b36a8a70b..f2fcaa387 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java @@ -1,40 +1,31 @@ package org.briarproject.briar.api.feed; -import org.briarproject.bramble.api.FormatException; -import org.briarproject.bramble.api.data.BdfDictionary; -import org.briarproject.bramble.api.data.BdfEntry; +import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.GroupId; +import org.briarproject.briar.api.blog.Blog; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_BLOG_GROUP_ID; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_ADDED; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_AUTHOR; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_DESC; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_LAST_ENTRY; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_TITLE; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_UPDATED; -import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_URL; - @Immutable @NotNullByDefault public class Feed { private final String url; - private final GroupId blogId; + private final Blog blog; + private final LocalAuthor localAuthor; @Nullable - private final String title, description, author; + private final String description, author; private final long added, updated, lastEntryTime; - public Feed(String url, GroupId blogId, @Nullable String title, - @Nullable String description, @Nullable String author, - long added, long updated, long lastEntryTime) { + public Feed(String url, Blog blog, LocalAuthor localAuthor, + @Nullable String description, @Nullable String author, long added, + long updated, long lastEntryTime) { this.url = url; - this.blogId = blogId; - this.title = title; + this.blog = blog; + this.localAuthor = localAuthor; this.description = description; this.author = author; this.added = added; @@ -42,13 +33,13 @@ public class Feed { this.lastEntryTime = lastEntryTime; } - public Feed(String url, GroupId blogId, @Nullable String title, + public Feed(String url, Blog blog, LocalAuthor localAuthor, @Nullable String description, @Nullable String author, long added) { - this(url, blogId, title, description, author, added, 0L, 0L); + this(url, blog, localAuthor, description, author, added, 0L, 0L); } - public Feed(String url, GroupId blogId, long added) { - this(url, blogId, null, null, null, added, 0L, 0L); + public Feed(String url, Blog blog, LocalAuthor localAuthor, long added) { + this(url, blog, localAuthor, null, null, added, 0L, 0L); } public String getUrl() { @@ -56,39 +47,19 @@ public class Feed { } public GroupId getBlogId() { - return blogId; + return blog.getId(); } - public BdfDictionary toBdfDictionary() { - BdfDictionary d = BdfDictionary.of( - new BdfEntry(KEY_FEED_URL, url), - new BdfEntry(KEY_BLOG_GROUP_ID, blogId.getBytes()), - new BdfEntry(KEY_FEED_ADDED, added), - new BdfEntry(KEY_FEED_UPDATED, updated), - new BdfEntry(KEY_FEED_LAST_ENTRY, lastEntryTime) - ); - if (title != null) d.put(KEY_FEED_TITLE, title); - if (description != null) d.put(KEY_FEED_DESC, description); - if (author != null) d.put(KEY_FEED_AUTHOR, author); - return d; + public Blog getBlog() { + return blog; } - public static Feed from(BdfDictionary d) throws FormatException { - String url = d.getString(KEY_FEED_URL); - GroupId blogId = new GroupId(d.getRaw(KEY_BLOG_GROUP_ID)); - String title = d.getOptionalString(KEY_FEED_TITLE); - String desc = d.getOptionalString(KEY_FEED_DESC); - String author = d.getOptionalString(KEY_FEED_AUTHOR); - long added = d.getLong(KEY_FEED_ADDED, 0L); - long updated = d.getLong(KEY_FEED_UPDATED, 0L); - long lastEntryTime = d.getLong(KEY_FEED_LAST_ENTRY, 0L); - return new Feed(url, blogId, title, desc, author, added, updated, - lastEntryTime); + public LocalAuthor getLocalAuthor() { + return localAuthor; } - @Nullable public String getTitle() { - return title; + return blog.getName(); } @Nullable @@ -118,20 +89,9 @@ public class Feed { if (this == o) return true; if (o instanceof Feed) { Feed f = (Feed) o; - return url.equals(f.url) && blogId.equals(f.getBlogId()) && - equalsWithNull(title, f.getTitle()) && - equalsWithNull(description, f.getDescription()) && - equalsWithNull(author, f.getAuthor()) && - added == f.getAdded() && - updated == f.getUpdated() && - lastEntryTime == f.getLastEntryTime(); + return blog.equals(f.blog); } return false; } - private boolean equalsWithNull(@Nullable Object a, @Nullable Object b) { - if (a == b) return true; - if (a == null || b == null) return false; - return a.equals(b); - } } diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java index 8be05d568..0fddb3aa7 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java @@ -18,8 +18,9 @@ public interface FeedConstants { // group metadata keys String KEY_FEEDS = "feeds"; String KEY_FEED_URL = "feedURL"; - String KEY_BLOG_GROUP_ID = "blogGroupId"; - String KEY_FEED_TITLE = "feedTitle"; + String KEY_BLOG_TITLE = "blogTitle"; + String KEY_PUBLIC_KEY = "publicKey"; + String KEY_PRIVATE_KEY = "privateKey"; String KEY_FEED_DESC = "feedDesc"; String KEY_FEED_AUTHOR = "feedAuthor"; String KEY_FEED_ADDED = "feedAdded"; diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java index 5d31b448a..7d83b22c1 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java @@ -3,7 +3,6 @@ package org.briarproject.briar.api.feed; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.ClientId; -import org.briarproject.bramble.api.sync.GroupId; import java.io.IOException; import java.util.List; @@ -17,14 +16,14 @@ public interface FeedManager { ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed"); /** - * Adds an RSS feed. + * Adds an RSS feed as a new dedicated blog. */ - void addFeed(String url, GroupId g) throws DbException, IOException; + void addFeed(String url) throws DbException, IOException; /** * Removes an RSS feed. */ - void removeFeed(String url) throws DbException; + void removeFeed(Feed feed) throws DbException; /** * Returns a list of all added RSS feeds diff --git a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java index a9a7bfc50..99d5879aa 100644 --- a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java @@ -2,6 +2,7 @@ package org.briarproject.briar; import org.briarproject.briar.blog.BlogModule; import org.briarproject.briar.client.BriarClientModule; +import org.briarproject.briar.feed.DnsModule; import org.briarproject.briar.feed.FeedModule; import org.briarproject.briar.forum.ForumModule; import org.briarproject.briar.introduction.IntroductionModule; @@ -16,6 +17,7 @@ import dagger.Module; BlogModule.class, BriarClientModule.class, FeedModule.class, + DnsModule.class, ForumModule.class, GroupInvitationModule.class, IntroductionModule.class, diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java index 26c1eb108..c7d3a2001 100644 --- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java @@ -14,6 +14,9 @@ import org.briarproject.briar.api.blog.BlogFactory; import javax.annotation.concurrent.Immutable; import javax.inject.Inject; +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; + @Immutable @NotNullByDefault class BlogFactoryImpl implements BlogFactory { @@ -33,28 +36,46 @@ class BlogFactoryImpl implements BlogFactory { @Override public Blog createBlog(Author a) { + return createBlog(a, false); + } + + @Override + public Blog createFeedBlog(Author a) { + return createBlog(a, true); + } + + private Blog createBlog(Author a, boolean rssFeed) { try { BdfList blog = BdfList.of( a.getName(), - a.getPublicKey() + a.getPublicKey(), + rssFeed ); byte[] descriptor = clientHelper.toByteArray(blog); Group g = groupFactory .createGroup(BlogManagerImpl.CLIENT_ID, descriptor); - return new Blog(g, a); + return new Blog(g, a, rssFeed); } catch (FormatException e) { throw new RuntimeException(e); } } @Override - public Blog parseBlog(Group g) throws FormatException { - byte[] descriptor = g.getDescriptor(); + public Blog parseBlog(Group group) throws FormatException { + byte[] descriptor = group.getDescriptor(); // Author Name, Public Key BdfList blog = clientHelper.toList(descriptor); - Author a = - authorFactory.createAuthor(blog.getString(0), blog.getRaw(1)); - return new Blog(g, a); + String name = blog.getString(0); + if (name.length() > MAX_AUTHOR_NAME_LENGTH) + throw new IllegalArgumentException(); + byte[] publicKey = blog.getRaw(1); + if (publicKey.length > MAX_PUBLIC_KEY_LENGTH) + throw new IllegalArgumentException(); + + Author author = + authorFactory.createAuthor(name, publicKey); + boolean rssFeed = blog.getBoolean(2); + return new Blog(group, author, rssFeed); } } diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java index 496b308fb..39ccb5241 100644 --- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java @@ -61,6 +61,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_PARENT_ import static org.briarproject.briar.api.blog.BlogConstants.KEY_PARENT_MSG_ID; import static org.briarproject.briar.api.blog.BlogConstants.KEY_PUBLIC_KEY; import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ; +import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIMESTAMP; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE; @@ -224,6 +225,11 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, } } + @Override + public void removeBlog(Transaction txn, Blog b) throws DbException { + removeBlog(txn, b, false); + } + private void removeBlog(Transaction txn, Blog b, boolean forced) throws DbException { if (!forced && !canBeRemoved(txn, b.getId())) @@ -248,15 +254,18 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, @Override public void addLocalPost(Transaction txn, BlogPost p) throws DbException { try { + GroupId groupId = p.getMessage().getGroupId(); + Blog b = getBlog(txn, groupId); + BdfDictionary meta = new BdfDictionary(); meta.put(KEY_TYPE, POST.getInt()); meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp()); meta.put(KEY_AUTHOR, authorToBdfDictionary(p.getAuthor())); meta.put(KEY_READ, true); + meta.put(KEY_RSS_FEED, b.isRssFeed()); clientHelper.addLocalMessage(txn, p.getMessage(), meta, true); // broadcast event about new post - GroupId groupId = p.getMessage().getGroupId(); MessageId postId = p.getMessage().getId(); BlogPostHeader h = getPostHeaderFromMetadata(txn, groupId, postId, meta); @@ -345,6 +354,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, wMessage = blogPostFactory .wrapPost(groupId, wDescriptor, wTimestamp, body); meta.put(KEY_TYPE, WRAPPED_POST.getInt()); + meta.put(KEY_RSS_FEED, pOriginalHeader.isRssFeed()); } else if (type == COMMENT) { Group wGroup = db.getGroup(txn, pOriginalHeader.getGroupId()); byte[] wDescriptor = wGroup.getDescriptor(); @@ -593,8 +603,11 @@ 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); + boolean isFeedPost = meta.getBoolean(KEY_RSS_FEED, false); Status authorStatus; - if (authorStatuses.containsKey(authorId)) { + if (isFeedPost) { + authorStatus = Status.NONE; + } else if (authorStatuses.containsKey(authorId)) { authorStatus = authorStatuses.get(authorId); } else { authorStatus = identityManager.getAuthorStatus(txn, authorId); @@ -611,7 +624,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager, timestamp, timeReceived, author, authorStatus, read); } else { return new BlogPostHeader(type, groupId, id, timestamp, - timeReceived, author, authorStatus, read); + timeReceived, author, authorStatus, isFeedPost, read); } } diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java index 33fb0bddf..5e74400c5 100644 --- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java +++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java @@ -39,6 +39,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_PARENT_ import static org.briarproject.briar.api.blog.BlogConstants.KEY_PARENT_MSG_ID; import static org.briarproject.briar.api.blog.BlogConstants.KEY_PUBLIC_KEY; import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ; +import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIMESTAMP; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED; import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE; @@ -123,6 +124,7 @@ class BlogPostValidator extends BdfMessageValidator { BdfDictionary meta = new BdfDictionary(); meta.put(KEY_ORIGINAL_MSG_ID, m.getId()); meta.put(KEY_AUTHOR, authorToBdfDictionary(a)); + meta.put(KEY_RSS_FEED, b.isRssFeed()); return new BdfMessageContext(meta); } diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java b/briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java new file mode 100644 index 000000000..dc6cd8a88 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java @@ -0,0 +1,18 @@ +package org.briarproject.briar.feed; + +import dagger.Module; +import dagger.Provides; +import okhttp3.Dns; + +/** + * This is a dedicated module, so it can be replaced for testing. + */ +@Module +public class DnsModule { + + @Provides + Dns provideDns(NoDns noDns) { + return noDns; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java new file mode 100644 index 000000000..192c4855e --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java @@ -0,0 +1,34 @@ +package org.briarproject.briar.feed; + +import com.rometools.rome.feed.synd.SyndFeed; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.briar.api.feed.Feed; + +interface FeedFactory { + + /** + * Create a new feed based on the feed url + * and the metadata of an existing {@link SyndFeed}. + */ + Feed createFeed(String url, SyndFeed feed); + + /** + * Creates a new updated feed, based on the given existing feed, + * new metadata from the given {@link SyndFeed} + * and the time of the last feed entry. + */ + Feed createFeed(Feed feed, SyndFeed f, long lastEntryTime); + + /** + * De-serializes a {@link BdfDictionary} into a {@link Feed}. + */ + Feed createFeed(BdfDictionary d) throws FormatException; + + /** + * Serializes a {@link Feed} into a {@link BdfDictionary}. + */ + BdfDictionary feedToBdfDictionary(Feed feed); + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java new file mode 100644 index 000000000..ec5d9c31b --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java @@ -0,0 +1,112 @@ +package org.briarproject.briar.feed; + +import com.rometools.rome.feed.synd.SyndFeed; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.KeyPair; +import org.briarproject.bramble.api.data.BdfDictionary; +import org.briarproject.bramble.api.data.BdfEntry; +import org.briarproject.bramble.api.identity.AuthorFactory; +import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.bramble.util.StringUtils; +import org.briarproject.briar.api.blog.Blog; +import org.briarproject.briar.api.blog.BlogFactory; +import org.briarproject.briar.api.feed.Feed; + +import javax.inject.Inject; + +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_BLOG_TITLE; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_ADDED; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_AUTHOR; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_DESC; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_LAST_ENTRY; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_UPDATED; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_URL; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_PRIVATE_KEY; +import static org.briarproject.briar.api.feed.FeedConstants.KEY_PUBLIC_KEY; + +class FeedFactoryImpl implements FeedFactory { + + private final CryptoComponent cryptoComponent; + private final AuthorFactory authorFactory; + private final BlogFactory blogFactory; + private final Clock clock; + + @Inject + FeedFactoryImpl(CryptoComponent cryptoComponent, + AuthorFactory authorFactory, BlogFactory blogFactory, Clock clock) { + this.cryptoComponent = cryptoComponent; + this.authorFactory = authorFactory; + this.blogFactory = blogFactory; + this.clock = clock; + } + + @Override + public Feed createFeed(String url, SyndFeed syndFeed) { + String title = syndFeed.getTitle(); + if (title == null) title = "RSS"; + title = StringUtils.truncateUtf8(title, MAX_AUTHOR_NAME_LENGTH); + + KeyPair keyPair = cryptoComponent.generateSignatureKeyPair(); + LocalAuthor localAuthor = authorFactory + .createLocalAuthor(title, + keyPair.getPublic().getEncoded(), + keyPair.getPrivate().getEncoded()); + Blog blog = blogFactory.createFeedBlog(localAuthor); + long added = clock.currentTimeMillis(); + + return new Feed(url, blog, localAuthor, added); + } + + @Override + public Feed createFeed(Feed feed, SyndFeed f, long lastEntryTime) { + long updated = clock.currentTimeMillis(); + return new Feed(feed.getUrl(), feed.getBlog(), feed.getLocalAuthor(), + f.getDescription(), f.getAuthor(), feed.getAdded(), updated, + lastEntryTime); + } + + @Override + public Feed createFeed(BdfDictionary d) throws FormatException { + String url = d.getString(KEY_FEED_URL); + + String blogTitle = d.getString(KEY_BLOG_TITLE); + byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY); + byte[] privateKey = d.getRaw(KEY_PRIVATE_KEY); + LocalAuthor localAuthor = authorFactory + .createLocalAuthor(blogTitle, publicKey, privateKey); + Blog blog = blogFactory.createFeedBlog(localAuthor); + + String desc = d.getOptionalString(KEY_FEED_DESC); + String author = d.getOptionalString(KEY_FEED_AUTHOR); + long added = d.getLong(KEY_FEED_ADDED, 0L); + long updated = d.getLong(KEY_FEED_UPDATED, 0L); + long lastEntryTime = d.getLong(KEY_FEED_LAST_ENTRY, 0L); + + return new Feed(url, blog, localAuthor, desc, author, added, + updated, lastEntryTime); + } + + @Override + public BdfDictionary feedToBdfDictionary(Feed feed) { + BdfDictionary d = BdfDictionary.of( + new BdfEntry(KEY_FEED_URL, feed.getUrl()), + new BdfEntry(KEY_BLOG_TITLE, feed.getLocalAuthor().getName()), + new BdfEntry(KEY_PUBLIC_KEY, + feed.getLocalAuthor().getPublicKey()), + new BdfEntry(KEY_PRIVATE_KEY, + feed.getLocalAuthor().getPrivateKey()), + new BdfEntry(KEY_FEED_ADDED, feed.getAdded()), + new BdfEntry(KEY_FEED_UPDATED, feed.getUpdated()), + new BdfEntry(KEY_FEED_LAST_ENTRY, feed.getLastEntryTime()) + ); + if (feed.getDescription() != null) + d.put(KEY_FEED_DESC, feed.getDescription()); + if (feed.getAuthor() != null) d.put(KEY_FEED_AUTHOR, feed.getAuthor()); + return d; + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java index d92df5001..bb97bb0d4 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java @@ -18,7 +18,6 @@ import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.EventListener; -import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -31,6 +30,7 @@ import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.util.StringUtils; +import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogPost; import org.briarproject.briar.api.blog.BlogPostFactory; @@ -39,8 +39,6 @@ import org.briarproject.briar.api.feed.FeedManager; import java.io.IOException; import java.io.InputStream; -import java.net.InetAddress; -import java.net.UnknownHostException; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Collections; @@ -75,12 +73,12 @@ import static org.briarproject.briar.util.HtmlUtils.clean; @ThreadSafe @NotNullByDefault -class FeedManagerImpl implements FeedManager, Client, EventListener { +class FeedManagerImpl implements FeedManager, Client, EventListener, + BlogManager.RemoveBlogHook { private static final Logger LOG = Logger.getLogger(FeedManagerImpl.class.getName()); - private static final byte[] UNSPECIFIED_ADDRESS = new byte[4]; private static final int CONNECT_TIMEOUT = 60 * 1000; // Milliseconds private final ScheduledExecutorService scheduler; @@ -88,31 +86,33 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { private final DatabaseComponent db; private final ContactGroupFactory contactGroupFactory; private final ClientHelper clientHelper; - private final IdentityManager identityManager; private final BlogManager blogManager; private final BlogPostFactory blogPostFactory; + private final FeedFactory feedFactory; private final SocketFactory torSocketFactory; private final Clock clock; + private final Dns noDnsLookups; private final AtomicBoolean fetcherStarted = new AtomicBoolean(false); @Inject FeedManagerImpl(@Scheduler ScheduledExecutorService scheduler, @IoExecutor Executor ioExecutor, DatabaseComponent db, ContactGroupFactory contactGroupFactory, ClientHelper clientHelper, - IdentityManager identityManager, BlogManager blogManager, - BlogPostFactory blogPostFactory, SocketFactory torSocketFactory, - Clock clock) { + BlogManager blogManager, BlogPostFactory blogPostFactory, + FeedFactory feedFactory, SocketFactory torSocketFactory, + Clock clock, Dns noDnsLookups) { this.scheduler = scheduler; this.ioExecutor = ioExecutor; this.db = db; this.contactGroupFactory = contactGroupFactory; this.clientHelper = clientHelper; - this.identityManager = identityManager; this.blogManager = blogManager; this.blogPostFactory = blogPostFactory; + this.feedFactory = feedFactory; this.torSocketFactory = torSocketFactory; this.clock = clock; + this.noDnsLookups = noDnsLookups; } @Override @@ -158,21 +158,21 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { } @Override - public void addFeed(String url, GroupId g) throws DbException, IOException { - LOG.info("Adding new RSS feed..."); - - // TODO check for existing feed? - // fetch feed to get its metadata - Feed feed = new Feed(url, g, clock.currentTimeMillis()); + public void addFeed(String url) throws DbException, IOException { + // fetch syndication feed to get its metadata + SyndFeed f; try { - feed = fetchFeed(feed, false); + f = fetchSyndFeed(url); } catch (FeedException e) { throw new IOException(e); } - // store feed + Feed feed = feedFactory.createFeed(url, f); + + // store feed and new blog Transaction txn = db.startTransaction(false); try { + blogManager.addBlog(txn, feed.getBlog()); List feeds = getFeeds(txn); feeds.add(feed); storeFeeds(txn, feeds); @@ -181,10 +181,10 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { db.endTransaction(txn); } - // fetch feed again, post entries this time + // fetch feed again and post entries Feed updatedFeed; try { - updatedFeed = fetchFeed(feed, true); + updatedFeed = fetchFeed(feed); } catch (FeedException e) { throw new IOException(e); } @@ -203,27 +203,35 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { } @Override - public void removeFeed(String url) throws DbException { + public void removeFeed(Feed feed) throws DbException { LOG.info("Removing RSS feed..."); Transaction txn = db.startTransaction(false); try { - List feeds = getFeeds(txn); - boolean found = false; - for (Feed feed : feeds) { - if (feed.getUrl().equals(url)) { - found = true; - feeds.remove(feed); - break; - } - } - if (!found) throw new DbException(); - storeFeeds(txn, feeds); + // this will call removingBlog() where the feed itself gets removed + blogManager.removeBlog(txn, feed.getBlog()); db.commitTransaction(txn); } finally { db.endTransaction(txn); } } + @Override + public void removingBlog(Transaction txn, Blog b) throws DbException { + if (!b.isRssFeed()) return; + + // delete blog's RSS feed if we have it + boolean found = false; + List feeds = getFeeds(txn); + for (Feed f : feeds) { + if (f.getBlogId().equals(b.getId())) { + found = true; + feeds.remove(f); + break; + } + } + if (found) storeFeeds(txn, feeds); + } + @Override public List getFeeds() throws DbException { List feeds; @@ -246,7 +254,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { for (Object object : d.getList(KEY_FEEDS)) { if (!(object instanceof BdfDictionary)) throw new FormatException(); - feeds.add(Feed.from((BdfDictionary) object)); + feeds.add(feedFactory.createFeed((BdfDictionary) object)); } } catch (FormatException e) { throw new DbException(e); @@ -259,7 +267,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { BdfList feedList = new BdfList(); for (Feed feed : feeds) { - feedList.add(feed.toBdfDictionary()); + feedList.add(feedFactory.feedToBdfDictionary(feed)); } BdfDictionary gm = BdfDictionary.of(new BdfEntry(KEY_FEEDS, feedList)); try { @@ -300,7 +308,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { List newFeeds = new ArrayList(feeds.size()); for (Feed feed : feeds) { try { - newFeeds.add(fetchFeed(feed, true)); + newFeeds.add(fetchFeed(feed)); } catch (FeedException e) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -323,49 +331,52 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { LOG.info("Done updating RSS feeds"); } - private Feed fetchFeed(Feed feed, boolean post) - throws FeedException, IOException, DbException { - String title, description, author; - long updated = clock.currentTimeMillis(); - long lastEntryTime = feed.getLastEntryTime(); - - SyndFeed f = getSyndFeed(getFeedInputStream(feed.getUrl())); - title = StringUtils.isNullOrEmpty(f.getTitle()) ? null : f.getTitle(); - if (title != null) title = clean(title, STRIP_ALL); - description = StringUtils.isNullOrEmpty(f.getDescription()) ? null : - f.getDescription(); - if (description != null) description = clean(description, STRIP_ALL); - author = - StringUtils.isNullOrEmpty(f.getAuthor()) ? null : f.getAuthor(); - if (author != null) author = clean(author, STRIP_ALL); + private SyndFeed fetchSyndFeed(String url) + throws FeedException, IOException { + // fetch feed + SyndFeed f = getSyndFeed(getFeedInputStream(url)); if (f.getEntries().size() == 0) throw new FeedException("Feed has no entries"); + // clean title + String title = + StringUtils.isNullOrEmpty(f.getTitle()) ? null : f.getTitle(); + if (title != null) title = clean(title, STRIP_ALL); + f.setTitle(title); + + // clean description + String description = + StringUtils.isNullOrEmpty(f.getDescription()) ? null : + f.getDescription(); + if (description != null) description = clean(description, STRIP_ALL); + f.setDescription(description); + + // clean author + String author = + StringUtils.isNullOrEmpty(f.getAuthor()) ? null : f.getAuthor(); + if (author != null) author = clean(author, STRIP_ALL); + f.setAuthor(author); + + return f; + } + + private Feed fetchFeed(Feed feed) + throws FeedException, IOException, DbException { + // fetch and clean feed + SyndFeed f = fetchSyndFeed(feed.getUrl()); + // sort and add new entries - if (post) { - lastEntryTime = postFeedEntries(feed, f.getEntries()); - } - return new Feed(feed.getUrl(), feed.getBlogId(), title, description, - author, feed.getAdded(), updated, lastEntryTime); + long lastEntryTime = postFeedEntries(feed, f.getEntries()); + + return feedFactory.createFeed(feed, f, lastEntryTime); } private InputStream getFeedInputStream(String url) throws IOException { - // Don't make local DNS lookups - Dns noLookups = new Dns() { - @Override - public List lookup(String hostname) - throws UnknownHostException { - InetAddress unspecified = - InetAddress.getByAddress(hostname, UNSPECIFIED_ADDRESS); - return Collections.singletonList(unspecified); - } - }; - // Build HTTP Client OkHttpClient client = new OkHttpClient.Builder() .socketFactory(torSocketFactory) - .dns(noLookups) + .dns(noDnsLookups) // Don't make local DNS lookups .connectTimeout(CONNECT_TIMEOUT, MILLISECONDS) .build(); @@ -422,9 +433,8 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { // build post body StringBuilder b = new StringBuilder(); - if (feed.getTitle() != null) { - b.append("

").append(feed.getTitle()).append("

"); - } + b.append("

").append(feed.getTitle()).append("

"); + if (!StringUtils.isNullOrEmpty(entry.getTitle())) { b.append("

").append(entry.getTitle()).append("

"); } @@ -461,9 +471,9 @@ class FeedManagerImpl implements FeedManager, Client, EventListener { String body = getPostBody(b.toString()); try { // create and store post - LocalAuthor author = identityManager.getLocalAuthor(txn); + LocalAuthor localAuthor = feed.getLocalAuthor(); BlogPost post = blogPostFactory - .createBlogPost(groupId, time, null, author, body); + .createBlogPost(groupId, time, null, localAuthor, body); blogManager.addLocalPost(txn, post); } catch (DbException e) { if (LOG.isLoggable(WARNING)) diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java index d47a088b8..c4b567c18 100644 --- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java @@ -2,6 +2,7 @@ package org.briarproject.briar.feed; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.feed.FeedManager; import javax.inject.Inject; @@ -21,11 +22,18 @@ public class FeedModule { @Provides @Singleton FeedManager provideFeedManager(FeedManagerImpl feedManager, - LifecycleManager lifecycleManager, EventBus eventBus) { + LifecycleManager lifecycleManager, EventBus eventBus, + BlogManager blogManager) { lifecycleManager.registerClient(feedManager); eventBus.addListener(feedManager); + blogManager.registerRemoveBlogHook(feedManager); return feedManager; } + @Provides + FeedFactory provideFeedFactory(FeedFactoryImpl feedFactory) { + return feedFactory; + } + } diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java b/briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java new file mode 100644 index 000000000..5f5cc8c44 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java @@ -0,0 +1,28 @@ +package org.briarproject.briar.feed; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Collections; +import java.util.List; + +import javax.inject.Inject; + +import okhttp3.Dns; + +class NoDns implements Dns { + + private static final byte[] UNSPECIFIED_ADDRESS = new byte[4]; + + @Inject + public NoDns() { + } + + @Override + public List lookup(String hostname) + throws UnknownHostException { + InetAddress unspecified = + InetAddress.getByAddress(hostname, UNSPECIFIED_ADDRESS); + return Collections.singletonList(unspecified); + } + +} diff --git a/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java b/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java index 6f0df8e47..f9b378ef7 100644 --- a/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java +++ b/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java @@ -39,14 +39,20 @@ class BlogSharingValidator extends SharingValidator { @Override protected GroupId validateDescriptor(BdfList descriptor) throws FormatException { - checkSize(descriptor, 2); + checkSize(descriptor, 3); String name = descriptor.getString(0); checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH); byte[] publicKey = descriptor.getRaw(1); checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH); + boolean rssFeed = descriptor.getBoolean(2); Author author = authorFactory.createAuthor(name, publicKey); - Blog blog = blogFactory.createBlog(author); + Blog blog; + if (rssFeed) { + blog = blogFactory.createFeedBlog(author); + } else { + blog = blogFactory.createBlog(author); + } return blog.getId(); } diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java index e2b5e8ef5..4ff1bb75c 100644 --- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java @@ -297,7 +297,7 @@ public class BlogManagerImplTest extends BriarTestCase { final LocalAuthor localAuthor = new LocalAuthor(authorId, "Author", publicKey, privateKey, created); - return new Blog(group, localAuthor); + return new Blog(group, localAuthor, false); } private BdfDictionary authorToBdfDictionary(Author a) { diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java index 643fe482c..165c1fab5 100644 --- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java @@ -1,5 +1,7 @@ package org.briarproject.briar.blog; +import org.briarproject.bramble.api.db.Transaction; +import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.test.TestDatabaseModule; import org.briarproject.briar.api.blog.Blog; @@ -32,7 +34,7 @@ public class BlogManagerIntegrationTest extends BriarIntegrationTest { private BlogManager blogManager0, blogManager1; - private Blog blog0, blog1; + private Blog blog0, blog1, rssBlog; @Rule public ExpectedException thrown = ExpectedException.none(); @@ -50,6 +52,12 @@ public class BlogManagerIntegrationTest blog0 = blogFactory.createBlog(author0); blog1 = blogFactory.createBlog(author1); + + rssBlog = blogFactory.createFeedBlog(author0); + Transaction txn = db0.startTransaction(false); + blogManager0.addBlog(txn, rssBlog); + db0.commitTransaction(txn); + db0.endTransaction(txn); } @Override @@ -393,4 +401,63 @@ public class BlogManagerIntegrationTest assertEquals(2, headers0.size()); } + @Test + public void testFeedPost() throws Exception { + assertTrue(rssBlog.isRssFeed()); + + // add a feed post to rssBlog + final String body = getRandomString(42); + BlogPost p = blogPostFactory + .createBlogPost(rssBlog.getId(), clock.currentTimeMillis(), + null, author0, body); + blogManager0.addLocalPost(p); + + // make sure it got saved as an RSS feed post + Collection headers = + blogManager0.getPostHeaders(rssBlog.getId()); + assertEquals(1, headers.size()); + BlogPostHeader header = headers.iterator().next(); + assertEquals(POST, header.getType()); + assertEquals(Author.Status.NONE, header.getAuthorStatus()); + assertTrue(header.isRssFeed()); + } + + @Test + public void testFeedReblog() throws Exception { + // add a feed post to rssBlog + final String body = getRandomString(42); + BlogPost p = blogPostFactory + .createBlogPost(rssBlog.getId(), clock.currentTimeMillis(), + null, author0, body); + blogManager0.addLocalPost(p); + + // reblog feed post to own blog + Collection headers = + blogManager0.getPostHeaders(rssBlog.getId()); + assertEquals(1, headers.size()); + BlogPostHeader header = headers.iterator().next(); + blogManager0.addLocalComment(author0, blog0.getId(), null, header); + + // make sure it got saved as an RSS feed post + headers = blogManager0.getPostHeaders(blog0.getId()); + assertEquals(1, headers.size()); + BlogCommentHeader commentHeader = + (BlogCommentHeader) headers.iterator().next(); + assertEquals(COMMENT, commentHeader.getType()); + assertTrue(commentHeader.getParent().isRssFeed()); + + // reblog reblogged post again to own blog + blogManager0 + .addLocalComment(author0, blog0.getId(), null, commentHeader); + + // make sure it got saved as an RSS feed post + headers = blogManager0.getPostHeaders(blog0.getId()); + assertEquals(2, headers.size()); + for (BlogPostHeader h: headers) { + assertTrue(h instanceof BlogCommentHeader); + assertEquals(COMMENT, h.getType()); + assertTrue(((BlogCommentHeader) h).getRootPost().isRssFeed()); + } + } + } diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java index 1ac77a550..d3731d19d 100644 --- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java @@ -79,7 +79,7 @@ public class BlogPostValidatorTest extends BriarTestCase { new BdfEntry(KEY_AUTHOR_NAME, author.getName()), new BdfEntry(KEY_PUBLIC_KEY, author.getPublicKey()) ); - blog = new Blog(group, author); + blog = new Blog(group, author, false); MessageId messageId = new MessageId(TestUtils.getRandomId()); long timestamp = System.currentTimeMillis(); diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java new file mode 100644 index 000000000..2fc24aae5 --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java @@ -0,0 +1,127 @@ +package org.briarproject.briar.feed; + +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.contact.ContactModule; +import org.briarproject.bramble.crypto.CryptoModule; +import org.briarproject.bramble.identity.IdentityModule; +import org.briarproject.bramble.lifecycle.LifecycleModule; +import org.briarproject.bramble.sync.SyncModule; +import org.briarproject.bramble.system.SystemModule; +import org.briarproject.bramble.test.TestDatabaseModule; +import org.briarproject.bramble.test.TestUtils; +import org.briarproject.bramble.transport.TransportModule; +import org.briarproject.briar.api.blog.Blog; +import org.briarproject.briar.api.blog.BlogManager; +import org.briarproject.briar.api.blog.BlogPostHeader; +import org.briarproject.briar.api.feed.Feed; +import org.briarproject.briar.api.feed.FeedManager; +import org.briarproject.briar.blog.BlogModule; +import org.briarproject.briar.test.BriarTestCase; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class FeedManagerIntegrationTest extends BriarTestCase { + + private LifecycleManager lifecycleManager; + private FeedManager feedManager; + private BlogManager blogManager; + private final File testDir = TestUtils.getTestDirectory(); + private final File testFile = new File(testDir, "feedTest"); + + @Before + public void setUp() throws Exception { + assertTrue(testDir.mkdirs()); + FeedManagerIntegrationTestComponent component = + DaggerFeedManagerIntegrationTestComponent.builder() + .testDatabaseModule(new TestDatabaseModule(testFile)) + .build(); + component.inject(this); + injectEagerSingletons(component); + + lifecycleManager = component.getLifecycleManager(); + lifecycleManager.startServices("feedTest"); + lifecycleManager.waitForStartup(); + + feedManager = component.getFeedManager(); + blogManager = component.getBlogManager(); + } + + @Test + public void testFeedImportAndRemoval() throws Exception { + // initially, there's only the one personal blog + Collection blogs = blogManager.getBlogs(); + assertEquals(1, blogs.size()); + Blog personalBlog = blogs.iterator().next(); + + // add feed into a dedicated blog + String url = "https://www.schneier.com/blog/atom.xml"; + feedManager.addFeed(url); + + // then there's the feed's blog now + blogs = blogManager.getBlogs(); + assertEquals(2, blogs.size()); + Blog feedBlog = null; + for (Blog blog : blogs) { + if (!blog.equals(personalBlog)) feedBlog = blog; + } + assertNotNull(feedBlog); + + // check the feed got saved as expected + Collection feeds = feedManager.getFeeds(); + assertEquals(1, feeds.size()); + Feed feed = feeds.iterator().next(); + assertTrue(feed.getLastEntryTime() > 0); + assertTrue(feed.getAdded() > 0); + assertTrue(feed.getUpdated() > 0); + assertEquals(url, feed.getUrl()); + assertEquals(feedBlog, feed.getBlog()); + assertEquals("Schneier on Security", feed.getTitle()); + assertEquals("A blog covering security and security technology.", + feed.getDescription()); + assertEquals(feed.getTitle(), feed.getBlog().getName()); + assertEquals(feed.getTitle(), feed.getLocalAuthor().getName()); + + // check the feed entries have been added to the blog as expected + Collection headers = + blogManager.getPostHeaders(feedBlog.getId()); + for (BlogPostHeader header : headers) { + assertTrue(header.isRssFeed()); + } + + // now let's remove the feed's blog again + blogManager.removeBlog(feedBlog); + blogs = blogManager.getBlogs(); + assertEquals(1, blogs.size()); + assertEquals(personalBlog, blogs.iterator().next()); + assertEquals(0, feedManager.getFeeds().size()); + } + + @After + public void tearDown() throws Exception { + lifecycleManager.stopServices(); + lifecycleManager.waitForShutdown(); + TestUtils.deleteTestDirectory(testDir); + } + + protected void injectEagerSingletons( + FeedManagerIntegrationTestComponent component) { + component.inject(new FeedModule.EagerSingletons()); + component.inject(new BlogModule.EagerSingletons()); + component.inject(new ContactModule.EagerSingletons()); + component.inject(new CryptoModule.EagerSingletons()); + component.inject(new IdentityModule.EagerSingletons()); + component.inject(new LifecycleModule.EagerSingletons()); + component.inject(new SyncModule.EagerSingletons()); + component.inject(new SystemModule.EagerSingletons()); + component.inject(new TransportModule.EagerSingletons()); + } + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java new file mode 100644 index 000000000..ce5ec5a00 --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java @@ -0,0 +1,79 @@ +package org.briarproject.briar.feed; + +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.client.ClientModule; +import org.briarproject.bramble.contact.ContactModule; +import org.briarproject.bramble.crypto.CryptoModule; +import org.briarproject.bramble.data.DataModule; +import org.briarproject.bramble.db.DatabaseModule; +import org.briarproject.bramble.event.EventModule; +import org.briarproject.bramble.identity.IdentityModule; +import org.briarproject.bramble.lifecycle.LifecycleModule; +import org.briarproject.bramble.sync.SyncModule; +import org.briarproject.bramble.system.SystemModule; +import org.briarproject.bramble.test.TestDatabaseModule; +import org.briarproject.bramble.test.TestPluginConfigModule; +import org.briarproject.bramble.test.TestSeedProviderModule; +import org.briarproject.bramble.test.TestSocksModule; +import org.briarproject.bramble.transport.TransportModule; +import org.briarproject.briar.api.blog.BlogManager; +import org.briarproject.briar.api.feed.FeedManager; +import org.briarproject.briar.blog.BlogModule; +import org.briarproject.briar.client.BriarClientModule; +import org.briarproject.briar.test.TestDnsModule; + +import javax.inject.Singleton; + +import dagger.Component; + +@Singleton +@Component(modules = { + TestDatabaseModule.class, + TestPluginConfigModule.class, + TestSeedProviderModule.class, + TestSocksModule.class, + TestDnsModule.class, + LifecycleModule.class, + BriarClientModule.class, + ClientModule.class, + ContactModule.class, + CryptoModule.class, + BlogModule.class, + FeedModule.class, + DataModule.class, + DatabaseModule.class, + EventModule.class, + IdentityModule.class, + SyncModule.class, + SystemModule.class, + TransportModule.class +}) +interface FeedManagerIntegrationTestComponent { + + void inject(FeedManagerIntegrationTest testCase); + + void inject(FeedModule.EagerSingletons init); + + void inject(BlogModule.EagerSingletons init); + + void inject(ContactModule.EagerSingletons init); + + void inject(CryptoModule.EagerSingletons init); + + void inject(IdentityModule.EagerSingletons init); + + void inject(LifecycleModule.EagerSingletons init); + + void inject(SyncModule.EagerSingletons init); + + void inject(SystemModule.EagerSingletons init); + + void inject(TransportModule.EagerSingletons init); + + LifecycleManager getLifecycleManager(); + + FeedManager getFeedManager(); + + BlogManager getBlogManager(); + +} diff --git a/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java index c67360d64..7c4c96ab2 100644 --- a/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java +++ b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java @@ -23,8 +23,8 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { private final byte[] publicKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH); private final Author author = new Author(authorId, authorName, publicKey); - private final Blog blog = new Blog(group, author); - private final BdfList descriptor = BdfList.of(authorName, publicKey); + private final Blog blog = new Blog(group, author, false); + private final BdfList descriptor = BdfList.of(authorName, publicKey, false); private final String content = TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH); @@ -64,7 +64,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsNullBlogName() throws Exception { - BdfList invalidDescriptor = BdfList.of(null, publicKey); + BdfList invalidDescriptor = BdfList.of(null, publicKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -72,7 +72,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsNonStringBlogName() throws Exception { - BdfList invalidDescriptor = BdfList.of(123, publicKey); + BdfList invalidDescriptor = BdfList.of(123, publicKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -80,7 +80,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsTooShortBlogName() throws Exception { - BdfList invalidDescriptor = BdfList.of("", publicKey); + BdfList invalidDescriptor = BdfList.of("", publicKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -89,7 +89,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test public void testAcceptsMinLengthBlogName() throws Exception { String shortBlogName = TestUtils.getRandomString(1); - BdfList validDescriptor = BdfList.of(shortBlogName, publicKey); + BdfList validDescriptor = BdfList.of(shortBlogName, publicKey, false); expectCreateBlog(shortBlogName, publicKey); expectEncodeMetadata(INVITE); BdfMessageContext messageContext = v.validateMessage(message, group, @@ -102,7 +102,8 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { public void testRejectsTooLongBlogName() throws Exception { String invalidBlogName = TestUtils.getRandomString(MAX_BLOG_NAME_LENGTH + 1); - BdfList invalidDescriptor = BdfList.of(invalidBlogName, publicKey); + BdfList invalidDescriptor = + BdfList.of(invalidBlogName, publicKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -110,7 +111,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsNullPublicKey() throws Exception { - BdfList invalidDescriptor = BdfList.of(authorName, null); + BdfList invalidDescriptor = BdfList.of(authorName, null, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -118,7 +119,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsNonRawPublicKey() throws Exception { - BdfList invalidDescriptor = BdfList.of(authorName, 123); + BdfList invalidDescriptor = BdfList.of(authorName, 123, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -127,7 +128,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test(expected = FormatException.class) public void testRejectsTooLongPublicKey() throws Exception { byte[] invalidKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1); - BdfList invalidDescriptor = BdfList.of(authorName, invalidKey); + BdfList invalidDescriptor = BdfList.of(authorName, invalidKey, false); v.validateMessage(message, group, BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor, null)); @@ -136,7 +137,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest { @Test public void testAcceptsMinLengthPublicKey() throws Exception { byte[] key = TestUtils.getRandomBytes(1); - BdfList validDescriptor = BdfList.of(authorName, key); + BdfList validDescriptor = BdfList.of(authorName, key, false); expectCreateBlog(authorName, key); expectEncodeMetadata(INVITE); diff --git a/briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java b/briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java new file mode 100644 index 000000000..13323b346 --- /dev/null +++ b/briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java @@ -0,0 +1,15 @@ +package org.briarproject.briar.test; + +import dagger.Module; +import dagger.Provides; +import okhttp3.Dns; + +@Module +public class TestDnsModule { + + @Provides + Dns provideDns() { + return Dns.SYSTEM; + } + +}