Refactoring

This commit is contained in:
M66B 2022-07-23 09:27:04 +02:00
parent 73f7b4a9a4
commit dbc8dc5266
5 changed files with 163 additions and 157 deletions

View File

@ -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;
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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<String>() {
@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");
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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;
}
}

View File

@ -215,7 +215,7 @@ public class AdapterAttachment extends RecyclerView.Adapter<AdapterAttachment.Vi
else if (id == R.id.ibSave)
onSave(attachment);
else if (id == R.id.ibScan)
Check.virus(context, owner, parentFragment.getParentFragmentManager(), attachment.id);
onScan(attachment);
else {
if (attachment.available)
onShare(attachment);
@ -310,6 +310,36 @@ public class AdapterAttachment extends RecyclerView.Adapter<AdapterAttachment.Vi
.putExtra("type", attachment.getMimeType()));
}
private void onScan(EntityAttachment attachment) {
Bundle args = new Bundle();
args.putLong("id", attachment.id);
new SimpleTask<Bundle>() {
@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);

View File

@ -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);
}
});