From dbc8dc52662811e8dae9084ea7e09b538ab04a31 Mon Sep 17 00:00:00 2001 From: M66B Date: Sat, 23 Jul 2022 09:27:04 +0200 Subject: [PATCH] Refactoring --- .../email/{Check.java => VirusTotal.java} | 10 +- .../extra/java/eu/faircode/email/Check.java | 150 ------------------ .../java/eu/faircode/email/VirusTotal.java | 126 +++++++++++++++ .../eu/faircode/email/AdapterAttachment.java | 32 +++- .../faircode/email/FragmentOptionsMisc.java | 2 +- 5 files changed, 163 insertions(+), 157 deletions(-) rename app/src/dummy/java/eu/faircode/email/{Check.java => VirusTotal.java} (79%) delete mode 100644 app/src/extra/java/eu/faircode/email/Check.java create mode 100644 app/src/extra/java/eu/faircode/email/VirusTotal.java diff --git a/app/src/dummy/java/eu/faircode/email/Check.java b/app/src/dummy/java/eu/faircode/email/VirusTotal.java similarity index 79% rename from app/src/dummy/java/eu/faircode/email/Check.java rename to app/src/dummy/java/eu/faircode/email/VirusTotal.java index c7618f95c9..5cee1d5483 100644 --- a/app/src/dummy/java/eu/faircode/email/Check.java +++ b/app/src/dummy/java/eu/faircode/email/VirusTotal.java @@ -20,14 +20,14 @@ package eu.faircode.email; */ import android.content.Context; +import android.os.Bundle; -import androidx.fragment.app.FragmentManager; -import androidx.lifecycle.LifecycleOwner; +import java.io.File; -public class Check { +public class VirusTotal { static final String URI_PRIVACY = ""; - static void virus(Context context, LifecycleOwner owner, FragmentManager fm, long id) { - // Do nothing + static Bundle scan(Context context, File file) { + return null; } } diff --git a/app/src/extra/java/eu/faircode/email/Check.java b/app/src/extra/java/eu/faircode/email/Check.java deleted file mode 100644 index bcb2685276..0000000000 --- a/app/src/extra/java/eu/faircode/email/Check.java +++ /dev/null @@ -1,150 +0,0 @@ -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.content.SharedPreferences; -import android.net.Uri; -import android.os.Bundle; -import android.text.TextUtils; - -import androidx.fragment.app.FragmentManager; -import androidx.lifecycle.LifecycleOwner; -import androidx.preference.PreferenceManager; - -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.net.URL; - -import javax.net.ssl.HttpsURLConnection; - -public class Check { - static final String URI_VT_PRIVACY = "https://support.virustotal.com/hc/en-us/articles/115002168385-Privacy-Policy"; - private static final int VT_TIMEOUT = 20; // seconds - private static final String URI_VT_ENDPOINT = "https://www.virustotal.com/"; - - static void virus(Context context, LifecycleOwner owner, FragmentManager fm, long id) { - Bundle args = new Bundle(); - args.putLong("id", id); - - new SimpleTask() { - @Override - protected String onExecute(Context context, Bundle args) throws Throwable { - long id = args.getLong("id"); - - DB db = DB.getInstance(context); - EntityAttachment attachment = db.attachment().getAttachment(id); - if (attachment == null) - return null; - - String hash; - try (InputStream is = new FileInputStream(attachment.getFile(context))) { - hash = Helper.getHash(is, "SHA-256"); - } - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String apikey = prefs.getString("vt_apikey", null); - - if (!TextUtils.isEmpty(apikey)) { - URL url = new URL(URI_VT_ENDPOINT + "api/v3/files/" + hash); - HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - connection.setReadTimeout(VT_TIMEOUT * 1000); - connection.setConnectTimeout(VT_TIMEOUT * 1000); - ConnectionHelper.setUserAgent(context, connection); - connection.setRequestProperty("x-apikey", apikey); - connection.connect(); - - try { - int status = connection.getResponseCode(); - if (status != HttpsURLConnection.HTTP_OK && status != HttpsURLConnection.HTTP_NOT_FOUND) { - String error = "Error " + status + ": " + connection.getResponseMessage(); - try { - InputStream is = connection.getErrorStream(); - if (is != null) - error += "\n" + Helper.readStream(is); - } catch (Throwable ex) { - Log.w(ex); - } - throw new FileNotFoundException(error); - } - - if (status == HttpsURLConnection.HTTP_OK) { - String response = Helper.readStream(connection.getInputStream()); - Log.i("VT response=" + response); - - // https://developers.virustotal.com/reference/files - // Example: https://gist.github.com/M66B/4ea95fdb93fb10bf4047761fcc9ec21a - JSONObject jroot = new JSONObject(response); - JSONObject jdata = jroot.getJSONObject("data"); - JSONObject jattributes = jdata.getJSONObject("attributes"); - - JSONObject jclassification = jattributes.getJSONObject("popular_threat_classification"); - String label = jclassification.getString("suggested_threat_label"); - - int count = 0; - int malicious = 0; - JSONObject jlast_analysis_results = jattributes.getJSONObject("last_analysis_results"); - JSONArray jnames = jlast_analysis_results.names(); - for (int i = 0; i < jnames.length(); i++) { - String name = jnames.getString(i); - JSONObject jresult = jlast_analysis_results.getJSONObject(name); - String category = jresult.getString("category"); - Log.i("VT " + name + "=" + category); - if (!"type-unsupported".equals(category)) - count++; - if ("malicious".equals(category)) - malicious++; - } - - Log.i("VT label=" + label + " " + malicious + "/" + count); - - args.putString("label", label); - args.putInt("count", count); - args.putInt("malicious", malicious); - } - } finally { - connection.disconnect(); - } - } - - return hash; - } - - @Override - protected void onExecuted(Bundle args, String hash) { - if (hash == null) - return; - - Uri uri = Uri.parse(URI_VT_ENDPOINT + "gui/file/" + hash); - Helper.view(context, uri, false); - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Log.unexpectedError(fm, ex); - } - }.execute(context, owner, args, "attachment:scan"); - } -} diff --git a/app/src/extra/java/eu/faircode/email/VirusTotal.java b/app/src/extra/java/eu/faircode/email/VirusTotal.java new file mode 100644 index 0000000000..e006aba8c7 --- /dev/null +++ b/app/src/extra/java/eu/faircode/email/VirusTotal.java @@ -0,0 +1,126 @@ +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.content.SharedPreferences; +import android.os.Bundle; +import android.text.TextUtils; + +import androidx.preference.PreferenceManager; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.HttpsURLConnection; + +public class VirusTotal { + static final String URI_ENDPOINT = "https://www.virustotal.com/"; + static final String URI_PRIVACY = "https://support.virustotal.com/hc/en-us/articles/115002168385-Privacy-Policy"; + + private static final int VT_TIMEOUT = 20; // seconds + + static Bundle scan(Context context, File file) throws NoSuchAlgorithmException, IOException, JSONException { + String hash; + try (InputStream is = new FileInputStream(file)) { + hash = Helper.getHash(is, "SHA-256"); + } + + Bundle result = new Bundle(); + result.putString("uri", URI_ENDPOINT + "gui/file/" + hash); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String apikey = prefs.getString("vt_apikey", null); + if (TextUtils.isEmpty(apikey)) + return result; + + URL url = new URL(URI_ENDPOINT + "api/v3/files/" + hash); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setReadTimeout(VT_TIMEOUT * 1000); + connection.setConnectTimeout(VT_TIMEOUT * 1000); + ConnectionHelper.setUserAgent(context, connection); + connection.setRequestProperty("x-apikey", apikey); + connection.connect(); + + try { + int status = connection.getResponseCode(); + if (status != HttpsURLConnection.HTTP_OK && status != HttpsURLConnection.HTTP_NOT_FOUND) { + String error = "Error " + status + ": " + connection.getResponseMessage(); + try { + InputStream is = connection.getErrorStream(); + if (is != null) + error += "\n" + Helper.readStream(is); + } catch (Throwable ex) { + Log.w(ex); + } + throw new FileNotFoundException(error); + } + + if (status == HttpsURLConnection.HTTP_OK) { + String response = Helper.readStream(connection.getInputStream()); + Log.i("VT response=" + response); + + // https://developers.virustotal.com/reference/files + // Example: https://gist.github.com/M66B/4ea95fdb93fb10bf4047761fcc9ec21a + JSONObject jroot = new JSONObject(response); + JSONObject jdata = jroot.getJSONObject("data"); + JSONObject jattributes = jdata.getJSONObject("attributes"); + + JSONObject jclassification = jattributes.getJSONObject("popular_threat_classification"); + String label = jclassification.getString("suggested_threat_label"); + + int count = 0; + int malicious = 0; + JSONObject janalysis = jattributes.getJSONObject("last_analysis_results"); + JSONArray jnames = janalysis.names(); + for (int i = 0; i < jnames.length(); i++) { + String name = jnames.getString(i); + JSONObject jresult = janalysis.getJSONObject(name); + String category = jresult.getString("category"); + Log.i("VT " + name + "=" + category); + if (!"type-unsupported".equals(category)) + count++; + if ("malicious".equals(category)) + malicious++; + } + + Log.i("VT label=" + label + " " + malicious + "/" + count); + + result.putString("label", label); + result.putInt("count", count); + result.putInt("malicious", malicious); + } + } finally { + connection.disconnect(); + } + + return result; + } +} diff --git a/app/src/main/java/eu/faircode/email/AdapterAttachment.java b/app/src/main/java/eu/faircode/email/AdapterAttachment.java index 0931b26133..1163744f56 100644 --- a/app/src/main/java/eu/faircode/email/AdapterAttachment.java +++ b/app/src/main/java/eu/faircode/email/AdapterAttachment.java @@ -215,7 +215,7 @@ public class AdapterAttachment extends RecyclerView.Adapter() { + @Override + protected Bundle onExecute(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + + DB db = DB.getInstance(context); + EntityAttachment attachment = db.attachment().getAttachment(id); + if (attachment == null) + return null; + + return VirusTotal.scan(context, attachment.getFile(context)); + } + + @Override + protected void onExecuted(Bundle args, Bundle result) { + String uri = result.getString("uri"); + Helper.view(context, Uri.parse(uri), true); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(parentFragment.getParentFragmentManager(), ex); + } + }.execute(context, owner, args, "attachment:scan"); + } + private void onShare(EntityAttachment attachment) { String title = (attachment.name == null ? attachment.cid : attachment.name); Helper.share(context, attachment.getFile(context), attachment.getMimeType(), title); diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java index d5e65799c6..68fad2278f 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java @@ -655,7 +655,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc tvVirusTotalPrivacy.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Helper.view(v.getContext(), Uri.parse(Check.URI_VT_PRIVACY), true); + Helper.view(v.getContext(), Uri.parse(VirusTotal.URI_PRIVACY), true); } });