From: Soren Stoutner Date: Tue, 29 Jan 2019 22:19:47 +0000 (-0700) Subject: Implement IP Address Pinning. https://redmine.stoutner.com/issues/212 X-Git-Tag: v2.16~1 X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyBrowserAndroid.git;a=commitdiff_plain;h=6b4312dc0c2d6cb059a0fbe6d4e7cd9317db34b6 Implement IP Address Pinning. https://redmine.stoutner.com/issues/212 --- diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java index 1d9a62a6..7dbb6bf3 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/DomainsActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2018 Soren Stoutner . + * Copyright © 2017-2019 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -224,7 +224,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo deleteMenuItem.setVisible(true); // Hide `add_domain_fab`. - addDomainFAB.setVisibility(View.GONE); + addDomainFAB.hide(); // Display `domainSettingsFragment`. supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit(); @@ -255,7 +255,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo deleteMenuItem.setVisible(true); // Hide `add_domain_fab`. - addDomainFAB.setVisibility(View.GONE); + addDomainFAB.hide(); // Display `domainSettingsFragment`. supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit(); @@ -317,8 +317,8 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Populate the list of domains. `-1` highlights the first domain if in two-paned mode. It has no effect in single-paned mode. populateDomainsListView(-1); - // Display the add domain FAB. - addDomainFAB.setVisibility(View.VISIBLE); + // Show the add domain FAB. + addDomainFAB.show(); // Hide the delete menu item. deleteMenuItem.setVisible(false); @@ -361,8 +361,8 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainsListFragment).commit(); supportFragmentManager.executePendingTransactions(); - // Display `addDomainFAB`. - addDomainFAB.setVisibility(View.VISIBLE); + // Show the add domain FAB. + addDomainFAB.show(); // Hide `deleteMenuItem`. deleteMenuItem.setVisible(false); @@ -453,9 +453,8 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Display `domainSettingsFragment`. supportFragmentManager.beginTransaction().replace(R.id.domains_listview_fragment_container, domainSettingsFragment).commit(); - // Hide `add_domain_fab`. - FloatingActionButton addDomainFAB = findViewById(R.id.add_domain_fab); - addDomainFAB.setVisibility(View.GONE); + // Hide the add domain FAB. + addDomainFAB.hide(); // Show and enable `deleteMenuItem`. deleteMenuItem.setVisible(true); @@ -575,8 +574,8 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo // Populate the list of domains. `-1` highlights the first domain if in two-paned mode. It has no effect in single-paned mode. populateDomainsListView(-1); - // Display the add domain FAB. - addDomainFAB.setVisibility(View.VISIBLE); + // Show the add domain FAB. + addDomainFAB.show(); // Hide the delete menu item. deleteMenuItem.setVisible(false); @@ -613,8 +612,8 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo if (twoPanedMode) { // The device in in two-paned mode. populateDomainsListView(currentDomainDatabaseId); } else { // The device is in single-paned mode. - // Hide `add_domain_fab`. - addDomainFAB.setVisibility(View.GONE); + // Hide the add domain FAB. + addDomainFAB.hide(); // Show and enable `deleteMenuItem`. DomainsActivity.deleteMenuItem.setVisible(true); @@ -635,26 +634,28 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo public void saveDomainSettings(View view, Resources resources) { // Get handles for the domain settings. EditText domainNameEditText = view.findViewById(R.id.domain_settings_name_edittext); - Switch javaScriptSwitch = view.findViewById(R.id.domain_settings_javascript_switch); - Switch firstPartyCookiesSwitch = view.findViewById(R.id.domain_settings_first_party_cookies_switch); - Switch thirdPartyCookiesSwitch = view.findViewById(R.id.domain_settings_third_party_cookies_switch); - Switch domStorageSwitch = view.findViewById(R.id.domain_settings_dom_storage_switch); - Switch formDataSwitch = view.findViewById(R.id.domain_settings_form_data_switch); // Form data can be removed once the minimum API >= 26. - Switch easyListSwitch = view.findViewById(R.id.domain_settings_easylist_switch); - Switch easyPrivacySwitch = view.findViewById(R.id.domain_settings_easyprivacy_switch); - Switch fanboysAnnoyanceSwitch = view.findViewById(R.id.domain_settings_fanboys_annoyance_list_switch); - Switch fanboysSocialBlockingSwitch = view.findViewById(R.id.domain_settings_fanboys_social_blocking_list_switch); - Switch ultraPrivacySwitch = view.findViewById(R.id.domain_settings_ultraprivacy_switch); - Switch blockAllThirdPartyRequestsSwitch = view.findViewById(R.id.domain_settings_block_all_third_party_requests_switch); - Spinner userAgentSpinner = view.findViewById(R.id.domain_settings_user_agent_spinner); - EditText customUserAgentEditText = view.findViewById(R.id.domain_settings_custom_user_agent_edittext); - Spinner fontSizeSpinner = view.findViewById(R.id.domain_settings_font_size_spinner); - Spinner swipeToRefreshSpinner = view.findViewById(R.id.domain_settings_swipe_to_refresh_spinner); - Spinner displayWebpageImagesSpinner = view.findViewById(R.id.domain_settings_display_webpage_images_spinner); - Spinner nightModeSpinner = view.findViewById(R.id.domain_settings_night_mode_spinner); - Switch pinnedSslCertificateSwitch = view.findViewById(R.id.domain_settings_pinned_ssl_certificate_switch); - RadioButton savedSslCertificateRadioButton = view.findViewById(R.id.saved_ssl_certificate_radiobutton); + Switch javaScriptSwitch = view.findViewById(R.id.javascript_switch); + Switch firstPartyCookiesSwitch = view.findViewById(R.id.first_party_cookies_switch); + Switch thirdPartyCookiesSwitch = view.findViewById(R.id.third_party_cookies_switch); + Switch domStorageSwitch = view.findViewById(R.id.dom_storage_switch); + Switch formDataSwitch = view.findViewById(R.id.form_data_switch); // Form data can be removed once the minimum API >= 26. + Switch easyListSwitch = view.findViewById(R.id.easylist_switch); + Switch easyPrivacySwitch = view.findViewById(R.id.easyprivacy_switch); + Switch fanboysAnnoyanceSwitch = view.findViewById(R.id.fanboys_annoyance_list_switch); + Switch fanboysSocialBlockingSwitch = view.findViewById(R.id.fanboys_social_blocking_list_switch); + Switch ultraPrivacySwitch = view.findViewById(R.id.ultraprivacy_switch); + Switch blockAllThirdPartyRequestsSwitch = view.findViewById(R.id.block_all_third_party_requests_switch); + Spinner userAgentSpinner = view.findViewById(R.id.user_agent_spinner); + EditText customUserAgentEditText = view.findViewById(R.id.custom_user_agent_edittext); + Spinner fontSizeSpinner = view.findViewById(R.id.font_size_spinner); + Spinner swipeToRefreshSpinner = view.findViewById(R.id.swipe_to_refresh_spinner); + Spinner displayWebpageImagesSpinner = view.findViewById(R.id.display_webpage_images_spinner); + Spinner nightModeSpinner = view.findViewById(R.id.night_mode_spinner); + Switch pinnedSslCertificateSwitch = view.findViewById(R.id.pinned_ssl_certificate_switch); RadioButton currentWebsiteCertificateRadioButton = view.findViewById(R.id.current_website_certificate_radiobutton); + Switch pinnedIpAddressesSwitch = view.findViewById(R.id.pinned_ip_addresses_switch); + RadioButton currentIpAddressesRadioButton = view.findViewById(R.id.current_ip_addresses_radiobutton); + TextView currentIpAddressesTextView = view.findViewById(R.id.current_ip_addresses_textview); // Extract the data for the domain settings. String domainNameString = domainNameEditText.getText().toString(); @@ -675,6 +676,7 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo int displayWebpageImagesInt = displayWebpageImagesSpinner.getSelectedItemPosition(); int nightModeInt = nightModeSpinner.getSelectedItemPosition(); boolean pinnedSslCertificate = pinnedSslCertificateSwitch.isChecked(); + boolean pinnedIpAddress = pinnedIpAddressesSwitch.isChecked(); // Initialize the user agent name string. String userAgentName; @@ -703,12 +705,12 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo int fontSizeInt = Integer.parseInt(resources.getStringArray(R.array.domain_settings_font_size_entry_values)[fontSizePosition]); // Save the domain settings. - if (savedSslCertificateRadioButton.isChecked()) { // The current certificate is being used. - // Update the database except for the certificate. - domainsDatabaseHelper.updateDomainExceptCertificate(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScriptEnabled, firstPartyCookiesEnabled, thirdPartyCookiesEnabled, + domainsDatabaseHelper.updateDomain(DomainsActivity.currentDomainDatabaseId, domainNameString, javaScriptEnabled, firstPartyCookiesEnabled, thirdPartyCookiesEnabled, domStorageEnabled, formDataEnabled, easyListEnabled, easyPrivacyEnabled, fanboysAnnoyanceEnabled, fanboysSocialBlockingEnabled, ultraPrivacyEnabled, blockAllThirdPartyRequests, - userAgentName, fontSizeInt, swipeToRefreshInt, nightModeInt, displayWebpageImagesInt, pinnedSslCertificate); - } else if (currentWebsiteCertificateRadioButton.isChecked()) { // The certificate is being updated with the current website certificate. + userAgentName, fontSizeInt, swipeToRefreshInt, nightModeInt, displayWebpageImagesInt, pinnedSslCertificate, pinnedIpAddress); + + // Update the pinned SSL certificate if a new one is checked. + if (currentWebsiteCertificateRadioButton.isChecked()) { // Get the current website SSL certificate. SslCertificate currentWebsiteSslCertificate = MainWebViewActivity.sslCertificate; @@ -723,16 +725,17 @@ public class DomainsActivity extends AppCompatActivity implements AddDomainDialo long endDateLong = currentWebsiteSslCertificate.getValidNotAfterDate().getTime(); // Update the database. - domainsDatabaseHelper.updateDomainWithCertificate(currentDomainDatabaseId, domainNameString, javaScriptEnabled, firstPartyCookiesEnabled, thirdPartyCookiesEnabled, domStorageEnabled, - formDataEnabled, easyListEnabled, easyPrivacyEnabled, fanboysAnnoyanceEnabled, fanboysSocialBlockingEnabled, ultraPrivacyEnabled, blockAllThirdPartyRequests, userAgentName, fontSizeInt, - swipeToRefreshInt, nightModeInt, displayWebpageImagesInt, pinnedSslCertificate, issuedToCommonName, issuedToOrganization, issuedToOrganizationalUnit, issuedByCommonName, - issuedByOrganization, issuedByOrganizationalUnit, startDateLong, endDateLong); - - } else { // No certificate is selected. - // Update the database, with PINNED_SSL_CERTIFICATE set to false. - domainsDatabaseHelper.updateDomainExceptCertificate(currentDomainDatabaseId, domainNameString, javaScriptEnabled, firstPartyCookiesEnabled, thirdPartyCookiesEnabled, domStorageEnabled, - formDataEnabled, easyListEnabled, easyPrivacyEnabled, fanboysAnnoyanceEnabled, fanboysSocialBlockingEnabled, ultraPrivacyEnabled, blockAllThirdPartyRequests, userAgentName, fontSizeInt, - swipeToRefreshInt, nightModeInt, displayWebpageImagesInt,false); + domainsDatabaseHelper.updatePinnedSslCertificate(currentDomainDatabaseId, issuedToCommonName, issuedToOrganization, issuedToOrganizationalUnit, issuedByCommonName, issuedByOrganization, + issuedByOrganizationalUnit, startDateLong, endDateLong); + } + + // Update the pinned IP addresses if new ones are checked. + if (currentIpAddressesRadioButton.isChecked()) { + // Get the current IP addresses. + String currentIpAddresses = currentIpAddressesTextView.getText().toString(); + + // Update the database. + domainsDatabaseHelper.updatePinnedIpAddresses(currentDomainDatabaseId, currentIpAddresses); } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java index fb300469..a2d17975 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ImportExportActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2018 Soren Stoutner . + * Copyright © 2018-2019 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -59,6 +59,7 @@ import com.stoutner.privacybrowser.helpers.ImportExportDatabaseHelper; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.Arrays; @@ -674,7 +675,7 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp secureRandom.nextBytes(saltByteArray); // Convert the encryption password to a byte array. - byte[] encryptionPasswordByteArray = encryptionPasswordString.getBytes("UTF-8"); + byte[] encryptionPasswordByteArray = encryptionPasswordString.getBytes(StandardCharsets.UTF_8); // Append the salt to the encryption password byte array. This protects against rainbow table attacks. byte[] encryptionPasswordWithSaltByteArray = new byte[encryptionPasswordByteArray.length + saltByteArray.length]; @@ -832,7 +833,7 @@ public class ImportExportActivity extends AppCompatActivity implements ImportExp encryptedImportFileInputStream.read(initializationVector); // Convert the encryption password to a byte array. - byte[] encryptionPasswordByteArray = encryptionPasswordString.getBytes("UTF-8"); + byte[] encryptionPasswordByteArray = encryptionPasswordString.getBytes(StandardCharsets.UTF_8); // Append the salt to the encryption password byte array. This protects against rainbow table attacks. byte[] encryptionPasswordWithSaltByteArray = new byte[encryptionPasswordByteArray.length + saltByteArray.length]; diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java index 96098947..1ced078a 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -47,6 +47,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.net.http.SslCertificate; import android.net.http.SslError; +import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; @@ -123,7 +124,7 @@ import com.stoutner.privacybrowser.dialogs.DownloadLocationPermissionDialog; import com.stoutner.privacybrowser.dialogs.EditBookmarkDialog; import com.stoutner.privacybrowser.dialogs.EditBookmarkFolderDialog; import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog; -import com.stoutner.privacybrowser.dialogs.PinnedSslCertificateMismatchDialog; +import com.stoutner.privacybrowser.dialogs.PinnedMismatchDialog; import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog; import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog; import com.stoutner.privacybrowser.helpers.AdHelper; @@ -139,10 +140,13 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.lang.ref.WeakReference; +import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -155,7 +159,7 @@ import java.util.Set; public class MainWebViewActivity extends AppCompatActivity implements CreateBookmarkDialog.CreateBookmarkListener, CreateBookmarkFolderDialog.CreateBookmarkFolderListener, CreateHomeScreenShortcutDialog.CreateHomeScreenShortcutListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, DownloadLocationPermissionDialog.DownloadLocationPermissionDialogListener, EditBookmarkDialog.EditBookmarkListener, EditBookmarkFolderDialog.EditBookmarkFolderListener, - HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener, + HttpAuthenticationDialog.HttpAuthenticationListener, NavigationView.OnNavigationItemSelectedListener, PinnedMismatchDialog.PinnedMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, UrlHistoryDialog.UrlHistoryListener { // `darkTheme` is public static so it can be accessed from everywhere. @@ -169,14 +173,21 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onCreateHomeScreenShortcutCreate()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `applyDomainSettings()`. public static Bitmap favoriteIconBitmap; - // `formattedUrlString` is public static so it can be accessed from `BookmarksActivity`, `CreateBookmarkDialog`, and `AddDomainDialog`. + // `favoriteIconDefaultBitmap` public static so it can be accessed from `PinnedMismatchDialog`. It is also used in `onCreate()` and `applyDomainSettings`. + public static Bitmap favoriteIconDefaultBitmap; + + // `formattedUrlString` is public static so it can be accessed from `AddDomainDialog`, `BookmarksActivity`, `DomainSettingsFragment`, `CreateBookmarkDialog`, and `PinnedMismatchDialog`. // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, `loadUrlFromTextBox()`, and `applyProxyThroughOrbot()`. public static String formattedUrlString; - // `sslCertificate` is public static so it can be accessed from `DomainsActivity`, `DomainsListFragment`, `DomainSettingsFragment`, `PinnedSslCertificateMismatchDialog`, - // and `ViewSslCertificateDialog`. It is also used in `onCreate()`. + // `sslCertificate` is public static so it can be accessed from `DomainsActivity`, `DomainsListFragment`, `DomainSettingsFragment`, `PinnedMismatchDialog`, and `ViewSslCertificateDialog`. + // It is also used in `onCreate()`. public static SslCertificate sslCertificate; + // `currentHostIpAddresses` is public static so it can be accessed from `DomainSettingsFragment` and `ViewSslCertificateDialog`. + // It is also used in `onCreate()` and `GetHostIpAddresses()`. + public static String currentHostIpAddresses; + // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`, `onResume()`, and `applyProxyThroughOrbot()`. public static String orbotStatus; @@ -257,18 +268,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, and `loadBookmarksFolder()`. public static String currentBookmarksFolder; - // `domainSettingsDatabaseId` is public static so it can be accessed from `PinnedSslCertificateMismatchDialog`. It is also used in `onCreate()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. + // `domainSettingsDatabaseId` is public static so it can be accessed from `PinnedMismatchDialog`. It is also used in `onCreate()`, `onOptionsItemSelected()`, and `applyDomainSettings()`. public static int domainSettingsDatabaseId; - // The pinned domain SSL Certificate variables are public static so they can be accessed from `PinnedSslCertificateMismatchDialog`. They are also used in `onCreate()` and `applyDomainSettings()`. - public static String pinnedDomainSslIssuedToCNameString; - public static String pinnedDomainSslIssuedToONameString; - public static String pinnedDomainSslIssuedToUNameString; - public static String pinnedDomainSslIssuedByCNameString; - public static String pinnedDomainSslIssuedByONameString; - public static String pinnedDomainSslIssuedByUNameString; - public static Date pinnedDomainSslStartDate; - public static Date pinnedDomainSslEndDate; + // The pinned variables are public static so they can be accessed from `PinnedMismatchDialog`. They are also used in `onCreate()` and `applyDomainSettings()`. + public static String pinnedSslIssuedToCName; + public static String pinnedSslIssuedToOName; + public static String pinnedSslIssuedToUName; + public static String pinnedSslIssuedByCName; + public static String pinnedSslIssuedByOName; + public static String pinnedSslIssuedByUName; + public static Date pinnedSslStartDate; + public static Date pinnedSslEndDate; + public static String pinnedHostIpAddresses; // The user agent constants are public static so they can be accessed from `SettingsFragment`, `DomainsActivity`, and `DomainSettingsFragment`. public final static int UNRECOGNIZED_USER_AGENT = -1; @@ -285,9 +297,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`. private boolean navigatingHistory; - // `favoriteIconDefaultBitmap` is used in `onCreate()` and `applyDomainSettings`. - private Bitmap favoriteIconDefaultBitmap; - // `drawerLayout` is used in `onCreate()`, `onNewIntent()`, `onBackPressed()`, and `onRestart()`. private DrawerLayout drawerLayout; @@ -404,8 +413,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `currentDomainName` is used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onAddDomain()`, and `applyDomainSettings()`. private String currentDomainName; - // `ignorePinnedSslCertificateForDomain` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`. - private boolean ignorePinnedSslCertificate; + // `pinnedDomainSslCertificate` is used in `onCreate()` and `applyDomainSettings()`. + private boolean pinnedSslCertificate; + + // `pinnedIpAddress` is used in `onCreate()` and `applyDomainSettings()`. + private boolean pinnedIpAddresses; + + // `ignorePinnedDomainInformation` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`. + private boolean ignorePinnedDomainInformation; // `orbotStatusBroadcastReceiver` is used in `onCreate()` and `onDestroy()`. private BroadcastReceiver orbotStatusBroadcastReceiver; @@ -468,9 +483,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // `urlIsLoading` is used in `onCreate()`, `onCreateOptionsMenu()`, `loadUrl()`, and `applyDomainSettings()`. private boolean urlIsLoading; - // `pinnedDomainSslCertificate` is used in `onCreate()` and `applyDomainSettings()`. - private boolean pinnedDomainSslCertificate; - // `bookmarksDatabaseHelper` is used in `onCreate()`, `onDestroy`, `onOptionsItemSelected()`, `onCreateBookmark()`, `onCreateBookmarkFolder()`, `onSaveEditBookmark()`, `onSaveEditBookmarkFolder()`, // and `loadBookmarksFolder()`. private BookmarksDatabaseHelper bookmarksDatabaseHelper; @@ -1595,6 +1607,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Update the URL in urlTextBox when the page starts to load. @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { + // Reset the list of host IP addresses. + currentHostIpAddresses = ""; + // Reset the list of resource requests. resourceRequests.clear(); @@ -1626,6 +1641,12 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Apply text highlighting to `urlTextBox`. highlightUrlText(); + // Get a URI for the current URL. + Uri currentUri = Uri.parse(formattedUrlString); + + // Get the IP addresses for the host. + new GetHostIpAddresses(activity).execute(currentUri.getHost()); + // Apply any custom domain settings if the URL was loaded by navigating history. if (navigatingHistory) { // Apply the domain settings. @@ -1713,7 +1734,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Update `urlTextBox` and apply domain settings if not waiting on Orbot. + // Update the URL text box and apply domain settings if not waiting on Orbot. if (!waitingForOrbot) { // Check to see if `WebView` has set `url` to be `about:blank`. if (url.equals("about:blank")) { // `WebView` is blank, so `formattedUrlString` should be `""` and `urlTextBox` should display a hint. @@ -1744,12 +1765,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } } - // Store the SSL certificate so it can be accessed from `ViewSslCertificateDialog` and `PinnedSslCertificateMismatchDialog`. + // Store the SSL certificate so it can be accessed from `ViewSslCertificateDialog` and `PinnedMismatchDialog`. sslCertificate = mainWebView.getCertificate(); - // Check the current website SSL certificate against the pinned SSL certificate if there is a pinned SSL certificate the user has not chosen to ignore it for this session. - // Also ignore if changes in the user agent causes an error while navigating history. - if (pinnedDomainSslCertificate && !ignorePinnedSslCertificate && navigatingHistory) { + // Check the current website information against any pinned domain information. + if ((pinnedSslCertificate || pinnedIpAddresses) && !ignorePinnedDomainInformation) { // Initialize the current SSL certificate variables. String currentWebsiteIssuedToCName = ""; String currentWebsiteIssuedToOName = ""; @@ -1773,11 +1793,11 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook currentWebsiteSslEndDate = sslCertificate.getValidNotAfterDate(); } - // Initialize `String` variables to store the SSL certificate dates. `Strings` are needed to compare the values below, which doesn't work with `Dates` if they are `null`. + // Initialize string variables to store the SSL certificate dates. Strings are needed to compare the values below, which doesn't work with `Dates` if they are `null`. String currentWebsiteSslStartDateString = ""; String currentWebsiteSslEndDateString = ""; - String pinnedDomainSslStartDateString = ""; - String pinnedDomainSslEndDateString = ""; + String pinnedSslStartDateString = ""; + String pinnedSslEndDateString = ""; // Convert the `Dates` to `Strings` if they are not `null`. if (currentWebsiteSslStartDate != null) { @@ -1788,23 +1808,26 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook currentWebsiteSslEndDateString = currentWebsiteSslEndDate.toString(); } - if (pinnedDomainSslStartDate != null) { - pinnedDomainSslStartDateString = pinnedDomainSslStartDate.toString(); + if (pinnedSslStartDate != null) { + pinnedSslStartDateString = pinnedSslStartDate.toString(); } - if (pinnedDomainSslEndDate != null) { - pinnedDomainSslEndDateString = pinnedDomainSslEndDate.toString(); + if (pinnedSslEndDate != null) { + pinnedSslEndDateString = pinnedSslEndDate.toString(); } - // Check to see if the pinned SSL certificate matches the current website certificate. - if (!currentWebsiteIssuedToCName.equals(pinnedDomainSslIssuedToCNameString) || !currentWebsiteIssuedToOName.equals(pinnedDomainSslIssuedToONameString) || - !currentWebsiteIssuedToUName.equals(pinnedDomainSslIssuedToUNameString) || !currentWebsiteIssuedByCName.equals(pinnedDomainSslIssuedByCNameString) || - !currentWebsiteIssuedByOName.equals(pinnedDomainSslIssuedByONameString) || !currentWebsiteIssuedByUName.equals(pinnedDomainSslIssuedByUNameString) || - !currentWebsiteSslStartDateString.equals(pinnedDomainSslStartDateString) || !currentWebsiteSslEndDateString.equals(pinnedDomainSslEndDateString)) { - // The pinned SSL certificate doesn't match the current domain certificate. - //Display the pinned SSL certificate mismatch `AlertDialog`. - AppCompatDialogFragment pinnedSslCertificateMismatchDialogFragment = new PinnedSslCertificateMismatchDialog(); - pinnedSslCertificateMismatchDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_mismatch)); + // Check to see if the pinned information matches the current information. + if ((pinnedIpAddresses && !currentHostIpAddresses.equals(pinnedHostIpAddresses)) || (pinnedSslCertificate && (!currentWebsiteIssuedToCName.equals(pinnedSslIssuedToCName) || + !currentWebsiteIssuedToOName.equals(pinnedSslIssuedToOName) || !currentWebsiteIssuedToUName.equals(pinnedSslIssuedToUName) || + !currentWebsiteIssuedByCName.equals(pinnedSslIssuedByCName) || !currentWebsiteIssuedByOName.equals(pinnedSslIssuedByOName) || + !currentWebsiteIssuedByUName.equals(pinnedSslIssuedByUName) || !currentWebsiteSslStartDateString.equals(pinnedSslStartDateString) || + !currentWebsiteSslEndDateString.equals(pinnedSslEndDateString)))) { + + // Get a handle for the pinned mismatch alert dialog. + AppCompatDialogFragment pinnedMismatchDialogFragment = PinnedMismatchDialog.displayDialog(pinnedSslCertificate, pinnedIpAddresses); + + // Show the pinned mismatch alert dialog. + pinnedMismatchDialogFragment.show(getSupportFragmentManager(), getString(R.string.pinned_mismatch)); } } } @@ -1827,13 +1850,13 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook Date currentWebsiteSslEndDate = currentWebsiteSslCertificate.getValidNotAfterDate(); // Proceed to the website if the current SSL website certificate matches the pinned domain certificate. - if (pinnedDomainSslCertificate && - currentWebsiteIssuedToCName.equals(pinnedDomainSslIssuedToCNameString) && currentWebsiteIssuedToOName.equals(pinnedDomainSslIssuedToONameString) && - currentWebsiteIssuedToUName.equals(pinnedDomainSslIssuedToUNameString) && currentWebsiteIssuedByCName.equals(pinnedDomainSslIssuedByCNameString) && - currentWebsiteIssuedByOName.equals(pinnedDomainSslIssuedByONameString) && currentWebsiteIssuedByUName.equals(pinnedDomainSslIssuedByUNameString) && - currentWebsiteSslStartDate.equals(pinnedDomainSslStartDate) && currentWebsiteSslEndDate.equals(pinnedDomainSslEndDate)) { - // An SSL certificate is pinned and matches the current domain certificate. - // Proceed to the website without displaying an error. + if (pinnedSslCertificate && + currentWebsiteIssuedToCName.equals(pinnedSslIssuedToCName) && currentWebsiteIssuedToOName.equals(pinnedSslIssuedToOName) && + currentWebsiteIssuedToUName.equals(pinnedSslIssuedToUName) && currentWebsiteIssuedByCName.equals(pinnedSslIssuedByCName) && + currentWebsiteIssuedByOName.equals(pinnedSslIssuedByOName) && currentWebsiteIssuedByUName.equals(pinnedSslIssuedByUName) && + currentWebsiteSslStartDate.equals(pinnedSslStartDate) && currentWebsiteSslEndDate.equals(pinnedSslEndDate)) { + + // An SSL certificate is pinned and matches the current domain certificate. Proceed to the website without displaying an error. handler.proceed(); } else { // Either there isn't a pinned SSL certificate or it doesn't match the current website certificate. // Store `handler` so it can be accesses from `onSslErrorCancel()` and `onSslErrorProceed()`. @@ -2849,20 +2872,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook mainWebView.reload(); return true; - case R.id.print: - // Get a `PrintManager` instance. - PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE); - - // Convert `mainWebView` to `printDocumentAdapter`. - PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter(); - - // Remove the lint error below that `printManager` might be `null`. - assert printManager != null; - - // Print the document. The print attributes are `null`. - printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null); - return true; - case R.id.find_on_page: // Hide the URL app bar. supportAppBar.setVisibility(View.GONE); @@ -2881,14 +2890,6 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook }, 200); return true; - case R.id.add_to_homescreen: - // Show the alert dialog. - AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog(); - createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); - - //Everything else will be handled by the alert dialog and the associated listener below. - return true; - case R.id.view_source: // Launch the View Source activity. Intent viewSourceIntent = new Intent(this, ViewSourceActivity.class); @@ -2908,6 +2909,20 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook startActivity(Intent.createChooser(shareIntent, getString(R.string.share_url))); return true; + case R.id.print: + // Get a `PrintManager` instance. + PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE); + + // Convert `mainWebView` to `printDocumentAdapter`. + PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter(); + + // Remove the lint error below that `printManager` might be `null`. + assert printManager != null; + + // Print the document. The print attributes are `null`. + printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null); + return true; + case R.id.open_with_app: // Create the open with intent with `ACTION_VIEW`. Intent openWithAppIntent = new Intent(Intent.ACTION_VIEW); @@ -2936,6 +2951,14 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook startActivity(Intent.createChooser(openWithBrowserIntent, getString(R.string.open_with))); return true; + case R.id.add_to_homescreen: + // Show the alert dialog. + AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog(); + createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); + + //Everything else will be handled by the alert dialog and the associated listener below. + return true; + case R.id.proxy_through_orbot: // Toggle the proxy through Orbot variable. proxyThroughOrbot = !proxyThroughOrbot; @@ -3856,7 +3879,7 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSslMismatchBack() { + public void onPinnedMismatchBack() { if (mainWebView.canGoBack()) { // There is a back page in the history. // Reset the formatted URL string so the page will load correctly if blocking of third-party requests is enabled. formattedUrlString = ""; @@ -3873,9 +3896,9 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook } @Override - public void onSslMismatchProceed() { - // Do not check the pinned SSL certificate for this domain again until the domain changes. - ignorePinnedSslCertificate = true; + public void onPinnedMismatchProceed() { + // Do not check the pinned information for this domain again until the domain changes. + ignorePinnedDomainInformation = true; } @Override @@ -4170,8 +4193,8 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the new `hostname` as the `currentDomainName`. currentDomainName = hostName; - // Reset `ignorePinnedSslCertificate`. - ignorePinnedSslCertificate = false; + // Reset the ignoring of pinned domain information. + ignorePinnedDomainInformation = false; // Reset the favorite icon if specified. if (resetFavoriteIcon) { @@ -4263,13 +4286,15 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook int swipeToRefreshInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SWIPE_TO_REFRESH)); int nightModeInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE)); int displayWebpageImagesInt = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES)); - pinnedDomainSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1); - pinnedDomainSslIssuedToCNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)); - pinnedDomainSslIssuedToONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)); - pinnedDomainSslIssuedToUNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)); - pinnedDomainSslIssuedByCNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)); - pinnedDomainSslIssuedByONameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)); - pinnedDomainSslIssuedByUNameString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)); + pinnedSslCertificate = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)) == 1); + pinnedSslIssuedToCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)); + pinnedSslIssuedToOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)); + pinnedSslIssuedToUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)); + pinnedSslIssuedByCName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)); + pinnedSslIssuedByOName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)); + pinnedSslIssuedByUName = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)); + pinnedIpAddresses = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)) == 1); + pinnedHostIpAddresses = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES)); // Set `nightMode` according to `nightModeInt`. If `nightModeInt` is `DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT` the current setting from `sharedPreferences` will be used. switch (nightModeInt) { @@ -4292,16 +4317,16 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook // Set the pinned SSL certificate start date to `null` if the saved date `long` is 0. if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE)) == 0) { - pinnedDomainSslStartDate = null; + pinnedSslStartDate = null; } else { - pinnedDomainSslStartDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); + pinnedSslStartDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); } // Set the pinned SSL certificate end date to `null` if the saved date `long` is 0. if (currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE)) == 0) { - pinnedDomainSslEndDate = null; + pinnedSslEndDate = null; } else { - pinnedDomainSslEndDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); + pinnedSslEndDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); } // Close `currentHostDomainSettingsCursor`. @@ -4454,17 +4479,19 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook mainWebView.getSettings().setSaveFormData(saveFormDataEnabled); } - // Reset the pinned SSL certificate information. + // Reset the pinned variables. domainSettingsDatabaseId = -1; - pinnedDomainSslCertificate = false; - pinnedDomainSslIssuedToCNameString = ""; - pinnedDomainSslIssuedToONameString = ""; - pinnedDomainSslIssuedToUNameString = ""; - pinnedDomainSslIssuedByCNameString = ""; - pinnedDomainSslIssuedByONameString = ""; - pinnedDomainSslIssuedByUNameString = ""; - pinnedDomainSslStartDate = null; - pinnedDomainSslEndDate = null; + pinnedSslCertificate = false; + pinnedSslIssuedToCName = ""; + pinnedSslIssuedToOName = ""; + pinnedSslIssuedToUName = ""; + pinnedSslIssuedByCName = ""; + pinnedSslIssuedByOName = ""; + pinnedSslIssuedByUName = ""; + pinnedSslStartDate = null; + pinnedSslEndDate = null; + pinnedIpAddresses = false; + pinnedHostIpAddresses = ""; // Set third-party cookies status if API >= 21. if (Build.VERSION.SDK_INT >= 21) { @@ -4799,4 +4826,70 @@ public class MainWebViewActivity extends AppCompatActivity implements CreateBook bookmarksTitleTextView.setText(currentBookmarksFolder); } } + + // 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 GetHostIpAddresses extends AsyncTask { + // The weak references are used to determine if the activity have disappeared while the AsyncTask is running. + private final WeakReference activityWeakReference; + + GetHostIpAddresses(Activity activity) { + // Populate the weak references. + activityWeakReference = new WeakReference<>(activity); + } + + @Override + protected String doInBackground(String... domainName) { + // Get handles for the activity and the alert dialog. + Activity activity = activityWeakReference.get(); + + // Abort if the activity or the dialog is gone. + if ((activity == null) || activity.isFinishing()) { + // Return an empty spannable string builder. + return ""; + } + + // 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. + } + + // Return the string. + return ipAddresses.toString(); + } + + // `onPostExecute()` operates on the UI thread. + @Override + protected void onPostExecute(String ipAddresses) { + // Get handles for the activity and the alert dialog. + Activity activity = activityWeakReference.get(); + + // Abort if the activity or the alert dialog is gone. + if ((activity == null) || activity.isFinishing()) { + return; + } + + // Store the IP addresses. + currentHostIpAddresses = ipAddresses; + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java index 50e7d65c..8e7f5e96 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/ViewSourceActivity.java @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2018 Soren Stoutner . + * Copyright © 2017-2019 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -308,7 +308,7 @@ public class ViewSourceActivity extends AppCompatActivity { Activity viewSourceActivity = activityWeakReference.get(); // Abort if the activity is gone. - if ((viewSourceActivity == null) || (viewSourceActivity.isFinishing())) { + if ((viewSourceActivity == null) || viewSourceActivity.isFinishing()) { return; } @@ -334,7 +334,7 @@ public class ViewSourceActivity extends AppCompatActivity { Activity activity = activityWeakReference.get(); // Abort if the activity is gone. - if ((activity == null) || (activity.isFinishing())) { + if ((activity == null) || activity.isFinishing()) { return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder}; } @@ -667,7 +667,7 @@ public class ViewSourceActivity extends AppCompatActivity { Activity activity = activityWeakReference.get(); // Abort if the activity is gone. - if ((activity == null) || (activity.isFinishing())) { + if ((activity == null) || activity.isFinishing()) { return; } diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadFileDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadFileDialog.java index cf948124..2ade1eae 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadFileDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/DownloadFileDialog.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2018 Soren Stoutner . + * Copyright © 2016-2019 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -66,13 +66,14 @@ public class DownloadFileDialog extends AppCompatDialogFragment { // Create a variable for the file name string. String fileNameString; - // Parse `filename` from `contentDisposition`. + // Parse the filename from `contentDisposition`. if (contentDisposition.contains("filename=\"")) { // The file name is contained in a string surrounded by `""`. fileNameString = contentDisposition.substring(contentDisposition.indexOf("filename=\"") + 10, contentDisposition.indexOf("\"", contentDisposition.indexOf("filename=\"") + 10)); - } else if (contentDisposition.contains("filename=") && ((contentDisposition.indexOf(";", contentDisposition.indexOf("filename=") + 9)) > 0 )) { // The file name is contained in a string beginning with `filename=` and ending with `;`. + } else if (contentDisposition.contains("filename=") && ((contentDisposition.indexOf(";", contentDisposition.indexOf("filename=") + 9)) > 0 )) { + // The file name is contained in a string beginning with `filename=` and ending with `;`. fileNameString = contentDisposition.substring(contentDisposition.indexOf("filename=") + 9, contentDisposition.indexOf(";", contentDisposition.indexOf("filename=") + 9)); } else if (contentDisposition.contains("filename=")) { // The file name is contained in a string beginning with `filename=` and proceeding to the end of `contentDisposition`. - fileNameString = contentDisposition.substring(contentDisposition.indexOf("filename=") + 9, contentDisposition.length()); + fileNameString = contentDisposition.substring(contentDisposition.indexOf("filename=") + 9); } else { // `contentDisposition` does not contain the filename, so use the last path segment of the URL. Uri downloadUri = Uri.parse(urlString); fileNameString = downloadUri.getLastPathSegment(); diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java new file mode 100644 index 00000000..2d506dc4 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedMismatchDialog.java @@ -0,0 +1,472 @@ +/* + * Copyright © 2017-2019 Soren Stoutner . + * + * This file is part of Privacy Browser . + * + * Privacy Browser 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. + * + * Privacy Browser 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 Privacy Browser. If not, see . + */ + +package com.stoutner.privacybrowser.dialogs; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.net.http.SslCertificate; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.TabLayout; +import android.support.v4.view.PagerAdapter; +// `AppCompatDialogFragment` is used instead of `DialogFragment` to avoid an error on API <=22. +import android.support.v7.app.AppCompatDialogFragment; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.style.ForegroundColorSpan; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.TextView; + +import com.stoutner.privacybrowser.R; +import com.stoutner.privacybrowser.activities.MainWebViewActivity; +import com.stoutner.privacybrowser.definitions.WrapVerticalContentViewPager; +import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; + +import java.text.DateFormat; +import java.util.Date; + +public class PinnedMismatchDialog extends AppCompatDialogFragment { + // Instantiate the class variables. + private PinnedMismatchListener pinnedMismatchListener; + private LayoutInflater layoutInflater; + private String currentSslIssuedToCName; + private String currentSslIssuedToOName; + private String currentSslIssuedToUName; + private String currentSslIssuedByCName; + private String currentSslIssuedByOName; + private String currentSslIssuedByUName; + private Date currentSslStartDate; + private Date currentSslEndDate; + private boolean pinnedSslCertificate; + private boolean pinnedIpAddresses; + + // The public interface is used to send information back to the parent activity. + public interface PinnedMismatchListener { + void onPinnedMismatchBack(); + + void onPinnedMismatchProceed(); + } + + // Check to make sure that the parent activity implements the listener. + public void onAttach(Context context) { + // Run the default commands. + super.onAttach(context); + + // Get a handle for `PinnedSslCertificateMismatchListener` from the launching context. + pinnedMismatchListener = (PinnedMismatchListener) context; + } + + public static PinnedMismatchDialog displayDialog(boolean pinnedSslCertificate, boolean pinnedIpAddresses) { + // Create an arguments bundle. + Bundle argumentsBundle = new Bundle(); + + // Store the variables in the bundle. + argumentsBundle.putBoolean("Pinned_SSL_Certificate", pinnedSslCertificate); + argumentsBundle.putBoolean("Pinned_IP_Addresses", pinnedIpAddresses); + + // Add the arguments bundle to this instance of `PinnedMismatchDialog`. + PinnedMismatchDialog thisPinnedMismatchDialog = new PinnedMismatchDialog(); + thisPinnedMismatchDialog.setArguments(argumentsBundle); + return thisPinnedMismatchDialog; + } + + // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`. + @SuppressLint("InflateParams") + @Override + @NonNull + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Remove the incorrect lint warning that `getActivity()` might be null. + assert getActivity() != null; + + // Get the activity's layout inflater. + layoutInflater = getActivity().getLayoutInflater(); + + // Use an alert dialog builder to create the alert dialog. + AlertDialog.Builder dialogBuilder; + + // Set the style according to the theme. + if (MainWebViewActivity.darkTheme) { + // Set the dialog theme. + dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark); + } else { + // Set the dialog theme. + dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight); + } + + // Remove the incorrect lint warning below that `.getArguments.getBoolean()` might be null. + assert getArguments() != null; + + // Get the variables from the bundle. + pinnedSslCertificate = getArguments().getBoolean("Pinned_SSL_Certificate"); + pinnedIpAddresses = getArguments().getBoolean("Pinned_IP_Addresses"); + + if (MainWebViewActivity.favoriteIconBitmap.equals(MainWebViewActivity.favoriteIconDefaultBitmap)) { + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_dark); + } else { + dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_light); + } + } else { + // Create a drawable version of the favorite icon. + Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIconBitmap); + + // Set the icon. + dialogBuilder.setIcon(favoriteIconDrawable); + } + + // Setup the neutral button. + dialogBuilder.setNeutralButton(R.string.update, (DialogInterface dialog, int which) -> { + // Initialize the long date variables. If the date is null, a long value of `0` will be stored in the Domains database entry. + long currentSslStartDateLong = 0; + long currentSslEndDateLong = 0; + + // Convert the `Dates` into `longs`. + if (currentSslStartDate != null) { + currentSslStartDateLong = currentSslStartDate.getTime(); + } + + if (currentSslEndDate != null) { + currentSslEndDateLong = currentSslEndDate.getTime(); + } + + // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. + DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(getContext(), null, null, 0); + + // Update the SSL certificate if it is pinned. + if (pinnedSslCertificate) { + // Update the pinned SSL certificate in the domain database. + domainsDatabaseHelper.updatePinnedSslCertificate(MainWebViewActivity.domainSettingsDatabaseId, currentSslIssuedToCName, currentSslIssuedToOName, currentSslIssuedToUName, + currentSslIssuedByCName, currentSslIssuedByOName, currentSslIssuedByUName, currentSslStartDateLong, currentSslEndDateLong); + + // Update the pinned SSL certificate class variables to match the information that is now in the database. + MainWebViewActivity.pinnedSslIssuedToCName = currentSslIssuedToCName; + MainWebViewActivity.pinnedSslIssuedToOName = currentSslIssuedToOName; + MainWebViewActivity.pinnedSslIssuedToUName = currentSslIssuedToUName; + MainWebViewActivity.pinnedSslIssuedByCName = currentSslIssuedByCName; + MainWebViewActivity.pinnedSslIssuedByOName = currentSslIssuedByOName; + MainWebViewActivity.pinnedSslIssuedByUName = currentSslIssuedByUName; + MainWebViewActivity.pinnedSslStartDate = currentSslStartDate; + MainWebViewActivity.pinnedSslEndDate = currentSslEndDate; + } + + // Update the IP addresses if they are pinned. + if (pinnedIpAddresses) { + // Update the pinned IP addresses in the domain database. + domainsDatabaseHelper.updatePinnedIpAddresses(MainWebViewActivity.domainSettingsDatabaseId, MainWebViewActivity.currentHostIpAddresses); + + // Update the pinned IP addresses class variable to match the information that is now in the database. + MainWebViewActivity.pinnedHostIpAddresses = MainWebViewActivity.currentHostIpAddresses; + } + }); + + // Setup the negative button. + dialogBuilder.setNegativeButton(R.string.back, (DialogInterface dialog, int which) -> { + // Call the `onSslMismatchBack` public interface to send the `WebView` back one page. + pinnedMismatchListener.onPinnedMismatchBack(); + }); + + // Setup the positive button. + dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> { + // Call the `onSslMismatchProceed` public interface. + pinnedMismatchListener.onPinnedMismatchProceed(); + }); + + // Set the title. + dialogBuilder.setTitle(R.string.pinned_mismatch); + + // Set the layout. The parent view is `null` because it will be assigned by `AlertDialog`. + dialogBuilder.setView(layoutInflater.inflate(R.layout.pinned_mismatch_linearlayout, null)); + + // Create an alert dialog from the alert dialog builder. + final AlertDialog alertDialog = dialogBuilder.create(); + + // Disable screenshots if not allowed. + if (!MainWebViewActivity.allowScreenshots) { + // Remove the warning below that `getWindow()` might be null. + assert alertDialog.getWindow() != null; + + // Disable screenshots. + alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); + } + + // Show the alert dialog so the items in the layout can be modified. + alertDialog.show(); + + // Setup the view pager. + WrapVerticalContentViewPager wrapVerticalContentViewPager = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_viewpager); + wrapVerticalContentViewPager.setAdapter(new pagerAdapter()); + + // Setup the tab layout and connect it to the view pager. + TabLayout tabLayout = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_tablayout); + tabLayout.setupWithViewPager(wrapVerticalContentViewPager); + + // `onCreateDialog()` requires the return of an `AlertDialog`. + return alertDialog; + } + + private class pagerAdapter extends PagerAdapter { + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { + // Check to see if the `View` and the `Object` are the same. + return (view == object); + } + + @Override + public int getCount() { + // There are two tabs. + return 2; + } + + @Override + public CharSequence getPageTitle(int position) { + // Return the current tab title. + if (position == 0) { // The current SSL certificate tab. + return getString(R.string.current); + } else { // The pinned SSL certificate tab. + return getString(R.string.pinned); + } + } + + @Override + @NonNull + public Object instantiateItem(@NonNull ViewGroup container, int position) { + // Inflate the scroll view for this tab. + ViewGroup tabViewGroup = (ViewGroup) layoutInflater.inflate(R.layout.pinned_mismatch_scrollview, container, false); + + // Get handles for the `TextViews`. + TextView domainNameTextView = tabViewGroup.findViewById(R.id.domain_name); + TextView ipAddressesTextView = tabViewGroup.findViewById(R.id.ip_addresses); + TextView issuedToCNameTextView = tabViewGroup.findViewById(R.id.issued_to_cname); + TextView issuedToONameTextView = tabViewGroup.findViewById(R.id.issued_to_oname); + TextView issuedToUNameTextView = tabViewGroup.findViewById(R.id.issued_to_uname); + TextView issuedByCNameTextView = tabViewGroup.findViewById(R.id.issued_by_cname); + TextView issuedByONameTextView = tabViewGroup.findViewById(R.id.issued_by_oname); + TextView issuedByUNameTextView = tabViewGroup.findViewById(R.id.issued_by_uname); + TextView startDateTextView = tabViewGroup.findViewById(R.id.start_date); + TextView endDateTextView = tabViewGroup.findViewById(R.id.end_date); + + // Setup the labels. + String domainNameLabel = getString(R.string.domain_label) + " "; + String ipAddressesLabel = getString(R.string.ip_addresses) + " "; + String cNameLabel = getString(R.string.common_name) + " "; + String oNameLabel = getString(R.string.organization) + " "; + String uNameLabel = getString(R.string.organizational_unit) + " "; + String startDateLabel = getString(R.string.start_date) + " "; + String endDateLabel = getString(R.string.end_date) + " "; + + // Get a URI for the URL. + Uri currentUri = Uri.parse(MainWebViewActivity.formattedUrlString); + + // Get the current host from the URI. + String domainName = currentUri.getHost(); + + // Get the current website SSL certificate. + SslCertificate sslCertificate = MainWebViewActivity.sslCertificate; + + // Extract the individual pieces of information from the current website SSL certificate if it is not null. + if (sslCertificate != null) { + currentSslIssuedToCName = sslCertificate.getIssuedTo().getCName(); + currentSslIssuedToOName = sslCertificate.getIssuedTo().getOName(); + currentSslIssuedToUName = sslCertificate.getIssuedTo().getUName(); + currentSslIssuedByCName = sslCertificate.getIssuedBy().getCName(); + currentSslIssuedByOName = sslCertificate.getIssuedBy().getOName(); + currentSslIssuedByUName = sslCertificate.getIssuedBy().getUName(); + currentSslStartDate = sslCertificate.getValidNotBeforeDate(); + currentSslEndDate = sslCertificate.getValidNotAfterDate(); + } else { + // Initialize the current website SSL certificate variables with blank information. + currentSslIssuedToCName = ""; + currentSslIssuedToOName = ""; + currentSslIssuedToUName = ""; + currentSslIssuedByCName = ""; + currentSslIssuedByOName = ""; + currentSslIssuedByUName = ""; + } + + // Setup the domain name spannable string builder. + SpannableStringBuilder domainNameStringBuilder = new SpannableStringBuilder(domainNameLabel + domainName); + + // Initialize the spannable string builders. + SpannableStringBuilder ipAddressesStringBuilder; + SpannableStringBuilder issuedToCNameStringBuilder; + SpannableStringBuilder issuedToONameStringBuilder; + SpannableStringBuilder issuedToUNameStringBuilder; + SpannableStringBuilder issuedByCNameStringBuilder; + SpannableStringBuilder issuedByONameStringBuilder; + SpannableStringBuilder issuedByUNameStringBuilder; + SpannableStringBuilder startDateStringBuilder; + SpannableStringBuilder endDateStringBuilder; + + // Setup the spannable string builders for each tab. + if (position == 0) { // Setup the current settings tab. + // Create the string builders. + ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + MainWebViewActivity.currentHostIpAddresses); + issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedToCName); + issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedToOName); + issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedToUName); + issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedByCName); + issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedByOName); + issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedByUName); + + // Set the dates if they aren't `null`. + if (currentSslStartDate == null) { + startDateStringBuilder = new SpannableStringBuilder(startDateLabel); + } else { + startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate)); + } + + if (currentSslEndDate == null) { + endDateStringBuilder = new SpannableStringBuilder(endDateLabel); + } else { + endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate)); + } + } else { // Setup the pinned settings tab. + // Create the string builders. + ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + MainWebViewActivity.pinnedHostIpAddresses); + issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + MainWebViewActivity.pinnedSslIssuedToCName); + issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + MainWebViewActivity.pinnedSslIssuedToOName); + issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + MainWebViewActivity.pinnedSslIssuedToUName); + issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + MainWebViewActivity.pinnedSslIssuedByCName); + issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + MainWebViewActivity.pinnedSslIssuedByOName); + issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + MainWebViewActivity.pinnedSslIssuedByUName); + + // Set the dates if they aren't `null`. + if (MainWebViewActivity.pinnedSslStartDate == null) { + startDateStringBuilder = new SpannableStringBuilder(startDateLabel); + } else { + startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG) + .format(MainWebViewActivity.pinnedSslStartDate)); + } + + if (MainWebViewActivity.pinnedSslEndDate == null) { + endDateStringBuilder = new SpannableStringBuilder(endDateLabel); + } else { + endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(MainWebViewActivity.pinnedSslEndDate)); + } + } + + // Create a red foreground color span. The deprecated `getResources().getColor` must be used until the minimum API >= 23. + @SuppressWarnings("deprecation") ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700)); + + // Create a blue foreground color span. + ForegroundColorSpan blueColorSpan; + + // Set the blue color span according to the theme. The deprecated `getResources().getColor` must be used until the minimum API >= 23. + if (MainWebViewActivity.darkTheme) { + //noinspection deprecation + blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400)); + } else { + //noinspection deprecation + blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700)); + } + + // Set the domain name to be blue. + domainNameStringBuilder.setSpan(blueColorSpan, domainNameLabel.length(), domainNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + + // Color coordinate the IP addresses if they are pinned. + if (pinnedIpAddresses) { + if (MainWebViewActivity.currentHostIpAddresses.equals(MainWebViewActivity.pinnedHostIpAddresses)) { + ipAddressesStringBuilder.setSpan(blueColorSpan, ipAddressesLabel.length(), ipAddressesStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { + ipAddressesStringBuilder.setSpan(redColorSpan, ipAddressesLabel.length(), ipAddressesStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + } + + // Color coordinate the SSL certificate fields if they are pinned. + if (pinnedSslCertificate) { + if (currentSslIssuedToCName.equals(MainWebViewActivity.pinnedSslIssuedToCName)) { + issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { + issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + + if (currentSslIssuedToOName.equals(MainWebViewActivity.pinnedSslIssuedToOName)) { + issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { + issuedToONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + + if (currentSslIssuedToUName.equals(MainWebViewActivity.pinnedSslIssuedToUName)) { + issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { + issuedToUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + + if (currentSslIssuedByCName.equals(MainWebViewActivity.pinnedSslIssuedByCName)) { + issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { + issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + + if (currentSslIssuedByOName.equals(MainWebViewActivity.pinnedSslIssuedByOName)) { + issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { + issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + + if (currentSslIssuedByUName.equals(MainWebViewActivity.pinnedSslIssuedByUName)) { + issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { + issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + + if ((currentSslStartDate != null) && currentSslStartDate.equals(MainWebViewActivity.pinnedSslStartDate)) { + startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { + startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + + if ((currentSslEndDate != null) && currentSslEndDate.equals(MainWebViewActivity.pinnedSslEndDate)) { + endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } else { + endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + } + } + + // Display the strings. + domainNameTextView.setText(domainNameStringBuilder); + ipAddressesTextView.setText(ipAddressesStringBuilder); + issuedToCNameTextView.setText(issuedToCNameStringBuilder); + issuedToONameTextView.setText(issuedToONameStringBuilder); + issuedToUNameTextView.setText(issuedToUNameStringBuilder); + issuedByCNameTextView.setText(issuedByCNameStringBuilder); + issuedByONameTextView.setText(issuedByONameStringBuilder); + issuedByUNameTextView.setText(issuedByUNameStringBuilder); + startDateTextView.setText(startDateStringBuilder); + endDateTextView.setText(endDateStringBuilder); + + // Display the tab. + container.addView(tabViewGroup); + + // Make it so. + return tabViewGroup; + } + } +} diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedSslCertificateMismatchDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedSslCertificateMismatchDialog.java deleted file mode 100644 index 7404708b..00000000 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedSslCertificateMismatchDialog.java +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright © 2017-2018 Soren Stoutner . - * - * This file is part of Privacy Browser . - * - * Privacy Browser 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. - * - * Privacy Browser 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 Privacy Browser. If not, see . - */ - -package com.stoutner.privacybrowser.dialogs; - -import android.annotation.SuppressLint; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.net.http.SslCertificate; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.design.widget.TabLayout; -import android.support.v4.view.PagerAdapter; -// `AppCompatDialogFragment` is used instead of `DialogFragment` to avoid an error on API <=22. -import android.support.v7.app.AppCompatDialogFragment; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.style.ForegroundColorSpan; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.TextView; - -import com.stoutner.privacybrowser.R; -import com.stoutner.privacybrowser.activities.MainWebViewActivity; -import com.stoutner.privacybrowser.definitions.WrapVerticalContentViewPager; -import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; - -import java.text.DateFormat; -import java.util.Date; - -public class PinnedSslCertificateMismatchDialog extends AppCompatDialogFragment { - // Instantiate the class variables. - private PinnedSslCertificateMismatchListener pinnedSslCertificateMismatchListener; - private LayoutInflater layoutInflater; - private String currentSslIssuedToCNameString; - private String currentSslIssuedToONameString; - private String currentSslIssuedToUNameString; - private String currentSslIssuedByCNameString; - private String currentSslIssuedByONameString; - private String currentSslIssuedByUNameString; - private Date currentSslStartDate; - private Date currentSslEndDate; - - // The public interface is used to send information back to the parent activity. - public interface PinnedSslCertificateMismatchListener { - void onSslMismatchBack(); - - void onSslMismatchProceed(); - } - - // Check to make sure that the parent activity implements the listener. - public void onAttach(Context context) { - // Run the default commands. - super.onAttach(context); - - // Get a handle for `PinnedSslCertificateMismatchListener` from the launching context. - pinnedSslCertificateMismatchListener = (PinnedSslCertificateMismatchListener) context; - } - - // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`. - @SuppressLint("InflateParams") - @Override - @NonNull - public Dialog onCreateDialog(Bundle savedInstanceState) { - // Remove the incorrect lint warning that `getActivity()` might be null. - assert getActivity() != null; - - // Get the activity's layout inflater. - layoutInflater = getActivity().getLayoutInflater(); - - // Use an alert dialog builder to create the alert dialog. - AlertDialog.Builder dialogBuilder; - - // Set the style according to the theme. - if (MainWebViewActivity.darkTheme) { - // Set the dialog theme. - dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark); - - // Set the icon. - dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_dark); - } else { - // Set the dialog theme. - dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight); - - // Set the icon. - dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_light); - } - - // Setup the neutral button. - dialogBuilder.setNeutralButton(R.string.update_ssl, (DialogInterface dialog, int which) -> { - // Initialize the `long` date variables. If the date is `null`, a long value of `0` will be stored in the Domains database entry. - long currentSslStartDateLong = 0; - long currentSslEndDateLong = 0; - - // Convert the `Dates` into `longs`. - if (currentSslStartDate != null) { - currentSslStartDateLong = currentSslStartDate.getTime(); - } - - if (currentSslEndDate != null) { - currentSslEndDateLong = currentSslEndDate.getTime(); - } - - // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. - DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(getContext(), null, null, 0); - - // Update the pinned SSL certificate for this domain. - domainsDatabaseHelper.updateCertificate(MainWebViewActivity.domainSettingsDatabaseId, currentSslIssuedToCNameString, currentSslIssuedToONameString, currentSslIssuedToUNameString, - currentSslIssuedByCNameString, currentSslIssuedByONameString, currentSslIssuedByUNameString, currentSslStartDateLong, currentSslEndDateLong); - - // Update the pinned SSL certificate global variables to match the information that is now in the database. - MainWebViewActivity.pinnedDomainSslIssuedToCNameString = currentSslIssuedToCNameString; - MainWebViewActivity.pinnedDomainSslIssuedToONameString = currentSslIssuedToONameString; - MainWebViewActivity.pinnedDomainSslIssuedToUNameString = currentSslIssuedToUNameString; - MainWebViewActivity.pinnedDomainSslIssuedByCNameString = currentSslIssuedByCNameString; - MainWebViewActivity.pinnedDomainSslIssuedByONameString = currentSslIssuedByONameString; - MainWebViewActivity.pinnedDomainSslIssuedByUNameString = currentSslIssuedByUNameString; - MainWebViewActivity.pinnedDomainSslStartDate = currentSslStartDate; - MainWebViewActivity.pinnedDomainSslEndDate = currentSslEndDate; - }); - - // Setup the negative button. - dialogBuilder.setNegativeButton(R.string.back, (DialogInterface dialog, int which) -> { - // Call the `onSslMismatchBack` public interface to send the `WebView` back one page. - pinnedSslCertificateMismatchListener.onSslMismatchBack(); - }); - - // Setup the positive button. - dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> { - // Call the `onSslMismatchProceed` public interface. - pinnedSslCertificateMismatchListener.onSslMismatchProceed(); - }); - - // Set the title. - dialogBuilder.setTitle(R.string.ssl_certificate_mismatch); - - // Set the layout. The parent view is `null` because it will be assigned by `AlertDialog`. - dialogBuilder.setView(layoutInflater.inflate(R.layout.pinned_ssl_certificate_mismatch_linearlayout, null)); - - // Create an alert dialog from the alert dialog builder. - final AlertDialog alertDialog = dialogBuilder.create(); - - // Disable screenshots if not allowed. - if (!MainWebViewActivity.allowScreenshots) { - // Remove the warning below that `getWindow()` might be null. - assert alertDialog.getWindow() != null; - - // Disable screenshots. - alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); - } - - // Show the alert dialog so the items in the layout can be modified. - alertDialog.show(); - - // Setup the view pager. - WrapVerticalContentViewPager wrapVerticalContentViewPager = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_viewpager); - wrapVerticalContentViewPager.setAdapter(new pagerAdapter()); - - // Setup the tab layout and connect it to the view pager. - TabLayout tabLayout = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_tablayout); - tabLayout.setupWithViewPager(wrapVerticalContentViewPager); - - // `onCreateDialog()` requires the return of an `AlertDialog`. - return alertDialog; - } - - private class pagerAdapter extends PagerAdapter { - @Override - public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { - // Check to see if the `View` and the `Object` are the same. - return (view == object); - } - - @Override - public int getCount() { - // There are two tabs. - return 2; - } - - @Override - public CharSequence getPageTitle(int position) { - // Return the current tab title. - if (position == 0) { // The current SSL certificate tab. - return getString(R.string.current_ssl); - } else { // The pinned SSL certificate tab. - return getString(R.string.pinned_ssl); - } - } - - @Override - @NonNull - public Object instantiateItem(@NonNull ViewGroup container, int position) { - // Inflate the `ScrollView` for this tab. - ViewGroup tabViewGroup = (ViewGroup) layoutInflater.inflate(R.layout.pinned_ssl_certificate_mismatch_scrollview, container, false); - - // Get handles for the `TextViews`. - TextView issuedToCNameTextView = tabViewGroup.findViewById(R.id.issued_to_cname); - TextView issuedToONameTextView = tabViewGroup.findViewById(R.id.issued_to_oname); - TextView issuedToUNameTextView = tabViewGroup.findViewById(R.id.issued_to_uname); - TextView issuedByCNameTextView = tabViewGroup.findViewById(R.id.issued_by_cname); - TextView issuedByONameTextView = tabViewGroup.findViewById(R.id.issued_by_oname); - TextView issuedByUNameTextView = tabViewGroup.findViewById(R.id.issued_by_uname); - TextView startDateTextView = tabViewGroup.findViewById(R.id.start_date); - TextView endDateTextView = tabViewGroup.findViewById(R.id.end_date); - - // Setup the labels. - String cNameLabel = getString(R.string.common_name) + " "; - String oNameLabel = getString(R.string.organization) + " "; - String uNameLabel = getString(R.string.organizational_unit) + " "; - String startDateLabel = getString(R.string.start_date) + " "; - String endDateLabel = getString(R.string.end_date) + " "; - - // Get the current website SSL certificate. - SslCertificate sslCertificate = MainWebViewActivity.sslCertificate; - - // Extract the individual pieces of information from the current website SSL certificate if it is not null. - if (sslCertificate != null) { - currentSslIssuedToCNameString = sslCertificate.getIssuedTo().getCName(); - currentSslIssuedToONameString = sslCertificate.getIssuedTo().getOName(); - currentSslIssuedToUNameString = sslCertificate.getIssuedTo().getUName(); - currentSslIssuedByCNameString = sslCertificate.getIssuedBy().getCName(); - currentSslIssuedByONameString = sslCertificate.getIssuedBy().getOName(); - currentSslIssuedByUNameString = sslCertificate.getIssuedBy().getUName(); - currentSslStartDate = sslCertificate.getValidNotBeforeDate(); - currentSslEndDate = sslCertificate.getValidNotAfterDate(); - } else { - // Initialize the current website SSL certificate variables with blank information. - currentSslIssuedToCNameString = ""; - currentSslIssuedToONameString = ""; - currentSslIssuedToUNameString = ""; - currentSslIssuedByCNameString = ""; - currentSslIssuedByONameString = ""; - currentSslIssuedByUNameString = ""; - } - - // Initialize the `SpannableStringBuilders`. - SpannableStringBuilder issuedToCNameStringBuilder; - SpannableStringBuilder issuedToONameStringBuilder; - SpannableStringBuilder issuedToUNameStringBuilder; - SpannableStringBuilder issuedByCNameStringBuilder; - SpannableStringBuilder issuedByONameStringBuilder; - SpannableStringBuilder issuedByUNameStringBuilder; - SpannableStringBuilder startDateStringBuilder; - SpannableStringBuilder endDateStringBuilder; - - // Setup the `SpannableStringBuilders` for each tab. - if (position == 0) { // Setup the current SSL certificate tab. - issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedToCNameString); - issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedToONameString); - issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedToUNameString); - issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedByCNameString); - issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedByONameString); - issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedByUNameString); - - // Set the dates if they aren't `null`. - if (currentSslStartDate == null) { - startDateStringBuilder = new SpannableStringBuilder(startDateLabel); - } else { - startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate)); - } - - if (currentSslEndDate == null) { - endDateStringBuilder = new SpannableStringBuilder(endDateLabel); - } else { - endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate)); - } - } else { // Setup the pinned SSL certificate tab. - issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + MainWebViewActivity.pinnedDomainSslIssuedToCNameString); - issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + MainWebViewActivity.pinnedDomainSslIssuedToONameString); - issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + MainWebViewActivity.pinnedDomainSslIssuedToUNameString); - issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + MainWebViewActivity.pinnedDomainSslIssuedByCNameString); - issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + MainWebViewActivity.pinnedDomainSslIssuedByONameString); - issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + MainWebViewActivity.pinnedDomainSslIssuedByUNameString); - - // Set the dates if they aren't `null`. - if (MainWebViewActivity.pinnedDomainSslStartDate == null) { - startDateStringBuilder = new SpannableStringBuilder(startDateLabel); - } else { - startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG) - .format(MainWebViewActivity.pinnedDomainSslStartDate)); - } - - if (MainWebViewActivity.pinnedDomainSslEndDate == null) { - endDateStringBuilder = new SpannableStringBuilder(endDateLabel); - } else { - endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(MainWebViewActivity.pinnedDomainSslEndDate)); - } - } - - // Create a red `ForegroundColorSpan`. We have to use the deprecated `getColor` until API >= 23. - @SuppressWarnings("deprecation") ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700)); - - // Create a blue `ForegroundColorSpan`. - ForegroundColorSpan blueColorSpan; - - // Set `blueColorSpan` according to the theme. We have to use the deprecated `getColor()` until API >= 23. - if (MainWebViewActivity.darkTheme) { - //noinspection deprecation - blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400)); - } else { - //noinspection deprecation - blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700)); - } - - // Configure the spans to display conflicting information in red. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. - if (currentSslIssuedToCNameString.equals(MainWebViewActivity.pinnedDomainSslIssuedToCNameString)) { - issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } else { - issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } - - if (currentSslIssuedToONameString.equals(MainWebViewActivity.pinnedDomainSslIssuedToONameString)) { - issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } else { - issuedToONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } - - if (currentSslIssuedToUNameString.equals(MainWebViewActivity.pinnedDomainSslIssuedToUNameString)) { - issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } else { - issuedToUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } - - if (currentSslIssuedByCNameString.equals(MainWebViewActivity.pinnedDomainSslIssuedByCNameString)) { - issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } else { - issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } - - if (currentSslIssuedByONameString.equals(MainWebViewActivity.pinnedDomainSslIssuedByONameString)) { - issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } else { - issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } - - if (currentSslIssuedByUNameString.equals(MainWebViewActivity.pinnedDomainSslIssuedByUNameString)) { - issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } else { - issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } - - if ((currentSslStartDate != null) && (MainWebViewActivity.pinnedDomainSslStartDate != null) && currentSslStartDate.equals(MainWebViewActivity.pinnedDomainSslStartDate)) { - startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } else { - startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } - - if ((currentSslEndDate != null) && (MainWebViewActivity.pinnedDomainSslEndDate != null) && currentSslEndDate.equals(MainWebViewActivity.pinnedDomainSslEndDate)) { - endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } else { - endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - } - - // Display the strings. - issuedToCNameTextView.setText(issuedToCNameStringBuilder); - issuedToONameTextView.setText(issuedToONameStringBuilder); - issuedToUNameTextView.setText(issuedToUNameStringBuilder); - issuedByCNameTextView.setText(issuedByCNameStringBuilder); - issuedByONameTextView.setText(issuedByONameStringBuilder); - issuedByUNameTextView.setText(issuedByUNameStringBuilder); - startDateTextView.setText(startDateStringBuilder); - endDateTextView.setText(endDateStringBuilder); - - // Display the tab. - container.addView(tabViewGroup); - - // Make it so. - return tabViewGroup; - } - } -} diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.java index 0c930259..76732ff2 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.java @@ -1,5 +1,5 @@ /* - * Copyright © 2016-2018 Soren Stoutner . + * Copyright © 2016-2019 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -20,12 +20,15 @@ package com.stoutner.privacybrowser.dialogs; import android.annotation.SuppressLint; +import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.net.Uri; import android.net.http.SslCertificate; import android.net.http.SslError; +import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; // `AppCompatDialogFragment` is used instead of `DialogFragment` to avoid an error on API <=22. @@ -40,6 +43,9 @@ import android.widget.TextView; import com.stoutner.privacybrowser.R; import com.stoutner.privacybrowser.activities.MainWebViewActivity; +import java.lang.ref.WeakReference; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.text.DateFormat; import java.util.Date; @@ -106,7 +112,7 @@ public class SslCertificateErrorDialog extends AppCompatDialogFragment { // Get the components of the SSL error message from the bundle. int primaryErrorInt = getArguments().getInt("PrimaryErrorInt"); - String urlWithError = getArguments().getString("UrlWithError"); + String urlWithErrors = getArguments().getString("UrlWithError"); String issuedToCName = getArguments().getString("IssuedToCName"); String issuedToOName = getArguments().getString("IssuedToOName"); String issuedToUName = getArguments().getString("IssuedToUName"); @@ -165,22 +171,28 @@ public class SslCertificateErrorDialog extends AppCompatDialogFragment { alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); } - // We have to show the alert dialog before we can modify the content. + // Get a URI for the URL with errors. + Uri uriWithErrors = Uri.parse(urlWithErrors); + + // Get the IP addresses for the URI. + new GetIpAddresses(getActivity(), alertDialog).execute(uriWithErrors.getHost()); + + // The alert dialog must be shown before the contents can be modified. alertDialog.show(); // Get handles for the `TextViews` TextView primaryErrorTextView = alertDialog.findViewById(R.id.primary_error); - TextView urlTextView = alertDialog.findViewById(R.id.url_error_dialog); - TextView issuedToCNameTextView = alertDialog.findViewById(R.id.issued_to_cname_error_dialog); - TextView issuedToONameTextView = alertDialog.findViewById(R.id.issued_to_oname_error_dialog); - TextView issuedToUNameTextView = alertDialog.findViewById(R.id.issued_to_uname_error_dialog); + TextView urlTextView = alertDialog.findViewById(R.id.url); + 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); TextView issuedByTextView = alertDialog.findViewById(R.id.issued_by_textview); - TextView issuedByCNameTextView = alertDialog.findViewById(R.id.issued_by_cname_error_dialog); - TextView issuedByONameTextView = alertDialog.findViewById(R.id.issued_by_oname_error_dialog); - TextView issuedByUNameTextView = alertDialog.findViewById(R.id.issued_by_uname_error_dialog); + TextView issuedByCNameTextView = alertDialog.findViewById(R.id.issued_by_cname); + TextView issuedByONameTextView = alertDialog.findViewById(R.id.issued_by_oname); + TextView issuedByUNameTextView = alertDialog.findViewById(R.id.issued_by_uname); TextView validDatesTextView = alertDialog.findViewById(R.id.valid_dates_textview); - TextView startDateTextView = alertDialog.findViewById(R.id.start_date_error_dialog); - TextView endDateTextView = alertDialog.findViewById(R.id.end_date_error_dialog); + TextView startDateTextView = alertDialog.findViewById(R.id.start_date); + TextView endDateTextView = alertDialog.findViewById(R.id.end_date); // Setup the common strings. String urlLabel = getString(R.string.url_label) + " "; @@ -190,8 +202,8 @@ public class SslCertificateErrorDialog extends AppCompatDialogFragment { String startDateLabel = getString(R.string.start_date) + " "; String endDateLabel = getString(R.string.end_date) + " "; - // Create a `SpannableStringBuilder` for each `TextView` that needs multiple colors of text. - SpannableStringBuilder urlStringBuilder = new SpannableStringBuilder(urlLabel + urlWithError); + // Create a spannable string builder for each text view that needs multiple colors of text. + SpannableStringBuilder urlStringBuilder = new SpannableStringBuilder(urlLabel + urlWithErrors); SpannableStringBuilder issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedToCName); SpannableStringBuilder issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedToOName); SpannableStringBuilder issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedToUName); @@ -201,13 +213,13 @@ public class SslCertificateErrorDialog extends AppCompatDialogFragment { SpannableStringBuilder startDateStringBuilder = new SpannableStringBuilder(startDateLabel + startDate); SpannableStringBuilder endDateStringBuilder = new SpannableStringBuilder((endDateLabel + endDate)); - // Create a red `ForegroundColorSpan`. We have to use the deprecated `getColor` until API >= 23. + // Create a red foreground color span. The deprecated `getResources().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`. ForegroundColorSpan blueColorSpan; - // Set `blueColorSpan` according to the theme. We have to use the deprecated `getColor()` until API >= 23. + // Set a blue color span according to the theme. The deprecated `getResources().getColor` must be used until the minimum API >= 23. if (MainWebViewActivity.darkTheme) { //noinspection deprecation blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400)); @@ -242,7 +254,7 @@ public class SslCertificateErrorDialog extends AppCompatDialogFragment { break; case SslError.SSL_UNTRUSTED: - // Change the `issuesByTextView` text to red. We have to use the deprecated `getColor()` until API >= 23. + // Change the issued by text view text to red. The deprecated `getResources().getColor` must be used until the minimum API >= 23. issuedByTextView.setTextColor(getResources().getColor(R.color.red_a700)); // Change the issued by span color to red. @@ -255,7 +267,7 @@ public class SslCertificateErrorDialog extends AppCompatDialogFragment { break; case SslError.SSL_DATE_INVALID: - // Change the `validDatesTextView` text to red. We have to use the deprecated `getColor()` until API >= 23. + // Change the valid dates text view text to red. The deprecated `getResources().getColor` must be used until the minimum API >= 23. validDatesTextView.setTextColor(getResources().getColor(R.color.red_a700)); // Change the date span colors to red. @@ -301,7 +313,101 @@ public class SslCertificateErrorDialog extends AppCompatDialogFragment { startDateTextView.setText(startDateStringBuilder); endDateTextView.setText(endDateStringBuilder); - // `onCreateDialog` requires the return of an `AlertDialog`. + // `onCreateDialog` requires the return of an alert dialog. return alertDialog; } + + + // This must run asynchronously because it involves a network request. `String` declares the parameters. `Void` does not declare progress units. `SpannableStringBuilder` contains the results. + private static class GetIpAddresses extends AsyncTask { + // The weak references are used to determine if the activity or the alert dialog have disappeared while the AsyncTask is running. + private WeakReference activityWeakReference; + private WeakReference 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); + } + } } diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java index c236d3a8..73ad2ce8 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/ViewSslCertificateDialog.java @@ -20,7 +20,6 @@ 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; @@ -28,7 +27,6 @@ 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; @@ -39,10 +37,6 @@ 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; @@ -52,10 +46,7 @@ import java.util.Date; public class ViewSslCertificateDialog extends DialogFragment { public Dialog onCreateDialog(Bundle savedInstanceState) { // Get the activity's layout inflater. - LayoutInflater layoutInflater = getActivity().getLayoutInflater(); - - // Create a drawable version of the favorite icon. - Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIconBitmap); + LayoutInflater layoutInflater = getActivity().getLayoutInflater(); // Use a builder to create the alert dialog. AlertDialog.Builder dialogBuilder; @@ -67,6 +58,9 @@ public class ViewSslCertificateDialog extends DialogFragment { dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight); } + // Create a drawable version of the favorite icon. + Drawable favoriteIconDrawable = new BitmapDrawable(getResources(), MainWebViewActivity.favoriteIconBitmap); + // Set the icon. dialogBuilder.setIcon(favoriteIconDrawable); @@ -132,6 +126,7 @@ public class ViewSslCertificateDialog extends DialogFragment { // Setup the labels. String domainLabel = getString(R.string.domain_label) + " "; + String ipAddressesLabel = getString(R.string.ip_addresses) + " "; String cNameLabel = getString(R.string.common_name) + " "; String oNameLabel = getString(R.string.organization) + " "; String uNameLabel = getString(R.string.organizational_unit) + " "; @@ -144,9 +139,6 @@ public class ViewSslCertificateDialog extends DialogFragment { // 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; @@ -162,6 +154,7 @@ public class ViewSslCertificateDialog extends DialogFragment { // Create spannable string builders for each text view that needs multiple colors of text. SpannableStringBuilder domainStringBuilder = new SpannableStringBuilder(domainLabel + domainString); + SpannableStringBuilder ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + MainWebViewActivity.currentHostIpAddresses); SpannableStringBuilder issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedToCName); SpannableStringBuilder issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedToOName); SpannableStringBuilder issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedToUName); @@ -189,7 +182,7 @@ public class ViewSslCertificateDialog extends DialogFragment { // Remove the incorrect lint error that `.equals` might produce a NullPointerException. assert domainString != null; - // Formet the `domainString` and `issuedToCName` colors. + // Formet the domain string and issued to CName colors. 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); @@ -231,13 +224,15 @@ public class ViewSslCertificateDialog extends DialogFragment { issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); } - // 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. + // Set the IP addresses, issued to, and issued by spans 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); 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); issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + // Get the current date. Date currentDate = Calendar.getInstance().getTime(); // Format the start date color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. @@ -256,7 +251,7 @@ public class ViewSslCertificateDialog extends DialogFragment { // Display the strings. domainTextView.setText(domainStringBuilder); - ipAddressesTextView.setText(getString(R.string.ip_addresses)); + ipAddressesTextView.setText(ipAddressesStringBuilder); issuedToCNameTextView.setText(issuedToCNameStringBuilder); issuedToONameTextView.setText(issuedToONameStringBuilder); issuedToUNameTextView.setText(issuedToUNameStringBuilder); @@ -266,101 +261,8 @@ public class ViewSslCertificateDialog extends DialogFragment { startDateTextView.setText(startDateStringBuilder); endDateTextView.setText(endDateStringBuilder); - // `onCreateDialog` requires the return of an `AlertDialog`. + // `onCreateDialog` requires the return of an alert dialog. 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 { - // The weak references are used to determine if the activity or the alert dialog have disappeared while the AsyncTask is running. - private WeakReference activityWeakReference; - private WeakReference 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 diff --git a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java index aea9bf3e..614bb374 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java +++ b/app/src/main/java/com/stoutner/privacybrowser/fragments/DomainSettingsFragment.java @@ -79,13 +79,12 @@ public class DomainSettingsFragment extends Fragment { } // The deprecated `getDrawable()` must be used until the minimum API >= 21. - @SuppressWarnings("deprecation") @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate `domain_settings_fragment`. `false` does not attach it to the root `container`. View domainSettingsView = inflater.inflate(R.layout.domain_settings_fragment, container, false); - // Get a handle for the `Context` and the `Resources`. + // Get a handle for the context and the resources. Context context = getContext(); final Resources resources = getResources(); @@ -102,45 +101,45 @@ public class DomainSettingsFragment extends Fragment { // Get handles for the views in the fragment. final EditText domainNameEditText = domainSettingsView.findViewById(R.id.domain_settings_name_edittext); - final Switch javaScriptEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_javascript_switch); - final ImageView javaScriptImageView = domainSettingsView.findViewById(R.id.domain_settings_javascript_imageview); - Switch firstPartyCookiesEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_switch); - final ImageView firstPartyCookiesImageView = domainSettingsView.findViewById(R.id.domain_settings_first_party_cookies_imageview); - LinearLayout thirdPartyCookiesLinearLayout = domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_linearlayout); - final Switch thirdPartyCookiesEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_switch); - final ImageView thirdPartyCookiesImageView = domainSettingsView.findViewById(R.id.domain_settings_third_party_cookies_imageview); - final Switch domStorageEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_dom_storage_switch); - final ImageView domStorageImageView = domainSettingsView.findViewById(R.id.domain_settings_dom_storage_imageview); - Switch formDataEnabledSwitch = domainSettingsView.findViewById(R.id.domain_settings_form_data_switch); // The form data views can be remove once the minimum API >= 26. - final ImageView formDataImageView = domainSettingsView.findViewById(R.id.domain_settings_form_data_imageview); // The form data views can be remove once the minimum API >= 26. - Switch easyListSwitch = domainSettingsView.findViewById(R.id.domain_settings_easylist_switch); - ImageView easyListImageView = domainSettingsView.findViewById(R.id.domain_settings_easylist_imageview); - Switch easyPrivacySwitch = domainSettingsView.findViewById(R.id.domain_settings_easyprivacy_switch); - ImageView easyPrivacyImageView = domainSettingsView.findViewById(R.id.domain_settings_easyprivacy_imageview); - Switch fanboysAnnoyanceListSwitch = domainSettingsView.findViewById(R.id.domain_settings_fanboys_annoyance_list_switch); - ImageView fanboysAnnoyanceListImageView = domainSettingsView.findViewById(R.id.domain_settings_fanboys_annoyance_list_imageview); - Switch fanboysSocialBlockingListSwitch = domainSettingsView.findViewById(R.id.domain_settings_fanboys_social_blocking_list_switch); - ImageView fanboysSocialBlockingListImageView = domainSettingsView.findViewById(R.id.domain_settings_fanboys_social_blocking_list_imageview); - Switch ultraPrivacySwitch = domainSettingsView.findViewById(R.id.domain_settings_ultraprivacy_switch); - ImageView ultraPrivacyImageView = domainSettingsView.findViewById(R.id.domain_settings_ultraprivacy_imageview); - Switch blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById(R.id.domain_settings_block_all_third_party_requests_switch); - ImageView blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById(R.id.domain_settings_block_all_third_party_requests_imageview); - final Spinner userAgentSpinner = domainSettingsView.findViewById(R.id.domain_settings_user_agent_spinner); - final TextView userAgentTextView = domainSettingsView.findViewById(R.id.domain_settings_user_agent_textview); - final EditText customUserAgentEditText = domainSettingsView.findViewById(R.id.domain_settings_custom_user_agent_edittext); - final Spinner fontSizeSpinner = domainSettingsView.findViewById(R.id.domain_settings_font_size_spinner); - final TextView fontSizeTextView = domainSettingsView.findViewById(R.id.domain_settings_font_size_textview); - final ImageView swipeToRefreshImageView = domainSettingsView.findViewById(R.id.domain_settings_swipe_to_refresh_imageview); - final Spinner swipeToRefreshSpinner = domainSettingsView.findViewById(R.id.domain_settings_swipe_to_refresh_spinner); - final TextView swipeToRefreshTextView = domainSettingsView.findViewById(R.id.domain_settings_swipe_to_refresh_textview); - final ImageView nightModeImageView = domainSettingsView.findViewById(R.id.domain_settings_night_mode_imageview); - final Spinner nightModeSpinner = domainSettingsView.findViewById(R.id.domain_settings_night_mode_spinner); - final TextView nightModeTextView = domainSettingsView.findViewById(R.id.domain_settings_night_mode_textview); - final ImageView displayWebpageImagesImageView = domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_imageview); - final Spinner displayWebpageImagesSpinner = domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_spinner); - final TextView displayImagesTextView = domainSettingsView.findViewById(R.id.domain_settings_display_webpage_images_textview); - final ImageView pinnedSslCertificateImageView = domainSettingsView.findViewById(R.id.domain_settings_pinned_ssl_certificate_imageview); - Switch pinnedSslCertificateSwitch = domainSettingsView.findViewById(R.id.domain_settings_pinned_ssl_certificate_switch); + final Switch javaScriptEnabledSwitch = domainSettingsView.findViewById(R.id.javascript_switch); + final ImageView javaScriptImageView = domainSettingsView.findViewById(R.id.javascript_imageview); + Switch firstPartyCookiesEnabledSwitch = domainSettingsView.findViewById(R.id.first_party_cookies_switch); + final ImageView firstPartyCookiesImageView = domainSettingsView.findViewById(R.id.first_party_cookies_imageview); + LinearLayout thirdPartyCookiesLinearLayout = domainSettingsView.findViewById(R.id.third_party_cookies_linearlayout); + final Switch thirdPartyCookiesEnabledSwitch = domainSettingsView.findViewById(R.id.third_party_cookies_switch); + final ImageView thirdPartyCookiesImageView = domainSettingsView.findViewById(R.id.third_party_cookies_imageview); + final Switch domStorageEnabledSwitch = domainSettingsView.findViewById(R.id.dom_storage_switch); + final ImageView domStorageImageView = domainSettingsView.findViewById(R.id.dom_storage_imageview); + Switch formDataEnabledSwitch = domainSettingsView.findViewById(R.id.form_data_switch); // The form data views can be remove once the minimum API >= 26. + final ImageView formDataImageView = domainSettingsView.findViewById(R.id.form_data_imageview); // The form data views can be remove once the minimum API >= 26. + Switch easyListSwitch = domainSettingsView.findViewById(R.id.easylist_switch); + ImageView easyListImageView = domainSettingsView.findViewById(R.id.easylist_imageview); + Switch easyPrivacySwitch = domainSettingsView.findViewById(R.id.easyprivacy_switch); + ImageView easyPrivacyImageView = domainSettingsView.findViewById(R.id.easyprivacy_imageview); + Switch fanboysAnnoyanceListSwitch = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_switch); + ImageView fanboysAnnoyanceListImageView = domainSettingsView.findViewById(R.id.fanboys_annoyance_list_imageview); + Switch fanboysSocialBlockingListSwitch = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_switch); + ImageView fanboysSocialBlockingListImageView = domainSettingsView.findViewById(R.id.fanboys_social_blocking_list_imageview); + Switch ultraPrivacySwitch = domainSettingsView.findViewById(R.id.ultraprivacy_switch); + ImageView ultraPrivacyImageView = domainSettingsView.findViewById(R.id.ultraprivacy_imageview); + Switch blockAllThirdPartyRequestsSwitch = domainSettingsView.findViewById(R.id.block_all_third_party_requests_switch); + ImageView blockAllThirdPartyRequestsImageView = domainSettingsView.findViewById(R.id.block_all_third_party_requests_imageview); + final Spinner userAgentSpinner = domainSettingsView.findViewById(R.id.user_agent_spinner); + final TextView userAgentTextView = domainSettingsView.findViewById(R.id.user_agent_textview); + final EditText customUserAgentEditText = domainSettingsView.findViewById(R.id.custom_user_agent_edittext); + final Spinner fontSizeSpinner = domainSettingsView.findViewById(R.id.font_size_spinner); + final TextView fontSizeTextView = domainSettingsView.findViewById(R.id.font_size_textview); + final ImageView swipeToRefreshImageView = domainSettingsView.findViewById(R.id.swipe_to_refresh_imageview); + final Spinner swipeToRefreshSpinner = domainSettingsView.findViewById(R.id.swipe_to_refresh_spinner); + final TextView swipeToRefreshTextView = domainSettingsView.findViewById(R.id.swipe_to_refresh_textview); + final ImageView nightModeImageView = domainSettingsView.findViewById(R.id.night_mode_imageview); + final Spinner nightModeSpinner = domainSettingsView.findViewById(R.id.night_mode_spinner); + final TextView nightModeTextView = domainSettingsView.findViewById(R.id.night_mode_textview); + final ImageView displayWebpageImagesImageView = domainSettingsView.findViewById(R.id.display_webpage_images_imageview); + final Spinner displayWebpageImagesSpinner = domainSettingsView.findViewById(R.id.display_webpage_images_spinner); + final TextView displayImagesTextView = domainSettingsView.findViewById(R.id.display_webpage_images_textview); + final ImageView pinnedSslCertificateImageView = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_imageview); + Switch pinnedSslCertificateSwitch = domainSettingsView.findViewById(R.id.pinned_ssl_certificate_switch); final CardView savedSslCertificateCardView = domainSettingsView.findViewById(R.id.saved_ssl_certificate_cardview); LinearLayout savedSslCertificateLinearLayout = domainSettingsView.findViewById(R.id.saved_ssl_certificate_linearlayout); final RadioButton savedSslCertificateRadioButton = domainSettingsView.findViewById(R.id.saved_ssl_certificate_radiobutton); @@ -164,9 +163,19 @@ public class DomainSettingsFragment extends Fragment { TextView currentWebsiteCertificateStartDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_start_date); TextView currentWebsiteCertificateEndDateTextView = domainSettingsView.findViewById(R.id.current_website_certificate_end_date); final TextView noCurrentWebsiteCertificateTextView = domainSettingsView.findViewById(R.id.no_current_website_certificate); - - // Setup the SSL certificate labels. - final String cNameLabel = getString(R.string.common_name) + " "; + ImageView pinnedIpAddressesImageView = domainSettingsView.findViewById(R.id.pinned_ip_addresses_imageview); + Switch pinnedIpAddressesSwitch = domainSettingsView.findViewById(R.id.pinned_ip_addresses_switch); + CardView savedIpAddressesCardView = domainSettingsView.findViewById(R.id.saved_ip_addresses_cardview); + LinearLayout savedIpAddressesLinearLayout = domainSettingsView.findViewById(R.id.saved_ip_addresses_linearlayout); + RadioButton savedIpAddressesRadioButton = domainSettingsView.findViewById(R.id.saved_ip_addresses_radiobutton); + TextView savedIpAddressesTextView = domainSettingsView.findViewById(R.id.saved_ip_addresses_textview); + CardView currentIpAddressesCardView = domainSettingsView.findViewById(R.id.current_ip_addresses_cardview); + LinearLayout currentIpAddressesLinearLayout = domainSettingsView.findViewById(R.id.current_ip_addresses_linearlayout); + RadioButton currentIpAddressesRadioButton = domainSettingsView.findViewById(R.id.current_ip_addresses_radiobutton); + TextView currentIpAddressesTextView = domainSettingsView.findViewById(R.id.current_ip_addresses_textview); + + // Setup the pinned labels. + String cNameLabel = getString(R.string.common_name) + " "; String oNameLabel = getString(R.string.organization) + " "; String uNameLabel = getString(R.string.organizational_unit) + " "; String startDateLabel = getString(R.string.start_date) + " "; @@ -178,11 +187,11 @@ public class DomainSettingsFragment extends Fragment { // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`. DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(context, null, null, 0); - // Get the database `Cursor` for this ID and move it to the first row. + // Get the database cursor for this ID and move it to the first row. Cursor domainCursor = domainsDatabaseHelper.getCursorForId(databaseId); domainCursor.moveToFirst(); - // Save the `Cursor` entries as variables. + // Save the cursor entries as variables. String domainNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.DOMAIN_NAME)); final int javaScriptEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)); int firstPartyCookiesEnabledInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)); @@ -201,12 +210,14 @@ public class DomainSettingsFragment extends Fragment { int nightModeInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.NIGHT_MODE)); int displayImagesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.DISPLAY_IMAGES)); int pinnedSslCertificateInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_SSL_CERTIFICATE)); - final String savedSslCertificateIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)); + String savedSslCertificateIssuedToCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_COMMON_NAME)); String savedSslCertificateIssuedToONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATION)); String savedSslCertificateIssuedToUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_TO_ORGANIZATIONAL_UNIT)); String savedSslCertificateIssuedByCNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_COMMON_NAME)); String savedSslCertificateIssuedByONameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATION)); String savedSslCertificateIssuedByUNameString = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT)); + int pinnedIpAddressesInt = domainCursor.getInt(domainCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES)); + String savedIpAddresses = domainCursor.getString(domainCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES)); // Initialize the saved SSL certificate date variables. Date savedSslCertificateStartDate = null; @@ -251,7 +262,7 @@ public class DomainSettingsFragment extends Fragment { SpannableStringBuilder savedSslCertificateIssuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + savedSslCertificateIssuedByONameString); SpannableStringBuilder savedSslCertificateIssuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + savedSslCertificateIssuedByUNameString); - // Initialize the `SpannableStringBuilders` for the SSL certificate dates. + // Initialize the spannable string builders for the SSL certificate dates. SpannableStringBuilder savedSslCertificateStartDateStringBuilder; SpannableStringBuilder savedSslCertificateEndDateStringBuilder; @@ -268,19 +279,19 @@ public class DomainSettingsFragment extends Fragment { savedSslCertificateEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(savedSslCertificateEndDate)); } - // Create a red `ForegroundColorSpan`. We have to use the deprecated `getColor` until API >= 23. - final ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700)); + // Create a red foreground color span. The deprecated `resources.getColor` must be used until the minimum API >= 23. + final ForegroundColorSpan redColorSpan = new ForegroundColorSpan(resources.getColor(R.color.red_a700)); - // Create a blue `ForegroundColorSpan`. + // Create a blue foreground color span. final 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 `resources.getColor` must be used until the minimum API >= 23. if (MainWebViewActivity.darkTheme) { //noinspection deprecation - blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400)); + blueColorSpan = new ForegroundColorSpan(resources.getColor(R.color.blue_400)); } else { //noinspection deprecation - blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700)); + blueColorSpan = new ForegroundColorSpan(resources.getColor(R.color.blue_700)); } // Set the domain name from the the database cursor. @@ -343,7 +354,7 @@ public class DomainSettingsFragment extends Fragment { } }); - // Create a `boolean` to track if night mode is enabled. + // Create a boolean to track if night mode is enabled. boolean nightModeEnabled = (nightModeInt == DomainsDatabaseHelper.NIGHT_MODE_ENABLED) || ((nightModeInt == DomainsDatabaseHelper.NIGHT_MODE_SYSTEM_DEFAULT) && defaultNightMode); // Disable the JavaScript switch if night mode is enabled. @@ -960,17 +971,17 @@ public class DomainSettingsFragment extends Fragment { // Store the current date. Date currentDate = Calendar.getInstance().getTime(); - // Setup the `StringBuilders` to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. + // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. savedSslCertificateIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslCertificateIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); savedSslCertificateIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslCertificateIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); savedSslCertificateIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCertificateIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); savedSslCertificateIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), savedSslCertificateIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); savedSslCertificateIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), savedSslCertificateIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - // Check the certificate `Common Name` against the domain name. + // Check the certificate Common Name against the domain name. boolean savedSSlCertificateCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, savedSslCertificateIssuedToCNameString); - // Format the `issuedToCommonName` color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. + // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. if (savedSSlCertificateCommonNameMatchesDomainName) { savedSslCertificateIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), savedSslCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); } else { @@ -991,7 +1002,7 @@ public class DomainSettingsFragment extends Fragment { savedSslCertificateEndDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), savedSslCertificateEndDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); } - // Display the current website SSL certificate strings. + // Display the saved website SSL certificate strings. savedSslCertificateIssuedToCNameTextView.setText(savedSslCertificateIssuedToCNameStringBuilder); savedSslCertificateIssuedToONameTextView.setText(savedSslCertificateIssuedToONameStringBuilder); savedSslCertificateIssuedToUNameTextView.setText(savedSslCertificateIssuedToUNameStringBuilder); @@ -1013,7 +1024,7 @@ public class DomainSettingsFragment extends Fragment { Date currentWebsiteCertificateStartDate = currentWebsiteSslCertificate.getValidNotBeforeDate(); Date currentWebsiteCertificateEndDate = currentWebsiteSslCertificate.getValidNotAfterDate(); - // Create a `SpannableStringBuilder` for each `TextView` that needs multiple colors of text. + // Create a spannable string builder for each text view that needs multiple colors of text. SpannableStringBuilder currentWebsiteCertificateIssuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentWebsiteCertificateIssuedToCNameString); SpannableStringBuilder currentWebsiteCertificateIssuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentWebsiteCertificateIssuedToONameString); SpannableStringBuilder currentWebsiteCertificateIssuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentWebsiteCertificateIssuedToUNameString); @@ -1025,17 +1036,17 @@ public class DomainSettingsFragment extends Fragment { SpannableStringBuilder currentWebsiteCertificateEndDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG) .format(currentWebsiteCertificateEndDate)); - // Setup the `StringBuilders` to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. + // Setup the string builders to display the general certificate information in blue. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. currentWebsiteCertificateIssuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentWebsiteCertificateIssuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); currentWebsiteCertificateIssuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentWebsiteCertificateIssuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); currentWebsiteCertificateIssuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentWebsiteCertificateIssuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); currentWebsiteCertificateIssuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), currentWebsiteCertificateIssuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); currentWebsiteCertificateIssuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), currentWebsiteCertificateIssuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - // Check the certificate `Common Name` against the domain name. + // Check the certificate Common Name against the domain name. boolean currentWebsiteCertificateCommonNameMatchesDomainName = checkDomainNameAgainstCertificate(domainNameString, currentWebsiteCertificateIssuedToCNameString); - // Format the `issuedToCommonName` color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. + // Format the issued to Common Name color. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction. if (currentWebsiteCertificateCommonNameMatchesDomainName) { currentWebsiteCertificateIssuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), currentWebsiteCertificateIssuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); } else { @@ -1067,8 +1078,8 @@ public class DomainSettingsFragment extends Fragment { currentWebsiteCertificateEndDateTextView.setText(currentWebsiteCertificateEndDateStringBuilder); } - // Set the initial display status for the SSL certificates. - if (pinnedSslCertificateSwitch.isChecked()) { + // Set the initial display status of the SSL certificates card views. + if (pinnedSslCertificateSwitch.isChecked()) { // An SSL certificate is pinned. // Set the visibility of the saved SSL certificate. if (savedSslCertificateIssuedToCNameString == null) { savedSslCertificateCardView.setVisibility(View.GONE); @@ -1077,13 +1088,13 @@ public class DomainSettingsFragment extends Fragment { } // Set the visibility of the current website SSL certificate. - if (currentWebsiteSslCertificate == null) { + if (currentWebsiteSslCertificate == null) { // There is no current SSL certificate. // Hide the SSL certificate. currentWebsiteCertificateCardView.setVisibility(View.GONE); // Show the instruction. noCurrentWebsiteCertificateTextView.setVisibility(View.VISIBLE); - } else { + } else { // There is a current SSL certificate. // Show the SSL certificate. currentWebsiteCertificateCardView.setVisibility(View.VISIBLE); @@ -1091,7 +1102,7 @@ public class DomainSettingsFragment extends Fragment { noCurrentWebsiteCertificateTextView.setVisibility(View.GONE); } - // Set the status of the radio buttons. + // Set the status of the radio buttons and the card view backgrounds. if (savedSslCertificateCardView.getVisibility() == View.VISIBLE) { // The saved SSL certificate is displayed. // Check the saved SSL certificate radio button. savedSslCertificateRadioButton.setChecked(true); @@ -1111,19 +1122,12 @@ public class DomainSettingsFragment extends Fragment { // Uncheck the saved SSL certificate radio button. savedSslCertificateRadioButton.setChecked(false); - - // Darken the background of the saved SSL certificate linear layout according to the theme. - if (MainWebViewActivity.darkTheme) { - savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_33); - } else { - savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11); - } } else { // Neither SSL certificate is visible. // Uncheck both radio buttons. savedSslCertificateRadioButton.setChecked(false); currentWebsiteCertificateRadioButton.setChecked(false); } - } else { // `pinnedSslCertificateSwitch` is not checked. + } else { // An SSL certificate is not pinned. // Hide the SSl certificates and instructions. savedSslCertificateCardView.setVisibility(View.GONE); currentWebsiteCertificateCardView.setVisibility(View.GONE); @@ -1134,6 +1138,76 @@ public class DomainSettingsFragment extends Fragment { currentWebsiteCertificateRadioButton.setChecked(false); } + // Set the pinned IP addresses icon. + if (pinnedIpAddressesInt == 1) { // Pinned IP addresses is enabled. Once the minimum API >= 21 a selector can be sued as the tint mode instead of specifying different icons. + // Check the switch. + pinnedIpAddressesSwitch.setChecked(true); + + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark)); + } else { + pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light)); + } + } else { // Pinned IP Addresses is disabled. + // Uncheck the switch. + pinnedIpAddressesSwitch.setChecked(false); + + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark)); + } else { + pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light)); + } + } + + // Populate the saved and current IP addresses. + savedIpAddressesTextView.setText(savedIpAddresses); + currentIpAddressesTextView.setText(MainWebViewActivity.currentHostIpAddresses); + + // Set the initial display status of the IP addresses card views. + if (pinnedIpAddressesSwitch.isChecked()) { // IP addresses are pinned. + // Set the visibility of the saved IP addresses. + if (savedIpAddresses == null) { // There are no saved IP addresses. + savedIpAddressesCardView.setVisibility(View.GONE); + } else { // There are saved IP addresses. + savedIpAddressesCardView.setVisibility(View.VISIBLE); + } + + // Set the visibility of the current IP addresses. + currentIpAddressesCardView.setVisibility(View.VISIBLE); + + // Set the status of the radio buttons and the card view backgrounds. + if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) { // The saved IP addresses are displayed. + // Check the saved IP addresses radio button. + savedIpAddressesRadioButton.setChecked(true); + + // Uncheck the current IP addresses radio button. + currentIpAddressesRadioButton.setChecked(false); + + // Darken the background of the current IP addresses linear layout according to the theme. + if (MainWebViewActivity.darkTheme) { + currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33); + } else { + currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11); + } + } else { // The saved IP addresses are hidden. + // Check the current IP addresses radio button. + currentIpAddressesRadioButton.setChecked(true); + + // Uncheck the saved IP addresses radio button. + savedIpAddressesRadioButton.setChecked(false); + } + } else { // IP addresses are not pinned. + // Hide the IP addresses card views. + savedIpAddressesCardView.setVisibility(View.GONE); + currentIpAddressesCardView.setVisibility(View.GONE); + + // Uncheck the radio buttons. + savedIpAddressesRadioButton.setChecked(false); + currentIpAddressesRadioButton.setChecked(false); + } + // Set the JavaScript switch listener. javaScriptEnabledSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> { @@ -1731,8 +1805,8 @@ public class DomainSettingsFragment extends Fragment { // Set the pinned SSL certificate switch listener. pinnedSslCertificateSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> { - // Update the icon - if (isChecked) { // Pinned SSL certificate is enabled. + // Update the icon. + if (isChecked) { // SSL certificate pinning is enabled. // Set the icon according to the theme. if (MainWebViewActivity.darkTheme) { pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark)); @@ -1779,6 +1853,9 @@ public class DomainSettingsFragment extends Fragment { } else { currentWebsiteCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11); } + + // Scroll to the current website SSL certificate card. + savedSslCertificateCardView.getParent().requestChildFocus(savedSslCertificateCardView, savedSslCertificateCardView); } else if (currentWebsiteCertificateCardView.getVisibility() == View.VISIBLE) { // The saved SSL certificate is hidden but the current website SSL certificate is visible. // Check the current website SSL certificate radio button. currentWebsiteCertificateRadioButton.setChecked(true); @@ -1795,12 +1872,18 @@ public class DomainSettingsFragment extends Fragment { } else { savedSslCertificateLinearLayout.setBackgroundResource(R.color.black_translucent_11); } + + // Scroll to the current website SSL certificate card. + currentWebsiteCertificateCardView.getParent().requestChildFocus(currentWebsiteCertificateCardView, currentWebsiteCertificateCardView); } else { // Neither SSL certificate is visible. // Uncheck both radio buttons. savedSslCertificateRadioButton.setChecked(false); currentWebsiteCertificateRadioButton.setChecked(false); + + // Scroll to the current website SSL certificate card. + noCurrentWebsiteCertificateTextView.getParent().requestChildFocus(noCurrentWebsiteCertificateTextView, noCurrentWebsiteCertificateTextView); } - } else { // Pinned SSL certificate is disabled. + } else { // SSL certificate pinning is disabled. // Set the icon according to the theme. if (MainWebViewActivity.darkTheme) { pinnedSslCertificateImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark)); @@ -1819,7 +1902,7 @@ public class DomainSettingsFragment extends Fragment { } }); - savedSslCertificateCardView.setOnClickListener((View v) -> { + savedSslCertificateCardView.setOnClickListener((View view) -> { // Check the saved SSL certificate radio button. savedSslCertificateRadioButton.setChecked(true); @@ -1837,7 +1920,7 @@ public class DomainSettingsFragment extends Fragment { } }); - savedSslCertificateRadioButton.setOnClickListener((View v) -> { + savedSslCertificateRadioButton.setOnClickListener((View view) -> { // Check the saved SSL certificate radio button. savedSslCertificateRadioButton.setChecked(true); @@ -1855,7 +1938,7 @@ public class DomainSettingsFragment extends Fragment { } }); - currentWebsiteCertificateCardView.setOnClickListener((View v) -> { + currentWebsiteCertificateCardView.setOnClickListener((View view) -> { // Check the current website SSL certificate radio button. currentWebsiteCertificateRadioButton.setChecked(true); @@ -1873,7 +1956,7 @@ public class DomainSettingsFragment extends Fragment { } }); - currentWebsiteCertificateRadioButton.setOnClickListener((View v) -> { + currentWebsiteCertificateRadioButton.setOnClickListener((View view) -> { // Check the current website SSL certificate radio button. currentWebsiteCertificateRadioButton.setChecked(true); @@ -1891,6 +1974,154 @@ public class DomainSettingsFragment extends Fragment { } }); + // Set the pinned IP addresses switch listener. + pinnedIpAddressesSwitch.setOnCheckedChangeListener((CompoundButton buttonView, boolean isChecked) -> { + // Update the icon. + if (isChecked) { // IP addresses pinning is enabled. + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_dark)); + } else { + pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_enabled_light)); + } + + // Update the visibility of the saved IP addresses card view. + if (savedIpAddresses == null) { // There are no saved IP addresses. + savedIpAddressesCardView.setVisibility(View.GONE); + } else { // There are saved IP addresses. + savedIpAddressesCardView.setVisibility(View.VISIBLE); + } + + // Show the current IP addresses card view. + currentIpAddressesCardView.setVisibility(View.VISIBLE); + + // Set the status of the radio buttons. + if (savedIpAddressesCardView.getVisibility() == View.VISIBLE) { // The saved IP addresses are visible. + // Check the saved IP addresses radio button. + savedIpAddressesRadioButton.setChecked(true); + + // Uncheck the current IP addresses radio button. + currentIpAddressesRadioButton.setChecked(false); + + // Set the background of the saved IP addresses linear layout to be transparent. + savedSslCertificateLinearLayout.setBackgroundResource(R.color.transparent); + + // Darken the background of the current IP addresses linear layout according to the theme. + if (MainWebViewActivity.darkTheme) { + currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33); + } else { + currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11); + } + } else { // The saved IP addresses are not visible. + // Check the current IP addresses radio button. + currentIpAddressesRadioButton.setChecked(true); + + // Uncheck the saved IP addresses radio button. + savedIpAddressesRadioButton.setChecked(false); + + // Set the background of the current IP addresses linear layout to be transparent. + currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent); + + // Darken the background of the saved IP addresses linear layout according to the theme. + if (MainWebViewActivity.darkTheme) { + savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33); + } else { + savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11); + } + } + + // Scroll to the bottom of the card views. + currentIpAddressesCardView.getParent().requestChildFocus(currentIpAddressesCardView, currentIpAddressesCardView); + } else { // IP addresses pinning is disabled. + // Set the icon according to the theme. + if (MainWebViewActivity.darkTheme) { + pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_dark)); + } else { + pinnedIpAddressesImageView.setImageDrawable(resources.getDrawable(R.drawable.ssl_certificate_disabled_light)); + } + + // Hide the IP addresses card views. + savedIpAddressesCardView.setVisibility(View.GONE); + currentIpAddressesCardView.setVisibility(View.GONE); + + // Uncheck the radio buttons. + savedIpAddressesRadioButton.setChecked(false); + currentIpAddressesRadioButton.setChecked(false); + } + }); + + savedIpAddressesCardView.setOnClickListener((View view) -> { + // Check the saved IP addresses radio button. + savedIpAddressesRadioButton.setChecked(true); + + // Uncheck the current website IP addresses radio button. + currentIpAddressesRadioButton.setChecked(false); + + // Set the background of the saved IP addresses linear layout to be transparent. + savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent); + + // Darken the background of the current IP addresses linear layout according to the theme. + if (MainWebViewActivity.darkTheme) { + currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33); + } else { + currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11); + } + }); + + savedIpAddressesRadioButton.setOnClickListener((View view) -> { + // Check the saved IP addresses radio button. + savedIpAddressesRadioButton.setChecked(true); + + // Uncheck the current website IP addresses radio button. + currentIpAddressesRadioButton.setChecked(false); + + // Set the background of the saved IP addresses linear layout to be transparent. + savedIpAddressesLinearLayout.setBackgroundResource(R.color.transparent); + + // Darken the background of the current IP addresses linear layout according to the theme. + if (MainWebViewActivity.darkTheme) { + currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33); + } else { + currentIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11); + } + }); + + currentIpAddressesCardView.setOnClickListener((View view) -> { + // Check the current IP addresses radio button. + currentIpAddressesRadioButton.setChecked(true); + + // Uncheck the saved IP addresses radio button. + savedIpAddressesRadioButton.setChecked(false); + + // Set the background of the current IP addresses linear layout to be transparent. + currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent); + + // Darken the background of the saved IP addresses linear layout according to the theme. + if (MainWebViewActivity.darkTheme) { + savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33); + } else { + savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11); + } + }); + + currentIpAddressesRadioButton.setOnClickListener((View view) -> { + // Check the current IP addresses radio button. + currentIpAddressesRadioButton.setChecked(true); + + // Uncheck the saved IP addresses radio button. + savedIpAddressesRadioButton.setChecked(false); + + // Set the background of the current IP addresses linear layout to be transparent. + currentIpAddressesLinearLayout.setBackgroundResource(R.color.transparent); + + // Darken the background of the saved IP addresses linear layout according to the theme. + if (MainWebViewActivity.darkTheme) { + savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_33); + } else { + savedIpAddressesLinearLayout.setBackgroundResource(R.color.black_translucent_11); + } + }); + return domainSettingsView; } diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java index 9494d991..b74ca52b 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/DomainsDatabaseHelper.java @@ -1,5 +1,5 @@ /* - * Copyright © 2017-2018 Soren Stoutner . + * Copyright © 2017-2019 Soren Stoutner . * * This file is part of Privacy Browser . * @@ -28,7 +28,7 @@ import android.database.sqlite.SQLiteOpenHelper; import android.preference.PreferenceManager; public class DomainsDatabaseHelper extends SQLiteOpenHelper { - private static final int SCHEMA_VERSION = 8; + private static final int SCHEMA_VERSION = 9; static final String DOMAINS_DATABASE = "domains.db"; static final String DOMAINS_TABLE = "domains"; @@ -59,6 +59,8 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { public static final String SSL_ISSUED_BY_ORGANIZATIONAL_UNIT = "sslissuedbyorganizationalunit"; public static final String SSL_START_DATE = "sslstartdate"; public static final String SSL_END_DATE = "sslenddate"; + public static final String PINNED_IP_ADDRESSES = "pinned_ip_addresses"; + public static final String IP_ADDRESSES = "ip_addresses"; // Swipe to refresh constants. public static final int SWIPE_TO_REFRESH_SYSTEM_DEFAULT = 0; @@ -102,7 +104,9 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { SSL_ISSUED_BY_ORGANIZATION + " TEXT, " + SSL_ISSUED_BY_ORGANIZATIONAL_UNIT + " TEXT, " + SSL_START_DATE + " INTEGER, " + - SSL_END_DATE + " INTEGER)"; + SSL_END_DATE + " INTEGER, " + + PINNED_IP_ADDRESSES + " BOOLEAN, " + + IP_ADDRESSES + " TEXT)"; private final Context appContext; @@ -209,6 +213,12 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { // Enable it for all existing rows. domainsDatabase.execSQL("UPDATE " + DOMAINS_TABLE + " SET " + ENABLE_ULTRAPRIVACY + " = " + 1); + + // Upgrade from schema version 8. + case 8: + // Add the Pinned IP Addresses columns. + domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + PINNED_IP_ADDRESSES + " BOOLEAN"); + domainsDatabase.execSQL("ALTER TABLE " + DOMAINS_TABLE + " ADD COLUMN " + IP_ADDRESSES + " TEXT"); } } @@ -331,10 +341,9 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { domainsDatabase.close(); } - public void updateDomainExceptCertificate(int databaseId, String domainName, boolean javaScriptEnabled, boolean firstPartyCookiesEnabled, boolean thirdPartyCookiesEnabled, boolean domStorageEnabled, - boolean formDataEnabled, boolean easyListEnabled, boolean easyPrivacyEnabled, boolean fanboysAnnoyanceEnabled, boolean fanboysSocialBlockingEnabled, - boolean ultraPrivacyEnabled, boolean blockAllThirdPartyRequests, String userAgent, int fontSize, int swipeToRefresh, int nightMode, int displayImages, - boolean pinnedSslCertificate) { + public void updateDomain(int databaseId, String domainName, boolean javaScriptEnabled, boolean firstPartyCookiesEnabled, boolean thirdPartyCookiesEnabled, boolean domStorageEnabled, boolean formDataEnabled, + boolean easyListEnabled, boolean easyPrivacyEnabled, boolean fanboysAnnoyanceEnabled, boolean fanboysSocialBlockingEnabled, boolean ultraPrivacyEnabled, + boolean blockAllThirdPartyRequests, String userAgent, int fontSize, int swipeToRefresh, int nightMode, int displayImages, boolean pinnedSslCertificate, boolean pinnedIpAddresses) { // Store the domain data in a `ContentValues`. ContentValues domainContentValues = new ContentValues(); @@ -358,6 +367,7 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { domainContentValues.put(NIGHT_MODE, nightMode); domainContentValues.put(DISPLAY_IMAGES, displayImages); domainContentValues.put(PINNED_SSL_CERTIFICATE, pinnedSslCertificate); + domainContentValues.put(PINNED_IP_ADDRESSES, pinnedIpAddresses); // Get a writable database handle. SQLiteDatabase domainsDatabase = this.getWritableDatabase(); @@ -369,73 +379,44 @@ public class DomainsDatabaseHelper extends SQLiteOpenHelper { domainsDatabase.close(); } - public void updateDomainWithCertificate(int databaseId, String domainName, boolean javaScriptEnabled, boolean firstPartyCookiesEnabled, boolean thirdPartyCookiesEnabled, boolean domStorageEnabled, - boolean formDataEnabled, boolean easyListEnabled, boolean easyPrivacyEnabled, boolean fanboysAnnoyanceEnabled, boolean fanboysSocialBlockingEnabled, - boolean ultraPrivacyEnabled, boolean blockAllThirdPartyRequests, String userAgent, int fontSize, int swipeToRefresh, int nightMode, int displayImages, - boolean pinnedSslCertificate, String sslIssuedToCommonName, String sslIssuedToOrganization, String sslIssuedToOrganizationalUnit, String sslIssuedByCommonName, - String sslIssuedByOrganization, String sslIssuedByOrganizationalUnit, long sslStartDate, long sslEndDate) { + public void updatePinnedSslCertificate(int databaseId, String sslIssuedToCommonName, String sslIssuedToOrganization, String sslIssuedToOrganizationalUnit, String sslIssuedByCommonName, + String sslIssuedByOrganization, String sslIssuedByOrganizationalUnit, long sslStartDate, long sslEndDate) { - // Store the domain data in a `ContentValues`. - ContentValues domainContentValues = new ContentValues(); + // Store the pinned SSL certificate in a content values. + ContentValues pinnedSslCertificateContentValues = new ContentValues(); - // Add entries for each field in the database. - domainContentValues.put(DOMAIN_NAME, domainName); - domainContentValues.put(ENABLE_JAVASCRIPT, javaScriptEnabled); - domainContentValues.put(ENABLE_FIRST_PARTY_COOKIES, firstPartyCookiesEnabled); - domainContentValues.put(ENABLE_THIRD_PARTY_COOKIES, thirdPartyCookiesEnabled); - domainContentValues.put(ENABLE_DOM_STORAGE, domStorageEnabled); - domainContentValues.put(ENABLE_FORM_DATA, formDataEnabled); // Form data can be removed once the minimum API >= 26. - domainContentValues.put(ENABLE_EASYLIST, easyListEnabled); - domainContentValues.put(ENABLE_EASYPRIVACY, easyPrivacyEnabled); - domainContentValues.put(ENABLE_FANBOYS_ANNOYANCE_LIST, fanboysAnnoyanceEnabled); - domainContentValues.put(ENABLE_FANBOYS_SOCIAL_BLOCKING_LIST, fanboysSocialBlockingEnabled); - domainContentValues.put(ENABLE_ULTRAPRIVACY, ultraPrivacyEnabled); - domainContentValues.put(BLOCK_ALL_THIRD_PARTY_REQUESTS, blockAllThirdPartyRequests); - domainContentValues.put(USER_AGENT, userAgent); - domainContentValues.put(FONT_SIZE, fontSize); - domainContentValues.put(SWIPE_TO_REFRESH, swipeToRefresh); - domainContentValues.put(NIGHT_MODE, nightMode); - domainContentValues.put(DISPLAY_IMAGES, displayImages); - domainContentValues.put(PINNED_SSL_CERTIFICATE, pinnedSslCertificate); - domainContentValues.put(SSL_ISSUED_TO_COMMON_NAME, sslIssuedToCommonName); - domainContentValues.put(SSL_ISSUED_TO_ORGANIZATION, sslIssuedToOrganization); - domainContentValues.put(SSL_ISSUED_TO_ORGANIZATIONAL_UNIT, sslIssuedToOrganizationalUnit); - domainContentValues.put(SSL_ISSUED_BY_COMMON_NAME, sslIssuedByCommonName); - domainContentValues.put(SSL_ISSUED_BY_ORGANIZATION, sslIssuedByOrganization); - domainContentValues.put(SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, sslIssuedByOrganizationalUnit); - domainContentValues.put(SSL_START_DATE, sslStartDate); - domainContentValues.put(SSL_END_DATE, sslEndDate); + // Add entries for each field in the certificate. + pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_COMMON_NAME, sslIssuedToCommonName); + pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATION, sslIssuedToOrganization); + pinnedSslCertificateContentValues.put(SSL_ISSUED_TO_ORGANIZATIONAL_UNIT, sslIssuedToOrganizationalUnit); + pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_COMMON_NAME, sslIssuedByCommonName); + pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATION, sslIssuedByOrganization); + pinnedSslCertificateContentValues.put(SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, sslIssuedByOrganizationalUnit); + pinnedSslCertificateContentValues.put(SSL_START_DATE, sslStartDate); + pinnedSslCertificateContentValues.put(SSL_END_DATE, sslEndDate); // Get a writable database handle. SQLiteDatabase domainsDatabase = this.getWritableDatabase(); - // Update the row for `databaseId`. The last argument is `null` because there are no `whereArgs`. - domainsDatabase.update(DOMAINS_TABLE, domainContentValues, _ID + " = " + databaseId, null); + // Update the row for database ID. + domainsDatabase.update(DOMAINS_TABLE, pinnedSslCertificateContentValues, _ID + " = " + databaseId, null); // Close the database handle. domainsDatabase.close(); } - public void updateCertificate(int databaseId, String sslIssuedToCommonName, String sslIssuedToOrganization, String sslIssuedToOrganizationalUnit, String sslIssuedByCommonName, - String sslIssuedByOrganization, String sslIssuedByOrganizationalUnit, long sslStartDate, long sslEndDate) { - // Store the domain data in a `ContentValues`. - ContentValues domainContentValues = new ContentValues(); + public void updatePinnedIpAddresses(int databaseId, String ipAddresses) { + // Store the pinned IP addresses in a content values. + ContentValues pinnedIpAddressesContentValues = new ContentValues(); - // Add entries for each field in the certificate. - domainContentValues.put(SSL_ISSUED_TO_COMMON_NAME, sslIssuedToCommonName); - domainContentValues.put(SSL_ISSUED_TO_ORGANIZATION, sslIssuedToOrganization); - domainContentValues.put(SSL_ISSUED_TO_ORGANIZATIONAL_UNIT, sslIssuedToOrganizationalUnit); - domainContentValues.put(SSL_ISSUED_BY_COMMON_NAME, sslIssuedByCommonName); - domainContentValues.put(SSL_ISSUED_BY_ORGANIZATION, sslIssuedByOrganization); - domainContentValues.put(SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, sslIssuedByOrganizationalUnit); - domainContentValues.put(SSL_START_DATE, sslStartDate); - domainContentValues.put(SSL_END_DATE, sslEndDate); + // Add the IP addresses to the content values. + pinnedIpAddressesContentValues.put(IP_ADDRESSES, ipAddresses); // Get a writable database handle. SQLiteDatabase domainsDatabase = this.getWritableDatabase(); - // Update the row for `databaseId`. The last argument is `null` because there are no `whereArgs`. - domainsDatabase.update(DOMAINS_TABLE, domainContentValues, _ID + " = " + databaseId, null); + // Update the row for the database ID. + domainsDatabase.update(DOMAINS_TABLE, pinnedIpAddressesContentValues, _ID + " = " + databaseId, null); // Close the database handle. domainsDatabase.close(); diff --git a/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java b/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java index bcec5294..4f9d2573 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java +++ b/app/src/main/java/com/stoutner/privacybrowser/helpers/ImportExportDatabaseHelper.java @@ -38,7 +38,7 @@ public class ImportExportDatabaseHelper { public static final String EXPORT_SUCCESSFUL = "Export Successful"; public static final String IMPORT_SUCCESSFUL = "Import Successful"; - private static final int SCHEMA_VERSION = 3; + private static final int SCHEMA_VERSION = 4; private static final String PREFERENCES_TABLE = "preferences"; // The preferences constants. @@ -138,6 +138,8 @@ public class ImportExportDatabaseHelper { domainsContentValues.put(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))); domainsContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, domainsCursor.getLong(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); domainsContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, domainsCursor.getLong(domainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); + domainsContentValues.put(DomainsDatabaseHelper.PINNED_IP_ADDRESSES, domainsCursor.getInt(domainsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES))); + domainsContentValues.put(DomainsDatabaseHelper.IP_ADDRESSES, domainsCursor.getString(domainsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES))); // Insert the record into the export database. exportDatabase.insert(DomainsDatabaseHelper.DOMAINS_TABLE, null, domainsContentValues); @@ -328,7 +330,7 @@ public class ImportExportDatabaseHelper { // Create an integer to track the number of bytes read. int bytesRead; - // Copy the import file to the temporary import file. Once API >= 26 `Files.copy` can be used instead. + // Copy the import file to the temporary import file. Once the minimum API >= 26 `Files.copy` can be used instead. while ((bytesRead = importFileInputStream.read(transferByteArray)) > 0) { temporaryImportFileOutputStream.write(transferByteArray, 0, bytesRead); } @@ -341,7 +343,7 @@ public class ImportExportDatabaseHelper { // Get a handle for the shared preference. SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); - // Open the import database. Once API >= 27 the file can be opened directly without using the string. + // Open the import database. Once the minimum API >= 27 the file can be opened directly without using the string. SQLiteDatabase importDatabase = SQLiteDatabase.openDatabase(temporaryImportFileString, null, SQLiteDatabase.OPEN_READWRITE); // Get the database version. @@ -388,6 +390,11 @@ public class ImportExportDatabaseHelper { // Place the font size string in the new column. importDatabase.execSQL("UPDATE " + PREFERENCES_TABLE + " SET " + FONT_SIZE + " = " + fontSize); + + case 3: + // Add the Pinned IP Addresses columns. + importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.PINNED_IP_ADDRESSES + " BOOLEAN"); + importDatabase.execSQL("ALTER TABLE " + DomainsDatabaseHelper.DOMAINS_TABLE + " ADD COLUMN " + DomainsDatabaseHelper.IP_ADDRESSES + " TEXT"); } } @@ -438,6 +445,8 @@ public class ImportExportDatabaseHelper { importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_ISSUED_BY_ORGANIZATIONAL_UNIT))); domainsContentValues.put(DomainsDatabaseHelper.SSL_START_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_START_DATE))); domainsContentValues.put(DomainsDatabaseHelper.SSL_END_DATE, importDomainsCursor.getLong(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); + domainsContentValues.put(DomainsDatabaseHelper.PINNED_IP_ADDRESSES, importDomainsCursor.getInt(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.PINNED_IP_ADDRESSES))); + domainsContentValues.put(DomainsDatabaseHelper.IP_ADDRESSES, importDomainsCursor.getString(importDomainsCursor.getColumnIndex(DomainsDatabaseHelper.IP_ADDRESSES))); // Insert the record into the export database. domainsDatabaseHelper.addDomain(domainsContentValues); diff --git a/app/src/main/res/layout/domain_settings_fragment.xml b/app/src/main/res/layout/domain_settings_fragment.xml index c44dd7bd..74e76de9 100644 --- a/app/src/main/res/layout/domain_settings_fragment.xml +++ b/app/src/main/res/layout/domain_settings_fragment.xml @@ -29,7 +29,6 @@ android:descendantFocusability="beforeDescendants" > + android:layout_marginBottom="18dp" > @@ -722,10 +720,7 @@ android:id="@+id/current_website_certificate_cardview" android:layout_height="wrap_content" android:layout_width="match_parent" - android:layout_marginTop="5dp" - android:layout_marginBottom="10dp" - android:layout_marginStart="10dp" - android:layout_marginEnd="10dp" > + android:layout_margin="10dp" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/pinned_mismatch_linearlayout.xml b/app/src/main/res/layout/pinned_mismatch_linearlayout.xml new file mode 100644 index 00000000..549e6f73 --- /dev/null +++ b/app/src/main/res/layout/pinned_mismatch_linearlayout.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/pinned_mismatch_scrollview.xml b/app/src/main/res/layout/pinned_mismatch_scrollview.xml new file mode 100644 index 00000000..b6b2a185 --- /dev/null +++ b/app/src/main/res/layout/pinned_mismatch_scrollview.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/pinned_ssl_certificate_mismatch_linearlayout.xml b/app/src/main/res/layout/pinned_ssl_certificate_mismatch_linearlayout.xml deleted file mode 100644 index 73e465dd..00000000 --- a/app/src/main/res/layout/pinned_ssl_certificate_mismatch_linearlayout.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/pinned_ssl_certificate_mismatch_scrollview.xml b/app/src/main/res/layout/pinned_ssl_certificate_mismatch_scrollview.xml deleted file mode 100644 index a8aa1a3e..00000000 --- a/app/src/main/res/layout/pinned_ssl_certificate_mismatch_scrollview.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/ssl_certificate_error.xml b/app/src/main/res/layout/ssl_certificate_error.xml index 6c95fbfa..0a2bf6fc 100644 --- a/app/src/main/res/layout/ssl_certificate_error.xml +++ b/app/src/main/res/layout/ssl_certificate_error.xml @@ -1,7 +1,7 @@ @@ -90,17 +96,17 @@ android:textColor="?attr/sslTitle"/> @@ -117,12 +123,12 @@ android:textColor="?attr/sslTitle"/> diff --git a/app/src/main/res/menu/webview_options_menu.xml b/app/src/main/res/menu/webview_options_menu.xml index 8c26ff92..6391bf6b 100644 --- a/app/src/main/res/menu/webview_options_menu.xml +++ b/app/src/main/res/menu/webview_options_menu.xml @@ -318,28 +318,16 @@ android:checkable="true" app:showAsAction="never" /> - - - - @@ -357,16 +345,28 @@ android:orderInCategory="1010" app:showAsAction="never" /> + + + + diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 174ef2fb..583a9476 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -92,11 +92,9 @@ URL URL: - - SSL aktualisieren - SSL-Zertifikat Mismatch - Aktuelles SSL - Pinned SSL + + Aktualisieren + Aktuelles HTTP-Authentifizierung @@ -255,9 +253,9 @@ Grafiken deaktiviert SSL-Zertifikat verankern - Gespeicherte SSL-Zertifikate - SSL-Zertifikat der aktuellen Webseite - Zuerst verschlüsselte Webseite laden... + Gespeicherte SSL-Zertifikate + SSL-Zertifikat der aktuellen Webseite + Zuerst verschlüsselte Webseite laden... Privacy Browser Handbuch diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 14913ce8..f7d106f2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -93,11 +93,10 @@ URL URL: - - Actualizar SSL - No coinciden los certificados SSL - SSL actual - SSL fijado + + Actualizar + Actual + Fijado Autenticación HTTP @@ -310,9 +309,9 @@ Imágenes deshabilitadas Certificado SSL fijado - Certificado SSL guardado - Certificado SSL actual de la web - Cargar una página web cifrada antes de abrir la configuración de dominio para rellenar el certificado SSL de la página web actual. + Certificado SSL guardado + Certificado SSL actual de la web + Cargar una página web cifrada antes de abrir la configuración de dominio para rellenar el certificado SSL de la página web actual. Cifrado diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 2929723f..db4d9d58 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -93,11 +93,10 @@ URL URL: - - Aggiorna SSL - Incompatibilità certificato SSL - SSL attuale - SSL appuntato + + Aggiorna + Attuale + Appuntato Autenticazione HTTP @@ -309,9 +308,9 @@ Immagini disabilitate Certificato SSL appuntato - Certificato SSL salvato - Certificato SSL di questo sito - Carica un sito Web criptato prima di aprire le impostazioni dei domini per popolare il certificato SSL del sito attuale. + Certificato SSL salvato + Certificato SSL di questo sito + Carica un sito Web criptato prima di aprire le impostazioni dei domini per popolare il certificato SSL del sito attuale. Cifratura diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 5796d029..85665219 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -91,10 +91,9 @@ URL: - Обновление SSL - Несоответствие сертификата SSL - Текущий SSL - Закрепленный SSL + Обновление + Текущий + Закрепленный Аутентификация HTTP @@ -306,9 +305,9 @@ Изображения выключены Закрепленный сертификат SSL - Сохраненный сертификат SSL - Текущий сертификат SSL сайта - Откройте зашифрованный сайт перед настройкой домена, чтобы заполнить текущий сертификат SSL веб-сайта. + Сохраненный сертификат SSL + Текущий сертификат SSL сайта + Откройте зашифрованный сайт перед настройкой домена, чтобы заполнить текущий сертификат SSL веб-сайта. Шифрование diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 1dd52e19..0ebbe610 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -90,11 +90,10 @@ URL URL: - - SSL güncelle - SSL Sertifikası Uyumsuzluğu - Geçerli SSL - İğneli SSL + + Güncelle + Geçerli + İğneli HTTP Kimlik Doğrulama @@ -305,9 +304,9 @@ Resimler devre dışı İğneli SSL sertifikası - Kayıtlı SSL sertifikası - Geçerli web sitesi SSL sertifikası - Geçerli web sitesinin SSL sertifikasını doldurmak için Domain Ayarlarını açmadan önce şifrelenmiş bir web sitesi yükleyin. + Kayıtlı SSL sertifikası + Geçerli web sitesi SSL sertifikası + Geçerli web sitesinin SSL sertifikasını doldurmak için Domain Ayarlarını açmadan önce şifrelenmiş bir web sitesi yükleyin. Şifreleme diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d39bf641..7e8a0375 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -97,11 +97,11 @@ URL URL: - - Update SSL - SSL Certificate Mismatch - Current SSL - Pinned SSL + + Pinned Mismatch + Update + Current + Pinned HTTP Authentication @@ -314,9 +314,12 @@ Images disabled Pinned SSL certificate - Saved SSL certificate - Current website SSL certificate - Load an encrypted website before opening Domain Settings to populate the current website SSL certificate. + Saved SSL certificate + Current website SSL certificate + Load an encrypted website before opening Domain Settings to populate the current website SSL certificate. + Pinned IP addresses + Saved IP addresses + Current IP addresses Encryption