Display the IP Address in the View SSL Certificate dialog. https://redmine.stoutner...
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / ViewSslCertificateDialog.java
index 90a13271993f2cd24759d7a38c9535c69b833309..c236d3a86e78061a3a581ab2e012fd269de48afd 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2016-2018 Soren Stoutner <soren@stoutner.com>.
+ * Copyright © 2016-2019 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
  *
@@ -20,6 +20,7 @@
 package com.stoutner.privacybrowser.dialogs;
 
 import android.annotation.SuppressLint;
+import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
@@ -27,6 +28,7 @@ import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.net.http.SslCertificate;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
@@ -38,6 +40,9 @@ import android.widget.TextView;
 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
 import com.stoutner.privacybrowser.R;
 
+import java.lang.ref.WeakReference;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.text.DateFormat;
 import java.util.Calendar;
 import java.util.Date;
@@ -113,8 +118,9 @@ public class ViewSslCertificateDialog extends DialogFragment {
             // The alert dialog must be shown before items in the layout can be modified.
             alertDialog.show();
 
-            // Get handles for the `TextViews`.
+            // Get handles for the text views.
             TextView domainTextView = alertDialog.findViewById(R.id.domain);
+            TextView ipAddressesTextView = alertDialog.findViewById(R.id.ip_addresses);
             TextView issuedToCNameTextView = alertDialog.findViewById(R.id.issued_to_cname);
             TextView issuedToONameTextView = alertDialog.findViewById(R.id.issued_to_oname);
             TextView issuedToUNameTextView = alertDialog.findViewById(R.id.issued_to_uname);
@@ -132,43 +138,46 @@ public class ViewSslCertificateDialog extends DialogFragment {
             String startDateLabel = getString(R.string.start_date) + "  ";
             String endDateLabel = getString(R.string.end_date) + "  ";
 
-            // Parse `formattedUrlString` to a `URI`.
+            // Convert the formatted URL string to a URI.
             Uri uri = Uri.parse(MainWebViewActivity.formattedUrlString);
 
-            // Extract the domain name from `uri`.
+            // Extract the domain name from the URI.
             String domainString = uri.getHost();
 
+            // Get the IP addresses.
+            new GetIpAddresses(getActivity(), alertDialog).execute(domainString);
+
             // Get the SSL certificate.
             SslCertificate sslCertificate = MainWebViewActivity.sslCertificate;
 
             // Get the strings from the SSL certificate.
-            String issuedToCNameString = sslCertificate.getIssuedTo().getCName();
-            String issuedToONameString = sslCertificate.getIssuedTo().getOName();
-            String issuedToUNameString = sslCertificate.getIssuedTo().getUName();
-            String issuedByCNameString = sslCertificate.getIssuedBy().getCName();
-            String issuedByONameString = sslCertificate.getIssuedBy().getOName();
-            String issuedByUNameString = sslCertificate.getIssuedBy().getUName();
+            String issuedToCName = sslCertificate.getIssuedTo().getCName();
+            String issuedToOName = sslCertificate.getIssuedTo().getOName();
+            String issuedToUName = sslCertificate.getIssuedTo().getUName();
+            String issuedByCName = sslCertificate.getIssuedBy().getCName();
+            String issuedByOName = sslCertificate.getIssuedBy().getOName();
+            String issuedByUName = sslCertificate.getIssuedBy().getUName();
             Date startDate = sslCertificate.getValidNotBeforeDate();
             Date endDate = sslCertificate.getValidNotAfterDate();
 
-            // Create a `SpannableStringBuilder` for each `TextView` that needs multiple colors of text.
+            // Create spannable string builders for each text view that needs multiple colors of text.
             SpannableStringBuilder domainStringBuilder = new SpannableStringBuilder(domainLabel + domainString);
-            SpannableStringBuilder issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedToCNameString);
-            SpannableStringBuilder issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedToONameString);
-            SpannableStringBuilder issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedToUNameString);
-            SpannableStringBuilder issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedByCNameString);
-            SpannableStringBuilder issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedByONameString);
-            SpannableStringBuilder issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedByUNameString);
+            SpannableStringBuilder issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedToCName);
+            SpannableStringBuilder issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedToOName);
+            SpannableStringBuilder issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedToUName);
+            SpannableStringBuilder issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedByCName);
+            SpannableStringBuilder issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedByOName);
+            SpannableStringBuilder issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedByUName);
             SpannableStringBuilder startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(startDate));
             SpannableStringBuilder endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(endDate));
 
-            // Create a red `ForegroundColorSpan`.  We have to use the deprecated `getColor` until API >= 23.
+            // Create a red foreground color span.  The deprecated `getColor` must be used until the minimum API >= 23.
             @SuppressWarnings("deprecation") ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
 
-            // Create a blue `ForegroundColorSpan`.
+            // Create a blue foreground color span.
             ForegroundColorSpan blueColorSpan;
 
-            // Set `blueColorSpan` according to the theme.  We have to use the deprecated `getColor()` until API >= 23.
+            // Set the blue color span according to the theme.  The deprecated `getColor()` must be used until the minimum API >= 23.
             if (MainWebViewActivity.darkTheme) {
                 //noinspection deprecation
                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
@@ -177,14 +186,17 @@ public class ViewSslCertificateDialog extends DialogFragment {
                 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
             }
 
+            // Remove the incorrect lint error that `.equals` might produce a NullPointerException.
+            assert domainString != null;
+
             // Formet the `domainString` and `issuedToCName` colors.
-            if (domainString.equals(issuedToCNameString)) {  // `domainString` and `issuedToCNameString` match.
+            if (domainString.equals(issuedToCName)) {  // `domainString` and `issuedToCName` match.
                 // Set the strings to be blue.
                 domainStringBuilder.setSpan(blueColorSpan, domainLabel.length(), domainStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                 issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-            } else if(issuedToCNameString.startsWith("*.")){  // `issuedToCNameString` begins with a wildcard.
+            } else if(issuedToCName.startsWith("*.")){  // `issuedToCName` begins with a wildcard.
                 // Remove the initial `*.`.
-                String baseCertificateDomain = issuedToCNameString.substring(2);
+                String baseCertificateDomain = issuedToCName.substring(2);
 
                 // Setup a copy of `domainString` to test subdomains.
                 String domainStringSubdomain = domainString;
@@ -219,7 +231,7 @@ public class ViewSslCertificateDialog extends DialogFragment {
                 issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
             }
 
-            // Setup the issued to and issued by spans to display the certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
+            // Set the issued to and issued by spans to display the certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
             issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
             issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
             issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
@@ -244,6 +256,7 @@ public class ViewSslCertificateDialog extends DialogFragment {
 
             // Display the strings.
             domainTextView.setText(domainStringBuilder);
+            ipAddressesTextView.setText(getString(R.string.ip_addresses));
             issuedToCNameTextView.setText(issuedToCNameStringBuilder);
             issuedToONameTextView.setText(issuedToONameStringBuilder);
             issuedToUNameTextView.setText(issuedToUNameStringBuilder);
@@ -257,4 +270,97 @@ public class ViewSslCertificateDialog extends DialogFragment {
             return alertDialog;
         }
     }
-}
+
+    // This must run asynchronously because it involves a network request.  `String` declares the parameters.  `Void` does not declare progress units.  `String` contains the results.
+    private static class GetIpAddresses extends AsyncTask<String, Void, SpannableStringBuilder> {
+        // The weak references are used to determine if the activity or the alert dialog have disappeared while the AsyncTask is running.
+        private WeakReference<Activity> activityWeakReference;
+        private WeakReference<AlertDialog> alertDialogWeakReference;
+
+        GetIpAddresses(Activity activity, AlertDialog alertDialog) {
+            // Populate the weak references.
+            activityWeakReference = new WeakReference<>(activity);
+            alertDialogWeakReference = new WeakReference<>(alertDialog);
+        }
+
+        @Override
+        protected SpannableStringBuilder doInBackground(String... domainName) {
+            // Get handles for the activity and the alert dialog.
+            Activity activity = activityWeakReference.get();
+            AlertDialog alertDialog = alertDialogWeakReference.get();
+
+            // Abort if the activity or the dialog is gone.
+            if ((activity == null) || (activity.isFinishing()) || (alertDialog == null)) {
+                return new SpannableStringBuilder();
+            }
+
+            // Initialize an IP address string builder.
+            StringBuilder ipAddresses = new StringBuilder();
+
+            // Get an array with the IP addresses for the host.
+            try {
+                // Get an array with all the IP addresses for the domain.
+                InetAddress[] inetAddressesArray = InetAddress.getAllByName(domainName[0]);
+
+                // Add each IP address to the string builder.
+                for (InetAddress inetAddress : inetAddressesArray) {
+                    if (ipAddresses.length() == 0) {  // This is the first IP address.
+                        // Add the IP Address to the string builder.
+                        ipAddresses.append(inetAddress.getHostAddress());
+                    } else {  // This is not the first IP address.
+                        // Add a line break to the string builder first.
+                        ipAddresses.append("\n");
+
+                        // Add the IP address to the string builder.
+                        ipAddresses.append(inetAddress.getHostAddress());
+                    }
+                }
+            } catch (UnknownHostException exception) {
+                // Do nothing.
+            }
+
+            // Set the label.
+            String ipAddressesLabel = activity.getString(R.string.ip_addresses) + "  ";
+
+            // Create a spannable string builder.
+            SpannableStringBuilder ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + ipAddresses);
+
+            // Create a blue foreground color span.
+            ForegroundColorSpan blueColorSpan;
+
+            // Set the blue color span according to the theme.  The deprecated `getColor()` must be used until the minimum API >= 23.
+            if (MainWebViewActivity.darkTheme) {
+                //noinspection deprecation
+                blueColorSpan = new ForegroundColorSpan(activity.getResources().getColor(R.color.blue_400));
+            } else {
+                //noinspection deprecation
+                blueColorSpan = new ForegroundColorSpan(activity.getResources().getColor(R.color.blue_700));
+            }
+
+            // Set the string builder to display the certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
+            ipAddressesStringBuilder.setSpan(blueColorSpan, ipAddressesLabel.length(), ipAddressesStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+
+            // Return the formatted string.
+            return ipAddressesStringBuilder;
+        }
+
+        // `onPostExecute()` operates on the UI thread.
+        @Override
+        protected void onPostExecute(SpannableStringBuilder ipAddresses) {
+            // Get handles for the activity and the alert dialog.
+            Activity activity = activityWeakReference.get();
+            AlertDialog alertDialog = alertDialogWeakReference.get();
+
+            // Abort if the activity or the alert dialog is gone.
+            if ((activity == null) || (activity.isFinishing()) || (alertDialog == null)) {
+                return;
+            }
+
+            // Get a handle for the IP addresses text view.
+            TextView ipAddressesTextView = alertDialog.findViewById(R.id.ip_addresses);
+
+            // Populate the IP addresses text view.
+            ipAddressesTextView.setText(ipAddresses);
+        }
+    }
+}
\ No newline at end of file