0
0
mirror of https://github.com/TrianguloY/UrlChecker.git synced 2024-09-19 20:02:16 +02:00

Connection Wrapper

This commit is contained in:
TrianguloY 2024-07-05 18:50:42 +02:00
parent a4073e53b3
commit fd52efd885
4 changed files with 186 additions and 77 deletions

View File

@ -2,20 +2,16 @@ package com.trianguloy.urlchecker.modules.companions;
import static android.util.Base64.NO_PADDING;
import static android.util.Base64.NO_WRAP;
import static android.util.Base64.URL_SAFE;
import android.content.Context;
import android.util.Base64;
import android.util.Pair;
import com.trianguloy.urlchecker.R;
import com.trianguloy.urlchecker.utilities.methods.HttpUtils;
import com.trianguloy.urlchecker.utilities.wrappers.Connection;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.util.Date;
import java.util.Map;
@ -28,7 +24,7 @@ public class VirusTotalUtility {
static private final String URLS_ENDPOINT = "https://www.virustotal.com/api/v3/urls";
static public class InternalReponse {
static public class InternalResponse {
public String error = "Unknown error";
public int detectionsPositive;
public int detectionsTotal;
@ -38,28 +34,23 @@ public class VirusTotalUtility {
}
/** Returns the analysis of an url, or null if the analysis is in progress */
public static InternalReponse scanUrl(String urlToScan, String key, Context cntx) {
var result = new InternalReponse();
public static InternalResponse scanUrl(String urlToScan, String key, Context cntx) {
// get analysis
Pair<Integer, String> responsePair;
try {
responsePair = HttpUtils.readFromUrl(URLS_ENDPOINT + "/" + Base64.encodeToString(urlToScan.getBytes(), NO_PADDING | NO_WRAP), Map.of(
"accept", "application/json",
"x-apikey", key
));
} catch (IOException e) {
e.printStackTrace();
result.error = cntx.getString(R.string.mVT_connectError);
return result;
}
// connect
var encodedUrl = Base64.encodeToString(urlToScan.getBytes(), NO_PADDING | NO_WRAP | URL_SAFE);
var connection = Connection.to(URLS_ENDPOINT + "/" + encodedUrl)
.addHeader("x-apikey", key)
.acceptJson()
.connect();
if (responsePair.first == 404) {
if (connection.getStatusCode() == 404) {
// not analyzed yet
return analyzeUrl(urlToScan, key, cntx);
}
if (responsePair.first != 200) {
var result = new InternalResponse();
var response = connection.getResultAsJson();
if (response == null || connection.getStatusCode() != 200) {
// error
result.error = cntx.getString(R.string.mVT_jsonError);
return result;
@ -67,13 +58,11 @@ public class VirusTotalUtility {
// parse response
try {
JSONObject response = new JSONObject(responsePair.second);
result.info = response.toString(1);
var data = response.getJSONObject("data");
result.scanUrl = data.getJSONObject("links").getString("self");
result.scanUrl = "https://www.virustotal.com/gui/url/" + encodedUrl;
// parse attributes
var attributes = data.getJSONObject("attributes");
var attributes = response.getJSONObject("data").getJSONObject("attributes");
if (!attributes.has("last_analysis_date")) {
// still analyzing
@ -89,7 +78,6 @@ public class VirusTotalUtility {
result.date = DateFormat.getInstance().format(new Date(attributes.getLong("last_analysis_date") * 1000));
result.error = null;
return result;
} catch (JSONException e) {
e.printStackTrace();
result.error = cntx.getString(R.string.mVT_jsonError);
@ -98,28 +86,23 @@ public class VirusTotalUtility {
return result;
}
/** Requests an analysis (if not one already) */
static private InternalReponse analyzeUrl(String urlToScan, String key, Context cntx) {
try {
var output = HttpUtils.performPOST(URLS_ENDPOINT, "url=" + URLEncoder.encode(urlToScan, "UTF-8"), Map.of(
"accept", "application/json",
"x-apikey", key,
"content-type", "application/x-www-form-urlencoded"
));
/** Requests an analysis (if not done already) */
static private InternalResponse analyzeUrl(String urlToScan, String key, Context cntx) {
var code = Connection.to(URLS_ENDPOINT)
.addHeader("x-apikey", key)
.acceptJson()
.postFormUrlEncoded(Map.of("url", urlToScan))
.getStatusCode();
if (output.first != 200) {
var result = new InternalReponse();
result.error = cntx.getString(R.string.mVT_jsonError);
return result;
}
return null;
} catch (IOException e) {
e.printStackTrace();
var result = new InternalReponse();
if (code != 200) {
// error
var result = new InternalResponse();
result.error = cntx.getString(R.string.mVT_jsonError);
return result;
}
// ok
return null;
}
}

View File

@ -18,7 +18,6 @@ import com.trianguloy.urlchecker.modules.companions.VirusTotalUtility;
import com.trianguloy.urlchecker.url.UrlData;
import com.trianguloy.urlchecker.utilities.generics.GenericPref;
import com.trianguloy.urlchecker.utilities.methods.AndroidUtils;
import com.trianguloy.urlchecker.utilities.methods.UrlUtils;
import com.trianguloy.urlchecker.utilities.wrappers.DefaultTextWatcher;
/**
@ -99,7 +98,7 @@ class VirusTotalDialog extends AModuleDialog {
private TextView txt_result;
private boolean scanning = false;
private VirusTotalUtility.InternalReponse result = null;
private VirusTotalUtility.InternalResponse result = null;
private final GenericPref.Str api_key;
@ -155,7 +154,7 @@ class VirusTotalDialog extends AModuleDialog {
* Manages the scanning in the background
*/
private void _scanUrl() {
VirusTotalUtility.InternalReponse response;
VirusTotalUtility.InternalResponse response;
while (scanning) {
// asks for the report
response = VirusTotalUtility.scanUrl(getUrl(), api_key.get(), getActivity());
@ -243,7 +242,8 @@ class VirusTotalDialog extends AModuleDialog {
if (result == null || result.error != null) return;
if (details) {
UrlUtils.openUrlRemoveThis(result.scanUrl, getActivity());
setUrl(result.scanUrl);
// UrlUtils.openUrlRemoveThis(result.scanUrl, getActivity());
} else {
// TODO: beautify this
new AlertDialog.Builder(getActivity())

View File

@ -30,31 +30,4 @@ public interface UrlUtils {
return intent;
}
/**
* Opens an url removing this app from the chooser
*
* @param url url to open
* @param cntx base context
*/
static void openUrlRemoveThis(String url, Context cntx) {
// get intents that can open the url
List<Intent> intents = new ArrayList<>();
for (var pack : IntentApp.getOtherPackages(getViewIntent(url, null), cntx)) {
intents.add(getViewIntent(url, pack));
}
// check if none
if (intents.isEmpty()) {
Toast.makeText(cntx, R.string.toast_noBrowser, Toast.LENGTH_SHORT).show();
return;
}
// create chooser
Intent chooserIntent = Intent.createChooser(intents.remove(0), cntx.getString(R.string.title_choose));
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents.toArray(new Parcelable[0]));
// open
PackageUtils.startActivity(chooserIntent, R.string.toast_noBrowser, cntx);
}
}

View File

@ -0,0 +1,153 @@
package com.trianguloy.urlchecker.utilities.wrappers;
import com.trianguloy.urlchecker.utilities.methods.StreamUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
import javax.net.ssl.HttpsURLConnection;
/** Wrapper of HttpsURLConnection with some util methods */
public class Connection {
public static final int CONNECT_TIMEOUT = 5000;
/** Initializes a new connection to a specific url */
public static Request to(String url) {
return new Request(url);
}
public static class Request {
private HttpsURLConnection connection = null;
private Request(String url) {
try {
connection = (HttpsURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(CONNECT_TIMEOUT);
} catch (IOException e) {
e.printStackTrace();
}
}
/** Adds a header to the connection */
public Request addHeader(String header, String value) {
if (connection == null) return this;
connection.setRequestProperty(header, value);
return this;
}
/** Marks this request as accepting json */
public Request acceptJson() {
if (connection != null) connection.setRequestProperty("accept", "application/json");
return this;
}
/** Connects and posts a string as body */
public Response postString(String body) {
if (connection == null) return new Response(null);
connection.setDoOutput(true);
try (var writer = new OutputStreamWriter(connection.getOutputStream())) {
writer.write(body);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
connection = null;
}
return new Response(connection);
}
/** Connects and posts a key=value collection as body */
public Response postFormUrlEncoded(Map<String, String> form) {
if (connection == null) return new Response(null);
addHeader("content-type", "application/x-www-form-urlencoded");
try {
var body = new StringBuilder();
for (var param : form.entrySet()) {
if (body.length() != 0) body.append('&');
body.append(URLEncoder.encode(param.getKey(), "UTF-8"));
body.append('=');
body.append(URLEncoder.encode(param.getValue(), "UTF-8"));
}
return postString(body.toString());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return new Response(null);
}
}
/** Performs the connection */
public Response connect() {
if (connection == null) return new Response(null);
try {
connection.connect();
return new Response(connection);
} catch (IOException e) {
e.printStackTrace();
return new Response(null);
}
}
}
public static class Response {
private HttpsURLConnection connection;
private Response(HttpsURLConnection connection) {
this.connection = connection;
}
/** Returns the status code, or -1 if something failed */
public int getStatusCode() {
if (connection == null) return -1;
try {
return connection.getResponseCode();
} catch (IOException e) {
e.printStackTrace();
connection = null;
return -1;
}
}
/** Returns the result as string, or null if something failed */
public String getResultAsString() {
if (connection == null) return null;
try {
return StreamUtils.inputStream2String(getStream());
} catch (IOException e) {
e.printStackTrace();
connection = null;
return null;
}
}
/** Returns the result as json, or null if something failed */
public JSONObject getResultAsJson() {
var string = getResultAsString();
if (string == null) return null;
try {
return new JSONObject(string);
} catch (JSONException e) {
e.printStackTrace();
connection = null;
return null;
}
}
private InputStream getStream() throws IOException {
return connection.getResponseCode() >= 200 && connection.getResponseCode() < 300
? connection.getInputStream()
: connection.getErrorStream();
}
}
}