diff --git a/briar-android/res/layout/fragment_link_dialog.xml b/briar-android/res/layout/fragment_link_dialog.xml
new file mode 100644
index 000000000..307b7110e
--- /dev/null
+++ b/briar-android/res/layout/fragment_link_dialog.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml
index e86654509..626f3d8a8 100644
--- a/briar-android/res/values/strings.xml
+++ b/briar-android/res/values/strings.xml
@@ -308,6 +308,12 @@
Feedback
Send feedback
+
+ Link Warning
+ You are about to open the following link with an external app.
+ This can be used to identify you. Think about whether you trust the person that sent you this link and consider opening it with Orfox.
+ Open Link
+
Anonymous
New Identity
diff --git a/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java b/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java
index 13a1b3f17..2fb84671e 100644
--- a/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java
+++ b/briar-android/src/org/briarproject/android/blogs/BlogPostViewHolder.java
@@ -30,6 +30,8 @@ import static org.briarproject.android.BriarActivity.GROUP_ID;
import static org.briarproject.android.blogs.BasePostPagerFragment.POST_ID;
import static org.briarproject.android.util.AndroidUtils.TEASER_LENGTH;
import static org.briarproject.android.util.AndroidUtils.getTeaser;
+import static org.briarproject.android.util.AndroidUtils.getSpanned;
+import static org.briarproject.android.util.AndroidUtils.makeLinksClickable;
import static org.briarproject.api.blogs.MessageType.POST;
@UiThread
@@ -108,15 +110,17 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
}
// post body
- CharSequence bodyText = item.getBody();
if (listener == null) {
+ body.setText(getSpanned(item.getBody()));
+ makeLinksClickable(body);
body.setTextIsSelectable(true);
} else {
body.setTextIsSelectable(false);
if (item.getBody().length() > TEASER_LENGTH)
- bodyText = getTeaser(ctx, item.getBody());
+ body.setText(getTeaser(ctx, item.getBody()));
+ else
+ body.setText(item.getBody());
}
- body.setText(bodyText);
// reblog button
reblogButton.setOnClickListener(new OnClickListener() {
diff --git a/briar-android/src/org/briarproject/android/util/AndroidUtils.java b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
index 66593d0f8..6a591f1dc 100644
--- a/briar-android/src/org/briarproject/android/util/AndroidUtils.java
+++ b/briar-android/src/org/briarproject/android/util/AndroidUtils.java
@@ -6,14 +6,24 @@ import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import android.support.design.widget.TextInputLayout;
+import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.text.Html;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
+import android.text.Spanned;
import android.text.format.DateUtils;
+import android.text.method.LinkMovementMethod;
+import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
+import android.text.style.URLSpan;
+import android.view.View;
+import android.widget.TextView;
import org.briarproject.R;
+import org.briarproject.android.widget.LinkDialogFragment;
import org.briarproject.util.IoUtils;
import org.briarproject.util.StringUtils;
@@ -142,4 +152,31 @@ public class AndroidUtils {
return builder;
}
+ public static Spanned getSpanned(String s) {
+ return Html.fromHtml(s);
+ }
+
+ public static void makeLinksClickable(TextView v) {
+ SpannableStringBuilder ssb = new SpannableStringBuilder(v.getText());
+ URLSpan[] spans = ssb.getSpans(0, ssb.length(), URLSpan.class);
+ for (URLSpan span : spans) {
+ int start = ssb.getSpanStart(span);
+ int end = ssb.getSpanEnd(span);
+ final String url = span.getURL();
+ ssb.removeSpan(span);
+ ClickableSpan cSpan = new ClickableSpan() {
+ @Override
+ public void onClick(View v2) {
+ LinkDialogFragment f = LinkDialogFragment.newInstance(url);
+ FragmentManager fm = ((AppCompatActivity) v2.getContext())
+ .getSupportFragmentManager();
+ f.show(fm, f.getUniqueTag());
+ }
+ };
+ ssb.setSpan(cSpan, start, end, 0);
+ }
+ v.setText(ssb);
+ v.setMovementMethod(LinkMovementMethod.getInstance());
+ }
+
}
diff --git a/briar-android/src/org/briarproject/android/widget/LinkDialogFragment.java b/briar-android/src/org/briarproject/android/widget/LinkDialogFragment.java
new file mode 100644
index 000000000..1a6e636ee
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/widget/LinkDialogFragment.java
@@ -0,0 +1,77 @@
+package org.briarproject.android.widget;
+
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import org.briarproject.R;
+
+
+public class LinkDialogFragment extends DialogFragment {
+
+ private static final String TAG = LinkDialogFragment.class.getName();
+
+ private String url;
+
+ public static LinkDialogFragment newInstance(String url) {
+ LinkDialogFragment f = new LinkDialogFragment();
+
+ Bundle args = new Bundle();
+ args.putString("url", url);
+ f.setArguments(args);
+
+ return f;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ url = getArguments().getString("url");
+
+ setStyle(STYLE_NO_TITLE, R.style.BriarDialogTheme);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+
+ View v = inflater.inflate(R.layout.fragment_link_dialog, container,
+ false);
+
+ TextView urlView = (TextView) v.findViewById(R.id.urlView);
+ urlView.setText(url);
+
+ Button openButton = (Button) v.findViewById(R.id.openButton);
+ openButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ startActivity(i);
+ }
+ });
+
+ Button cancelButton = (Button) v.findViewById(R.id.cancelButton);
+ cancelButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ getDialog().cancel();
+ }
+ });
+
+ return v;
+ }
+
+ public String getUniqueTag() {
+ return TAG;
+ }
+
+}
diff --git a/briar-core/build.gradle b/briar-core/build.gradle
index ae5071be7..9ffd0987f 100644
--- a/briar-core/build.gradle
+++ b/briar-core/build.gradle
@@ -13,6 +13,7 @@ dependencies {
compile 'org.jdom:jdom2:2.0.6'
compile 'org.slf4j:slf4j-api:1.7.21'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
+ compile 'org.jsoup:jsoup:1.9.2'
}
dependencyVerification {
@@ -25,6 +26,7 @@ dependencyVerification {
'com.squareup.okhttp3:okhttp:a47f4efa166551cd5acc04f1071d82dafbf05638c21f9ca13068bc6633e3bff6',
'com.rometools:rome-utils:2be18a1edc601c31fe49c2000bb5484dd75182309270c2a2561d71888d81587a',
'com.squareup.okio:okio:5cfea5afe6c6e441a4dbf6053a07a733b1249d1009382eb44ac2255ccedd0c15',
+ 'org.jsoup:jsoup:9c1885f1b182256e06f1e30b8451caed0c0dee96299d6348f968d18b54d0a46a',
]
}
diff --git a/briar-core/src/org/briarproject/feed/FeedManagerImpl.java b/briar-core/src/org/briarproject/feed/FeedManagerImpl.java
index 6384f06c0..b9263c062 100644
--- a/briar-core/src/org/briarproject/feed/FeedManagerImpl.java
+++ b/briar-core/src/org/briarproject/feed/FeedManagerImpl.java
@@ -66,6 +66,9 @@ import static org.briarproject.api.feed.FeedConstants.FETCH_DELAY_INITIAL;
import static org.briarproject.api.feed.FeedConstants.FETCH_INTERVAL;
import static org.briarproject.api.feed.FeedConstants.FETCH_UNIT;
import static org.briarproject.api.feed.FeedConstants.KEY_FEEDS;
+import static org.briarproject.util.HtmlUtils.article;
+import static org.briarproject.util.HtmlUtils.clean;
+import static org.briarproject.util.HtmlUtils.stripAll;
class FeedManagerImpl implements FeedManager, Client, EventListener {
@@ -337,13 +340,13 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
SyndFeed f = getSyndFeed(getFeedInputStream(feed.getUrl()));
title = StringUtils.isNullOrEmpty(f.getTitle()) ? null : f.getTitle();
- if (title != null) title = stripHTML(title);
+ if (title != null) title = clean(title, stripAll);
description = StringUtils.isNullOrEmpty(f.getDescription()) ? null :
f.getDescription();
- if (description != null) description = stripHTML(description);
+ if (description != null) description = clean(description, stripAll);
author =
StringUtils.isNullOrEmpty(f.getAuthor()) ? null : f.getAuthor();
- if (author != null) author = stripHTML(author);
+ if (author != null) author = clean(author, stripAll);
if (f.getEntries().size() == 0)
throw new FeedException("Feed has no entries");
@@ -418,23 +421,23 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
// build post body
StringBuilder b = new StringBuilder();
if (feed.getTitle() != null) {
- // HTML in feed title was already stripped
- b.append(feed.getTitle()).append("\n\n");
+ b.append("
").append(feed.getTitle()).append("
");
}
if (!StringUtils.isNullOrEmpty(entry.getTitle())) {
- b.append(stripHTML(entry.getTitle())).append("\n\n");
+ b.append("").append(entry.getTitle()).append("
");
}
for (SyndContent content : entry.getContents()) {
- // extract content and do a very simple HTML tag stripping
if (content.getValue() != null)
- b.append(stripHTML(content.getValue()));
+ b.append(content.getValue());
}
if (entry.getContents().size() == 0) {
- if (entry.getDescription().getValue() != null)
- b.append(stripHTML(entry.getDescription().getValue()));
+ if (entry.getDescription() != null &&
+ entry.getDescription().getValue() != null)
+ b.append(entry.getDescription().getValue());
}
+ b.append("");
if (!StringUtils.isNullOrEmpty(entry.getAuthor())) {
- b.append("\n\n-- ").append(stripHTML(entry.getAuthor()));
+ b.append("\n\n-- ").append(clean(entry.getAuthor(), stripAll));
}
if (entry.getPublishedDate() != null) {
b.append(" (").append(entry.getPublishedDate().toString())
@@ -443,8 +446,11 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
b.append(" (").append(entry.getUpdatedDate().toString())
.append(")");
}
- if (!StringUtils.isNullOrEmpty(entry.getLink())) {
- b.append("\n\n").append(stripHTML(entry.getLink()));
+ b.append("
");
+ String link = entry.getLink();
+ if (!StringUtils.isNullOrEmpty(link)) {
+ b.append("").append(link)
+ .append("");
}
// get other information for post
@@ -476,12 +482,8 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
}
}
- private String stripHTML(String s) {
- s = s.replaceAll("(?s).*?", "");
- return StringUtils.trim(s.replaceAll("<(?s).*?>", ""));
- }
-
private String getPostBody(String text) {
+ text = clean(text, article);
if (text.length() <= MAX_BLOG_POST_BODY_LENGTH) return text;
else return text.substring(0, MAX_BLOG_POST_BODY_LENGTH);
}
diff --git a/briar-core/src/org/briarproject/util/HtmlUtils.java b/briar-core/src/org/briarproject/util/HtmlUtils.java
new file mode 100644
index 000000000..e9c338401
--- /dev/null
+++ b/briar-core/src/org/briarproject/util/HtmlUtils.java
@@ -0,0 +1,16 @@
+package org.briarproject.util;
+
+import org.jsoup.Jsoup;
+import org.jsoup.safety.Whitelist;
+
+public class HtmlUtils {
+
+ public static Whitelist stripAll = Whitelist.none();
+ public static Whitelist article =
+ Whitelist.basic().addTags("h1", "h2", "h3", "h4", "h5", "h6");
+
+ public static String clean(String s, Whitelist list) {
+ return Jsoup.clean(s, list);
+ }
+
+}
diff --git a/briar-core/src/org/briarproject/util/PrivacyUtils.java b/briar-core/src/org/briarproject/util/PrivacyUtils.java
index 247ccda3f..c2bf42628 100644
--- a/briar-core/src/org/briarproject/util/PrivacyUtils.java
+++ b/briar-core/src/org/briarproject/util/PrivacyUtils.java
@@ -1,6 +1,5 @@
package org.briarproject.util;
-import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;