mirror of
https://github.com/M66B/FairEmail.git
synced 2026-05-19 05:29:50 +02:00
Better link sanitization
This commit is contained in:
1
FAQ.md
1
FAQ.md
@@ -1543,7 +1543,6 @@ The advanced option *extra privacy features* enables:
|
||||
|
||||
* Looking up the owner of the IP address of a link
|
||||
* Detection and removal of [tracking images](#user-content-faq82)
|
||||
* Removal of [Urchin Tracking Module (UTM) parameters](https://en.wikipedia.org/wiki/UTM_parameters) from links
|
||||
|
||||
<br />
|
||||
|
||||
|
||||
@@ -44,12 +44,14 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.ContactsContract;
|
||||
import android.provider.Settings;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.Layout;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.method.ArrowKeyMovementMethod;
|
||||
import android.text.style.DynamicDrawableSpan;
|
||||
import android.text.style.ImageSpan;
|
||||
@@ -2211,56 +2213,105 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
boolean paranoid = prefs.getBoolean("paranoid", true);
|
||||
|
||||
final Uri _uri;
|
||||
if (paranoid && !uri.isOpaque()) {
|
||||
final Uri sanitized;
|
||||
if (uri.isOpaque())
|
||||
sanitized = uri;
|
||||
else {
|
||||
// https://en.wikipedia.org/wiki/UTM_parameters
|
||||
Uri.Builder builder = new Uri.Builder();
|
||||
|
||||
String scheme = uri.getScheme();
|
||||
if (!TextUtils.isEmpty(scheme))
|
||||
builder.scheme(scheme);
|
||||
|
||||
String authority = uri.getEncodedAuthority();
|
||||
if (!TextUtils.isEmpty(authority))
|
||||
builder.encodedAuthority(authority);
|
||||
|
||||
String path = uri.getEncodedPath();
|
||||
if (!TextUtils.isEmpty(path))
|
||||
builder.encodedPath(path);
|
||||
Uri.Builder builder = uri.buildUpon();
|
||||
|
||||
builder.clearQuery();
|
||||
for (String key : uri.getQueryParameterNames())
|
||||
if (!PARANOID_QUERY.contains(key.toLowerCase()))
|
||||
for (String value : uri.getQueryParameters(key))
|
||||
builder.appendQueryParameter(key, value);
|
||||
if (!TextUtils.isEmpty(key)) {
|
||||
Log.i("Query " + key + "=" + value);
|
||||
builder.appendQueryParameter(key, value);
|
||||
}
|
||||
|
||||
String fragment = uri.getEncodedFragment();
|
||||
if (!TextUtils.isEmpty(fragment))
|
||||
builder.encodedFragment(fragment);
|
||||
|
||||
_uri = builder.build();
|
||||
|
||||
Log.i("Source uri=" + uri);
|
||||
Log.i("Target uri=" + _uri);
|
||||
} else
|
||||
_uri = uri;
|
||||
sanitized = builder.build();
|
||||
}
|
||||
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_open_link, null);
|
||||
TextView tvTitle = view.findViewById(R.id.tvTitle);
|
||||
final EditText etLink = view.findViewById(R.id.etLink);
|
||||
TextView tvInsecure = view.findViewById(R.id.tvInsecure);
|
||||
final CheckBox cbSecure = view.findViewById(R.id.cbSecure);
|
||||
CheckBox cbSanitize = view.findViewById(R.id.cbSanitize);
|
||||
final TextView tvOwner = view.findViewById(R.id.tvOwner);
|
||||
final Group grpOwner = view.findViewById(R.id.grpOwner);
|
||||
|
||||
tvTitle.setText(title);
|
||||
tvTitle.setVisibility(TextUtils.isEmpty(title) ? View.GONE : View.VISIBLE);
|
||||
|
||||
etLink.setText(_uri.toString());
|
||||
tvInsecure.setVisibility("http".equals(_uri.getScheme()) ? View.VISIBLE : View.GONE);
|
||||
cbSecure.setVisibility(View.GONE);
|
||||
etLink.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence text, int i, int i1, int i2) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable text) {
|
||||
Uri uri = Uri.parse(text.toString());
|
||||
cbSecure.setVisibility(!uri.isOpaque() &&
|
||||
("http".equals(uri.getScheme()) || "https".equals(uri.getScheme()))
|
||||
? View.VISIBLE : View.GONE);
|
||||
}
|
||||
});
|
||||
etLink.setText(uri.toString());
|
||||
|
||||
boolean secure = "https".equals(uri.getScheme());
|
||||
cbSecure.setChecked(secure);
|
||||
cbSecure.setText(
|
||||
secure ? R.string.title_link_secured : R.string.title_secure_link);
|
||||
cbSecure.setTextColor(Helper.resolveColor(context,
|
||||
secure ? android.R.attr.textColorSecondary : R.attr.colorWarning));
|
||||
cbSecure.setTypeface(
|
||||
secure ? Typeface.DEFAULT : Typeface.DEFAULT_BOLD);
|
||||
cbSecure.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
|
||||
Uri uri = Uri.parse(etLink.getText().toString());
|
||||
Uri.Builder builder = uri.buildUpon();
|
||||
|
||||
builder.scheme(checked ? "https" : "http");
|
||||
|
||||
String authority = uri.getEncodedAuthority();
|
||||
if (authority != null) {
|
||||
authority = authority.replace(checked ? ":80" : ":443", checked ? ":443" : ":80");
|
||||
builder.encodedAuthority(authority);
|
||||
}
|
||||
|
||||
etLink.setText(builder.build().toString());
|
||||
|
||||
cbSecure.setText(
|
||||
checked ? R.string.title_link_secured : R.string.title_secure_link);
|
||||
cbSecure.setTextColor(Helper.resolveColor(context,
|
||||
checked ? android.R.attr.textColorSecondary : R.attr.colorWarning));
|
||||
cbSecure.setTypeface(
|
||||
checked ? Typeface.DEFAULT : Typeface.DEFAULT_BOLD);
|
||||
}
|
||||
});
|
||||
|
||||
cbSanitize.setVisibility(uri.equals(sanitized) ? View.GONE : View.VISIBLE);
|
||||
cbSanitize.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
|
||||
if (checked)
|
||||
etLink.setText(sanitized.toString());
|
||||
else
|
||||
etLink.setText(uri.toString());
|
||||
}
|
||||
});
|
||||
|
||||
grpOwner.setVisibility(View.GONE);
|
||||
|
||||
if (paranoid) {
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable("uri", _uri);
|
||||
args.putParcelable("uri", uri);
|
||||
|
||||
new SimpleTask<String>() {
|
||||
@Override
|
||||
|
||||
@@ -39,18 +39,26 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvTitle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvInsecure"
|
||||
<CheckBox
|
||||
android:id="@+id/cbSecure"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/title_insecure_link"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:text="@string/title_secure_link"
|
||||
android:textColor="?attr/colorWarning"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/etLink" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cbSanitize"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/title_sanitize_link"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbSecure" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvOwnerTitle"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -60,7 +68,7 @@
|
||||
android:text="@string/title_ip_owner"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvInsecure" />
|
||||
app:layout_constraintTop_toBottomOf="@id/cbSanitize" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvOwner"
|
||||
|
||||
@@ -692,8 +692,11 @@
|
||||
<string name="title_hint_ignore_contact">Long press a contact to never consider it as a favorite</string>
|
||||
|
||||
<string name="title_open_link">Open link</string>
|
||||
<string name="title_ip_owner">Owner IP address</string>
|
||||
<string name="title_secure_link">Make link secure</string>
|
||||
<string name="title_link_secured">Link is secure</string>
|
||||
<string name="title_sanitize_link">Remove tracking parameters</string>
|
||||
<string name="title_insecure_link">This link is insecure</string>
|
||||
<string name="title_ip_owner">Owner IP address</string>
|
||||
<string name="title_select_app">Select app</string>
|
||||
<string name="title_updated">There is an update to version %1$s available</string>
|
||||
<string name="title_issue">Do you have a question or problem?</string>
|
||||
|
||||
Reference in New Issue
Block a user