commit 72d4bb0200188a0281d9c58eff918c9fc7696524 Author: TrianguloY Date: Sun Jul 12 11:53:24 2020 +0200 old first version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7cae86d --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# Built application files +*.apk +*.ap_ +mapping.txt +output.json + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# Intellij +*.iml +.idea/ + +# Keystore files +*.jks + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild + +# Google Services (e.g. APIs or Firebase) +google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..5f2a202 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,22 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 29 + defaultConfig { + applicationId "com.trianguloy.urlchecker" + minSdkVersion 10 + targetSdkVersion 29 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b1b9165 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000..d117d1e Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ diff --git a/app/src/main/java/com/trianguloy/urlchecker/CustomAdapter.java b/app/src/main/java/com/trianguloy/urlchecker/CustomAdapter.java new file mode 100644 index 0000000..6bc800f --- /dev/null +++ b/app/src/main/java/com/trianguloy/urlchecker/CustomAdapter.java @@ -0,0 +1,68 @@ +package com.trianguloy.urlchecker; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; + +import java.util.ArrayList; + +/** + * Class that [INSERT DESCRIPTION HERE] + */ +class CustomAdapter extends BaseAdapter { + + private Context cntx; + private ArrayList items = new ArrayList<>(); + + // Constructor + CustomAdapter(Context cntx) { + this.cntx = cntx; + } + + void addItem(String packageName) { + items.add(packageName); + } + + void clearAll() { + items.clear(); + } + + + @Override + public int getCount() { + return items.size(); + } + + @Override + public String getItem(int i) { + return items.get(i); + } + + @Override + public long getItemId(int i) { + return 0; + } + + @Override + public View getView(int i, View convertView, ViewGroup viewGroup) { + ImageView imageView; + if (convertView == null) { + // if it's not recycled, initialize some attributes + imageView = new ImageView(cntx); + imageView.setPadding(8, 8, 8, 8); + } else { + imageView = (ImageView) convertView; + } + + try { + imageView.setImageDrawable(cntx.getPackageManager().getApplicationIcon(items.get(i))); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + imageView.setImageResource(R.mipmap.ic_launcher); + } + return imageView; + } +} diff --git a/app/src/main/java/com/trianguloy/urlchecker/OpenLink.java b/app/src/main/java/com/trianguloy/urlchecker/OpenLink.java new file mode 100644 index 0000000..6f276ac --- /dev/null +++ b/app/src/main/java/com/trianguloy/urlchecker/OpenLink.java @@ -0,0 +1,301 @@ +package com.trianguloy.urlchecker; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.Color; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Parcelable; +import android.view.View; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.GridView; +import android.widget.ImageButton; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.List; + +public class OpenLink extends Activity { + + private TextView txt_url; + private TextView txt_result; + private ImageButton btn_redirect; + private ImageButton btn_scan; + + private CustomAdapter adapter; + + private String url = null; + private VirusTotalUtility.InternalReponse result = null; + + private boolean scanning = false; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + setContentView(R.layout.activity_open_link); + + Uri uri = this.getIntent().getData(); + if (uri == null) { + Toast.makeText(this, "No url!!!!", Toast.LENGTH_SHORT).show(); + finish(); + return; + } + url = uri.toString(); + + txt_url = findViewById(R.id.txt_url); + txt_result = findViewById(R.id.txt_result); + btn_redirect = findViewById(R.id.btn_goRedirect); + btn_scan = findViewById(R.id.btn_scan); + + GridView grdVw_browsers = findViewById(R.id.grdVw_browsers); + adapter = new CustomAdapter(this); + + grdVw_browsers.setAdapter(adapter); + grdVw_browsers.setOnItemClickListener(new AdapterView.OnItemClickListener() { + public void onItemClick(AdapterView parent, View v, int position, long id) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.setPackage(adapter.getItem(position)); + startActivity(intent); + } + }); + + + //setOnLongClick + View.OnLongClickListener onLongClickListener = new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + OpenLink.this.onLongClick(view); + return false; + } + }; + txt_url.setOnLongClickListener(onLongClickListener); + txt_result.setOnLongClickListener(onLongClickListener); + + btn_redirect.setOnLongClickListener(onLongClickListener); + btn_scan.setOnLongClickListener(onLongClickListener); + + + updateUI(); + createBrowsers(); + + } + + private void setResult(String message, int color) { + txt_result.setBackgroundColor(color); + txt_result.setText(message); + } + + private void scanUrl() { + if (scanning) { + scanning = false; + } else { + scanning = true; + new Thread(new Runnable() { + public void run() { + _scanUrl(); + } + }).start(); + } + updateUI(); + } + + private void _scanUrl() { + VirusTotalUtility.InternalReponse response; + while (scanning) { + response = VirusTotalUtility.scanUrl(url); + + if (response.detectionsTotal > 0) { + result = response; + scanning = false; + runOnUiThread(new Runnable() { + public void run() { + updateUI(); + } + }); + return; + } + + //retry + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + } + + private void updateUI() { + txt_url.setText(url); + + if (scanning) { + btn_scan.setImageResource(android.R.drawable.ic_menu_close_clear_cancel); + setResult("Scanning...", Color.GRAY); + } else { + btn_scan.setImageResource(android.R.drawable.ic_menu_search); + if (result == null) { + setResult("Press to scan", 0); + } else { + if (result.detectionsTotal <= 0) { + setResult("no detections? strange", Color.YELLOW); + } else if (result.detectionsPositive > 0) { + setResult("Uh oh, " + result.detectionsPositive + "/" + result.detectionsTotal + " engines detected the url (as of date " + result.date + ")", Color.RED); + } else { + setResult("None of the " + result.detectionsTotal + " engines detected the site (as of date " + result.date + ")", Color.GREEN); + } + } + } + + } + + public void onClick(View view) { + switch (view.getId()) { + case R.id.btn_scan: + scanUrl(); + break; + case R.id.btn_goRedirect: + followRedirect(); + break; + case R.id.txt_result: + if (result != null) { + openUrlInBrowser(result.scanUrl); + } + break; + + //DEBUG: just in case + case R.id.txt_url: + openUrlInBrowser(url); + break; + } + } + + public void onLongClick(View view) { + switch (view.getId()) { + case R.id.btn_goRedirect: + case R.id.btn_scan: + Toast.makeText(this, view.getContentDescription(), Toast.LENGTH_SHORT).show(); + break; + + case R.id.txt_result: + showDebug(); + break; + } + } + + private void showDebug() { + if (result != null) { + new AlertDialog.Builder(this) + .setMessage(result.info) + .show(); + } + } + + //https://stackoverflow.com/questions/1884230/urlconnection-doesnt-follow-redirect + private void followRedirect() { + + new Thread(new Runnable() { + public void run() { + String message = null; + HttpURLConnection conn = null; + try { + conn = (HttpURLConnection) new URL(url).openConnection(); + conn.setInstanceFollowRedirects(false); // Make the logic below easier to detect redirections + switch (conn.getResponseCode()) { + case HttpURLConnection.HTTP_MOVED_PERM: + case HttpURLConnection.HTTP_MOVED_TEMP: + String location = conn.getHeaderField("Location"); + location = URLDecoder.decode(location, "UTF-8"); + url = new URL(new URL(url), location).toExternalForm(); // Deal with relative URLs + result = null; + break; + default: + message = "No redirection, final URL, try to scan now"; + } + } catch (IOException e) { + e.printStackTrace(); + message = "Error when following redirect"; + } finally { + if (conn != null) { + conn.disconnect(); + } + } + _createBrowsers(); + + final String finalMessage = message; + runOnUiThread(new Runnable() { + public void run() { + updateUI(); + if (finalMessage != null) { + Toast.makeText(OpenLink.this, finalMessage, Toast.LENGTH_SHORT).show(); + btn_redirect.setEnabled(false); + } + } + }); + } + }).start(); + + + } + + private void createBrowsers() { + new Thread(new Runnable() { + @Override + public void run() { + _createBrowsers(); + runOnUiThread(new Runnable() { + @Override + public void run() { + adapter.notifyDataSetChanged(); + } + }); + } + }).start(); + } + + private void _createBrowsers() { + adapter.clearAll(); + Intent baseIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + PackageManager packageManager = getPackageManager(); + List resolveInfos = packageManager.queryIntentActivities(baseIntent, Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PackageManager.MATCH_ALL : 0); + for (ResolveInfo resolveInfo : resolveInfos) { + if (!resolveInfo.activityInfo.packageName.equals(getPackageName())) { + adapter.addItem(resolveInfo.activityInfo.packageName); + } + } + } + + + private void openUrlInBrowser(String url) { + Intent baseIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + PackageManager packageManager = getPackageManager(); + List resolveInfos = packageManager.queryIntentActivities(baseIntent, Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PackageManager.MATCH_ALL : 0); + List intents = new ArrayList<>(); + for (ResolveInfo resolveInfo : resolveInfos) { + if (!resolveInfo.activityInfo.packageName.equals(getPackageName())) { + Intent intent = new Intent(baseIntent); + intent.setPackage(resolveInfo.activityInfo.packageName); + intents.add(intent); + } + } + + Intent chooserIntent = Intent.createChooser(intents.remove(0), "Choose app"); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents.toArray(new Parcelable[intents.size()])); + + startActivity(chooserIntent); + + //startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); + finish(); + } +} diff --git a/app/src/main/java/com/trianguloy/urlchecker/Settings.java b/app/src/main/java/com/trianguloy/urlchecker/Settings.java new file mode 100644 index 0000000..d535bae --- /dev/null +++ b/app/src/main/java/com/trianguloy/urlchecker/Settings.java @@ -0,0 +1,30 @@ +package com.trianguloy.urlchecker; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.View; + +public class Settings extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + + + } + + public void onClick(View view) { + switch (view.getId()) { + case R.id.btn_debug: + openGoogle(); + break; + } + } + + private void openGoogle() { + startActivity(Intent.createChooser(new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com")), "Open")); + } +} diff --git a/app/src/main/java/com/trianguloy/urlchecker/VirusTotalUtility.java b/app/src/main/java/com/trianguloy/urlchecker/VirusTotalUtility.java new file mode 100644 index 0000000..c7106bf --- /dev/null +++ b/app/src/main/java/com/trianguloy/urlchecker/VirusTotalUtility.java @@ -0,0 +1,123 @@ +package com.trianguloy.urlchecker; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; + +/** + * Class that [INSERT DESCRIPTION HERE] + */ +public class VirusTotalUtility { + static private final String key = "**REMOVED**"; + + static private final String urlGetReport = "http://www.virustotal.com/vtapi/v2/url/report"; + + static class InternalReponse { + String error = "Unknown error"; + int detectionsPositive; + int detectionsTotal; + String date; + String scanUrl; + String info; + } + + static InternalReponse scanUrl(String urlToScan) { + InternalReponse result = new InternalReponse(); + + String responseJSON = performPOST(urlGetReport, getPOSTparameters(urlToScan)); + + // parse response + try { + JSONObject response = new JSONObject(responseJSON); + + result.info = response.toString(1); + + if (response.getInt("response_code") == 1) { + result.detectionsPositive = response.optInt("positives", -1); + result.detectionsTotal = response.optInt("total", -1); + result.date = response.optString("scan_date", null); + result.scanUrl = response.optString("permalink", null); + + result.error = null; + return result; + } else { + result.error = response.getString("verbose_msg"); + } + } catch (JSONException e) { + e.printStackTrace(); + result.error = "JSON exception"; + } + + + return result; + + } + + static private String getPOSTparameters(String url) { + String data = null; + try { + data = URLEncoder.encode("resource", "UTF-8") + + "=" + URLEncoder.encode(url, "UTF-8"); + + data += "&" + URLEncoder.encode("scan", "UTF-8") + "=" + + URLEncoder.encode("1", "UTF-8"); + + data += "&" + URLEncoder.encode("apikey", "UTF-8") + + "=" + URLEncoder.encode(key, "UTF-8"); + + data += "&" + URLEncoder.encode("allinfo", "UTF-8") + + "=" + URLEncoder.encode("true", "UTF-8"); + + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return data; + } + + + private static String performPOST(String urlString, String parameters) { + BufferedReader reader = null; + try { + + // Defined URL where to send data + URL url = new URL(urlString); + + // Send POST data request + URLConnection conn = url.openConnection(); + conn.setDoOutput(true); + OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); + wr.write(parameters); + wr.flush(); + + // Get the server response + + reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); + StringBuilder sb = new StringBuilder(); + String line = null; + // Read Server Response + while ((line = reader.readLine()) != null) { + // Append server response in string + sb.append(line).append("\n"); + } + return sb.toString(); + } catch (Exception ex) { + ex.printStackTrace(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + return null; + } +} diff --git a/app/src/main/res/layout/activity_open_link.xml b/app/src/main/res/layout/activity_open_link.xml new file mode 100644 index 0000000..327d7dc --- /dev/null +++ b/app/src/main/res/layout/activity_open_link.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..c0627f3 --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,22 @@ + + + + + +