From 86ed20a1bd822b94824379614c0851c29f6d3e38 Mon Sep 17 00:00:00 2001 From: M66B Date: Thu, 9 Jun 2022 09:19:15 +0200 Subject: [PATCH] Removed Gravatar/Libravatar from Play store release --- PRIVACY.md | 42 +++--- .../dummy/java/eu/faircode/email/Avatar.java | 49 +++++++ .../extra/java/eu/faircode/email/Avatar.java | 120 ++++++++++++++++++ .../eu/faircode/email/AdapterMessage.java | 4 +- .../java/eu/faircode/email/ContactInfo.java | 95 +------------- .../email/FragmentOptionsDisplay.java | 7 +- .../main/java/eu/faircode/email/Helper.java | 2 - .../res/layout/fragment_options_display.xml | 8 ++ 8 files changed, 209 insertions(+), 118 deletions(-) create mode 100644 app/src/dummy/java/eu/faircode/email/Avatar.java create mode 100644 app/src/extra/java/eu/faircode/email/Avatar.java diff --git a/PRIVACY.md b/PRIVACY.md index 9414406225..9c38b53ee0 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -37,9 +37,9 @@ FairEmail **can use** these services if they are explicitly enabled (off by defa * [Thunderbird autoconfiguration](https://wiki.mozilla.org/Thunderbird:Autoconfiguration) – [Privacy policy](https://www.mozilla.org/privacy/) * [DeepL](https://www.deepl.com/) – [Privacy policy](https://www.deepl.com/privacy/) * [LanguageTool](https://languagetool.org/) – [Privacy policy](https://languagetool.org/legal/privacy) -* [Gravatar](https://gravatar.com/) – [Privacy policy](https://automattic.com/privacy/) -* [Libravatar](https://www.libravatar.org/) – [Privacy policy](https://www.libravatar.org/privacy/) -* [GitHub](https://github.com/) – [Privacy policy](https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement) +* [Gravatar](https://gravatar.com/) (GitHub version only) – [Privacy policy](https://automattic.com/privacy/) +* [Libravatar](https://www.libravatar.org/) (GitHub version only) – [Privacy policy](https://www.libravatar.org/privacy/) +* [GitHub](https://github.com/) (GitHub version only) – [Privacy policy](https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement) FairEmail **can access** the websites at the domain names of email addresses (username@domain.name) if [Brand Indicators for Message Identification](https://en.wikipedia.org/wiki/Brand_Indicators_for_Message_Identification) (BIMI) @@ -58,24 +58,24 @@ FairEmail **is** [GDPR compliant](https://gdpr.eu/). This table provides a complete overview of all shared data and the conditions under which data will be shared: -| Service/function | Data sent | When the data will be sent | -| ----------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------- | -| Mozilla autoconfig| Email address of email accounts | Upon configuring an email account | -| Email server | Login credentials, messages sent | Upon configuring and using an account or identity and upon sending messages | -| ipinfo.io | IP (network) address of domain names of links or email addresses | Upon pressing a button in the link confirmation dialog | -| Spamhaus | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message | -| Spamcop | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message | -| Barracuda | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message | -| DeepL | Received or entered message text and target language code | Upon pressing a translate button | -| LanguageTool | Entered message texts | Upon long pressing the save draft button | -| Gravatar | [MD5 hash](https://en.wikipedia.org/wiki/MD5) of email addresses | If Gravatars are enabled, upon receiving a message | -| Libravatar | [MD5 hash](https://en.wikipedia.org/wiki/MD5) of email addresses | If Libravatars are enabled, upon receiving a message | -| GitHub | None, but see the remarks below | Upon downloading Disconnect's Tracker Protection lists | -| | | Upon checking for updates (GitHub version only) | -| BIMI | Domain name of email addresses | If BIMI is enabled, upon receiving a message | -| Favicons | Domain name of email addresses | If favicons are enabled, upon receiving a message | -| Link title | Link address | Upon pressing a button in the insert link dialog | -| Bugsnag | Information about warnings and errors | If error reporting is enabled, upon detecting an abnormal situation | +| Service/function | Data sent | When the data will be sent | +| ------------------ | ----------------------------------------------------------------- | --------------------------------------------------------------------------- | +| Mozilla autoconfig | Email address of email accounts | Upon configuring an email account | +| Email server | Login credentials, messages sent | Upon configuring and using an account or identity and upon sending messages | +| ipinfo.io | IP (network) address of domain names of links or email addresses | Upon pressing a button in the link confirmation dialog | +| Spamhaus | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message | +| Spamcop | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message | +| Barracuda | IP (network) address of domain names of links or email addresses | If spam blocklists are enabled, upon receiving a message | +| DeepL | Received or entered message text and target language code | Upon pressing a translate button | +| LanguageTool | Entered message texts | Upon long pressing the save draft button | +| Gravatar | [MD5 hash](https://en.wikipedia.org/wiki/MD5) of email addresses | If Gravatars are enabled, upon receiving a message (GitHub version only) | +| Libravatar | [MD5 hash](https://en.wikipedia.org/wiki/MD5) of email addresses | If Libravatars are enabled, upon receiving a message (GitHub version only) | +| GitHub | None, but see the remarks below | Upon downloading Disconnect's Tracker Protection lists | +| | | Upon checking for updates (GitHub version only) | +| BIMI | Domain name of email addresses | If BIMI is enabled, upon receiving a message | +| Favicons | Domain name of email addresses | If favicons are enabled, upon receiving a message | +| Link title | Link address | Upon pressing a button in the insert link dialog | +| Bugsnag | Information about warnings and errors | If error reporting is enabled, upon detecting an abnormal situation | All data is sent to improve the user experience in some way, like to simplify account setup, identify spam and malicious messages, display message and sender information, find bugs and errors, etc. diff --git a/app/src/dummy/java/eu/faircode/email/Avatar.java b/app/src/dummy/java/eu/faircode/email/Avatar.java new file mode 100644 index 0000000000..c57b707056 --- /dev/null +++ b/app/src/dummy/java/eu/faircode/email/Avatar.java @@ -0,0 +1,49 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2022 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; + +import org.apache.poi.ss.formula.eval.NotImplementedException; + +import java.util.concurrent.Callable; + +public class Avatar { + static final String GRAVATAR_PRIVACY_URI = ""; + static final String LIBRAVATAR_PRIVACY_URI = ""; + + static Callable getGravatar(String email, int scaleToPixels, Context context) { + return new Callable() { + @Override + public ContactInfo.Favicon call() throws Exception { + throw new NotImplementedException("Gravatar"); + } + }; + } + + static Callable getLibravatar(String email, int scaleToPixels, Context context) { + return new Callable() { + @Override + public ContactInfo.Favicon call() throws Exception { + throw new NotImplementedException("Libravatar"); + } + }; + } +} diff --git a/app/src/extra/java/eu/faircode/email/Avatar.java b/app/src/extra/java/eu/faircode/email/Avatar.java new file mode 100644 index 0000000000..ef38303ea7 --- /dev/null +++ b/app/src/extra/java/eu/faircode/email/Avatar.java @@ -0,0 +1,120 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2022 by Marcel Bokhorst (M66B) +*/ + +import android.content.Context; +import android.graphics.Bitmap; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.concurrent.Callable; + +public class Avatar { + static final String GRAVATAR_PRIVACY_URI = "https://automattic.com/privacy/"; + static final String LIBRAVATAR_PRIVACY_URI = "https://www.libravatar.org/privacy/"; + + private static final String GRAVATAR_URI = "https://www.gravatar.com/avatar/"; + private static final int GRAVATAR_CONNECT_TIMEOUT = 5 * 1000; // milliseconds + private static final int GRAVATAR_READ_TIMEOUT = 10 * 1000; // milliseconds + private static final int LIBRAVATAR_CONNECT_TIMEOUT = 5 * 1000; // milliseconds + private static final int LIBRAVATAR_READ_TIMEOUT = 10 * 1000; // milliseconds + private static final String LIBRAVATAR_DNS = "_avatars-sec._tcp,_avatars._tcp"; + private static final String LIBRAVATAR_URI = "https://seccdn.libravatar.org/avatar/"; + + static Callable getGravatar(String email, int scaleToPixels, Context context) { + return new Callable() { + @Override + public ContactInfo.Favicon call() throws Exception { + String hash = Helper.md5(email.getBytes()); + URL url = new URL(GRAVATAR_URI + hash + "?d=404"); + Log.i("Gravatar key=" + email + " url=" + url); + + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestMethod("GET"); + urlConnection.setReadTimeout(GRAVATAR_READ_TIMEOUT); + urlConnection.setConnectTimeout(GRAVATAR_CONNECT_TIMEOUT); + ConnectionHelper.setUserAgent(context, urlConnection); + urlConnection.connect(); + + try { + int status = urlConnection.getResponseCode(); + if (status == HttpURLConnection.HTTP_OK) { + // Positive reply + Bitmap bitmap = ImageHelper.getScaledBitmap(urlConnection.getInputStream(), url.toString(), null, scaleToPixels); + return (bitmap == null ? null : new ContactInfo.Favicon(bitmap, "gravatar", false)); + } else if (status == HttpURLConnection.HTTP_NOT_FOUND) { + // Negative reply + return null; + } else + throw new IOException("Error " + status + ": " + urlConnection.getResponseMessage()); + } finally { + urlConnection.disconnect(); + } + } + }; + } + + static Callable getLibravatar(String email, int scaleToPixels, Context context) { + return new Callable() { + @Override + public ContactInfo.Favicon call() throws Exception { + String domain = UriHelper.getEmailDomain(email); + + // https://wiki.libravatar.org/api/ + String baseUrl = LIBRAVATAR_URI; + for (String dns : LIBRAVATAR_DNS.split(",")) { + DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, dns + "." + domain, "srv"); + if (records.length > 0) { + baseUrl = (records[0].port == 443 ? "https" : "http") + "://" + records[0].name + "/avatar/"; + break; + } + } + + String hash = Helper.md5(email.getBytes()); + + URL url = new URL(baseUrl + hash + "?d=404"); + Log.i("Libravatar key=" + email + " url=" + url); + + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestMethod("GET"); + urlConnection.setReadTimeout(LIBRAVATAR_READ_TIMEOUT); + urlConnection.setConnectTimeout(LIBRAVATAR_CONNECT_TIMEOUT); + ConnectionHelper.setUserAgent(context, urlConnection); + urlConnection.connect(); + + try { + int status = urlConnection.getResponseCode(); + if (status == HttpURLConnection.HTTP_OK) { + // Positive reply + Bitmap bitmap = ImageHelper.getScaledBitmap(urlConnection.getInputStream(), url.toString(), null, scaleToPixels); + return (bitmap == null ? null : new ContactInfo.Favicon(bitmap, "libravatar", false)); + } else if (status == HttpURLConnection.HTTP_NOT_FOUND) { + // Negative reply + return null; + } else + throw new IOException("Error " + status + ": " + urlConnection.getResponseMessage()); + } finally { + urlConnection.disconnect(); + } + } + }; + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index 0ebf956859..413572c7d5 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -6888,8 +6888,8 @@ public class AdapterMessage extends RecyclerView.Adapter REL_EXCLUDE = Collections.unmodifiableList(Arrays.asList( @@ -265,8 +257,8 @@ public class ContactInfo { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean avatars = prefs.getBoolean("avatars", true); boolean bimi = prefs.getBoolean("bimi", false); - boolean gravatars = prefs.getBoolean("gravatars", false); - boolean libravatars = prefs.getBoolean("libravatars", false); + boolean gravatars = (prefs.getBoolean("gravatars", false) && !BuildConfig.PLAY_STORE_RELEASE); + boolean libravatars = (prefs.getBoolean("libravatars", false) && !BuildConfig.PLAY_STORE_RELEASE); boolean favicons = prefs.getBoolean("favicons", false); boolean generated = prefs.getBoolean("generated_icons", true); boolean identicons = prefs.getBoolean("identicons", false); @@ -388,9 +380,9 @@ public class ContactInfo { })); if (gravatars) - futures.add(executorFavicon.submit(getGravatar(email, scaleToPixels, context))); + futures.add(executorFavicon.submit(Avatar.getGravatar(email, scaleToPixels, context))); if (libravatars) - futures.add(executorFavicon.submit(getLibravatar(email, scaleToPixels, context))); + futures.add(executorFavicon.submit(Avatar.getLibravatar(email, scaleToPixels, context))); if (favicons) { String host = domain; @@ -876,85 +868,6 @@ public class ContactInfo { } } - static Callable getGravatar(String email, int scaleToPixels, Context context) { - return new Callable() { - @Override - public ContactInfo.Favicon call() throws Exception { - String hash = Helper.md5(email.getBytes()); - URL url = new URL(GRAVATAR_URI + hash + "?d=404"); - Log.i("Gravatar key=" + email + " url=" + url); - - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setRequestMethod("GET"); - urlConnection.setReadTimeout(GRAVATAR_READ_TIMEOUT); - urlConnection.setConnectTimeout(GRAVATAR_CONNECT_TIMEOUT); - ConnectionHelper.setUserAgent(context, urlConnection); - urlConnection.connect(); - - try { - int status = urlConnection.getResponseCode(); - if (status == HttpURLConnection.HTTP_OK) { - // Positive reply - Bitmap bitmap = ImageHelper.getScaledBitmap(urlConnection.getInputStream(), url.toString(), null, scaleToPixels); - return (bitmap == null ? null : new ContactInfo.Favicon(bitmap, "gravatar", false)); - } else if (status == HttpURLConnection.HTTP_NOT_FOUND) { - // Negative reply - return null; - } else - throw new IOException("Error " + status + ": " + urlConnection.getResponseMessage()); - } finally { - urlConnection.disconnect(); - } - } - }; - } - - static Callable getLibravatar(String email, int scaleToPixels, Context context) { - return new Callable() { - @Override - public ContactInfo.Favicon call() throws Exception { - String domain = UriHelper.getEmailDomain(email); - - // https://wiki.libravatar.org/api/ - String baseUrl = LIBRAVATAR_URI; - for (String dns : LIBRAVATAR_DNS.split(",")) { - DnsHelper.DnsRecord[] records = DnsHelper.lookup(context, dns + "." + domain, "srv"); - if (records.length > 0) { - baseUrl = (records[0].port == 443 ? "https" : "http") + "://" + records[0].name + "/avatar/"; - break; - } - } - - String hash = Helper.md5(email.getBytes()); - - URL url = new URL(baseUrl + hash + "?d=404"); - Log.i("Libravatar key=" + email + " url=" + url); - - HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); - urlConnection.setRequestMethod("GET"); - urlConnection.setReadTimeout(LIBRAVATAR_READ_TIMEOUT); - urlConnection.setConnectTimeout(LIBRAVATAR_CONNECT_TIMEOUT); - ConnectionHelper.setUserAgent(context, urlConnection); - urlConnection.connect(); - - try { - int status = urlConnection.getResponseCode(); - if (status == HttpURLConnection.HTTP_OK) { - // Positive reply - Bitmap bitmap = ImageHelper.getScaledBitmap(urlConnection.getInputStream(), url.toString(), null, scaleToPixels); - return (bitmap == null ? null : new ContactInfo.Favicon(bitmap, "libravatar", false)); - } else if (status == HttpURLConnection.HTTP_NOT_FOUND) { - // Negative reply - return null; - } else - throw new IOException("Error " + status + ": " + urlConnection.getResponseMessage()); - } finally { - urlConnection.disconnect(); - } - } - }; - } - private static boolean isRecoverable(Throwable ex, Context context) { if (ex instanceof SocketTimeoutException) { ConnectivityManager cm = Helper.getSystemService(context, ConnectivityManager.class); diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java b/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java index 9de7e54a13..9465f6b441 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsDisplay.java @@ -180,6 +180,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer private SwitchCompat swAuthentication; private SwitchCompat swAuthenticationIndicator; + private Group grpAvatar; private Group grpUnzip; private NumberFormat NF = NumberFormat.getNumberInstance(); @@ -334,6 +335,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer swAuthentication = view.findViewById(R.id.swAuthentication); swAuthenticationIndicator = view.findViewById(R.id.swAuthenticationIndicator); + grpAvatar = view.findViewById(R.id.grpAvatar); grpUnzip = view.findViewById(R.id.grpUnzip); List fonts = StyleHelper.getFonts(getContext()); @@ -744,7 +746,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer tvGravatarPrivacy.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Helper.view(v.getContext(), Uri.parse(Helper.GRAVATAR_PRIVACY_URI), true); + Helper.view(v.getContext(), Uri.parse(Avatar.GRAVATAR_PRIVACY_URI), true); } }); @@ -760,7 +762,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer tvLibravatarPrivacy.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Helper.view(v.getContext(), Uri.parse(Helper.LIBRAVATAR_PRIVACY_URI), true); + Helper.view(v.getContext(), Uri.parse(Avatar.LIBRAVATAR_PRIVACY_URI), true); } }); @@ -1257,6 +1259,7 @@ public class FragmentOptionsDisplay extends FragmentBase implements SharedPrefer FragmentDialogTheme.setBackground(getContext(), view, false); swFaviconsPartial.setText(getString(R.string.title_advanced_favicons_partial, Helper.humanReadableByteCount(ContactInfo.FAVICON_READ_BYTES, false))); + grpAvatar.setVisibility(BuildConfig.PLAY_STORE_RELEASE ? View.GONE : View.VISIBLE); grpUnzip.setVisibility(Build.VERSION.SDK_INT < Build.VERSION_CODES.O ? View.GONE : View.VISIBLE); tvBimiUnverified.setVisibility(BuildConfig.DEBUG ? View.VISIBLE : View.GONE); diff --git a/app/src/main/java/eu/faircode/email/Helper.java b/app/src/main/java/eu/faircode/email/Helper.java index 4c7cedf842..63cf740734 100644 --- a/app/src/main/java/eu/faircode/email/Helper.java +++ b/app/src/main/java/eu/faircode/email/Helper.java @@ -184,8 +184,6 @@ public class Helper { static final String SUPPORT_URI = "https://contact.faircode.eu/"; static final String TEST_URI = "https://play.google.com/apps/testing/" + BuildConfig.APPLICATION_ID; static final String BIMI_PRIVACY_URI = "https://datatracker.ietf.org/doc/html/draft-brotman-ietf-bimi-guidance-03#section-7.4"; - static final String GRAVATAR_PRIVACY_URI = "https://automattic.com/privacy/"; - static final String LIBRAVATAR_PRIVACY_URI = "https://www.libravatar.org/privacy/"; static final String ID_COMMAND_URI = "https://datatracker.ietf.org/doc/html/rfc2971#section-3.1"; static final String AUTH_RESULTS_URI = "https://datatracker.ietf.org/doc/html/rfc7601"; static final String FAVICON_PRIVACY_URI = "https://en.wikipedia.org/wiki/Favicon"; diff --git a/app/src/main/res/layout/fragment_options_display.xml b/app/src/main/res/layout/fragment_options_display.xml index d7cf8d452d..53c425ed4b 100644 --- a/app/src/main/res/layout/fragment_options_display.xml +++ b/app/src/main/res/layout/fragment_options_display.xml @@ -1524,6 +1524,14 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvPreviewLinesHint" /> + +