X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Factivities%2FMainWebViewActivity.java;h=b13a785235c96e6d72208d92d0fc794e17140714;hb=e75f230075b4059be6a9b6d27d8b6b202c74a6ff;hp=b98843ab4ce4f487f83e10a44df270214a67dbfd;hpb=ecf7a1f450559304c67a6dbe78ddb10c876ed140;p=PrivacyBrowserAndroid.git 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 b98843ab..b13a7852 100644 --- a/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java +++ b/app/src/main/java/com/stoutner/privacybrowser/activities/MainWebViewActivity.java @@ -74,6 +74,7 @@ import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.webkit.CookieManager; import android.webkit.DownloadListener; +import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; import android.webkit.WebBackForwardList; import android.webkit.WebChromeClient; @@ -95,6 +96,8 @@ import com.stoutner.privacybrowser.BuildConfig; import com.stoutner.privacybrowser.R; import com.stoutner.privacybrowser.dialogs.CreateHomeScreenShortcutDialog; import com.stoutner.privacybrowser.dialogs.DownloadImageDialog; +import com.stoutner.privacybrowser.dialogs.HttpAuthenticationDialog; +import com.stoutner.privacybrowser.dialogs.PinnedSslCertificateMismatchDialog; import com.stoutner.privacybrowser.dialogs.UrlHistoryDialog; import com.stoutner.privacybrowser.dialogs.ViewSslCertificateDialog; import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper; @@ -112,6 +115,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -119,22 +123,23 @@ import java.util.Set; // We need to use AppCompatActivity from android.support.v7.app.AppCompatActivity to have access to the SupportActionBar until the minimum API is >= 21. public class MainWebViewActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, CreateHomeScreenShortcutDialog.CreateHomeScreenSchortcutListener, - SslCertificateErrorDialog.SslCertificateErrorListener, DownloadFileDialog.DownloadFileListener, DownloadImageDialog.DownloadImageListener, UrlHistoryDialog.UrlHistoryListener { + HttpAuthenticationDialog.HttpAuthenticationListener, PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener, SslCertificateErrorDialog.SslCertificateErrorListener, DownloadFileDialog.DownloadFileListener, + DownloadImageDialog.DownloadImageListener, UrlHistoryDialog.UrlHistoryListener { // `darkTheme` is public static so it can be accessed from `AboutActivity`, `GuideActivity`, `AddDomainDialog`, `SettingsActivity`, `DomainsActivity`, `DomainsListFragment`, `BookmarksActivity`, `BookmarksDatabaseViewActivity`, - // `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`, `ViewSslCertificateDialog`, - // `CreateHomeScreenShortcutDialog`, and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`. + // `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `DownloadFileDialog`, `DownloadImageDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `HttpAuthenticationDialog`, `MoveToFolderDialog`, `SslCertificateErrorDialog`, `UrlHistoryDialog`, + // `ViewSslCertificateDialog`, `CreateHomeScreenShortcutDialog`, and `OrbotProxyHelper`. It is also used in `onCreate()`, `applyAppSettings()`, `applyDomainSettings()`, and `updatePrivacyIcons()`. public static boolean darkTheme; - // `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, `ViewSslCertificateDialog`. - // It is also used in `onCreate()`, `onCreateHomeScreenShortcutCreate()`, and `applyDomainSettings`. + // `favoriteIconBitmap` is public static so it can be accessed from `CreateHomeScreenShortcutDialog`, `BookmarksActivity`, `CreateBookmarkDialog`, `CreateBookmarkFolderDialog`, `EditBookmarkDialog`, `EditBookmarkFolderDialog`, + // and `ViewSslCertificateDialog`. It is also used in `onCreate()`, `onCreateHomeScreenShortcutCreate()`, and `applyDomainSettings`. public static Bitmap favoriteIconBitmap; // `formattedUrlString` is public static so it can be accessed from `BookmarksActivity`, `CreateBookmarkDialog`, and `AddDomainDialog`. // It is also used in `onCreate()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onCreateHomeScreenShortcutCreate()`, and `loadUrlFromTextBox()`. public static String formattedUrlString; - // `sslCertificate` is public static so it can be accessed from `ViewSslCertificateDialog`. It is also used in `onCreate()`. + // `sslCertificate` is public static so it can be accessed from `DomainsActivity`, `DomainsListFragment`, `DomainSettingsFragment`, `PinnedSslCertificateMismatchDialog`, and `ViewSslCertificateDialog`. It is also used in `onCreate()`. public static SslCertificate sslCertificate; // `orbotStatus` is public static so it can be accessed from `OrbotProxyHelper`. It is also used in `onCreate()`. @@ -149,11 +154,22 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `reloadOnRestartBoolean` is public static so it can be accessed from `SettingsFragment`. It is also used in `onRestart()` public static boolean reloadOnRestartBoolean; + // 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 int domainSettingsDatabaseId; + 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; + // `appBar` is used in `onCreate()`, `onOptionsItemSelected()`, `closeFindOnPage()`, and `applyAppSettings()`. private ActionBar appBar; - // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, and `applyDomainSettings()`. + // `navigatingHistory` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchBack()`, and `applyDomainSettings()`. private boolean navigatingHistory; // `favoriteIconDefaultBitmap` is used in `onCreate()` and `applyDomainSettings`. @@ -166,7 +182,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation private CoordinatorLayout rootCoordinatorLayout; // `mainWebView` is used in `onCreate()`, `onPrepareOptionsMenu()`, `onOptionsItemSelected()`, `onNavigationItemSelected()`, `onRestart()`, `onCreateContextMenu()`, `findPreviousOnPage()`, `findNextOnPage()`, `closeFindOnPage()`, `loadUrlFromTextBox()` - // and `setDisplayWebpageImages()`. + // `onSslMismatchBack()`, and `setDisplayWebpageImages()`. private WebView mainWebView; // `fullScreenVideoFrameLayout` is used in `onCreate()` and `onConfigurationChanged()`. @@ -233,9 +249,12 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `translucentNavigationBarOnFullscreen` is used in `onCreate()` and `applyAppSettings()`. private boolean translucentNavigationBarOnFullscreen; - // `currentDomainName` is used in `onCreate()`, `onNavigationItemSelected()`, and `applyDomainSettings()`. + // `currentDomainName` is used in `onCreate()`, `onNavigationItemSelected()`, `onSslMismatchProceed()`, and `applyDomainSettings()`. private String currentDomainName; + // `ignorePinnedSslCertificateForDomain` is used in `onCreate()`, `onSslMismatchProceed()`, and `applyDomainSettings()`. + private boolean ignorePinnedSslCertificate; + // `waitingForOrbot` is used in `onCreate()` and `applyAppSettings()`. private boolean waitingForOrbot; @@ -287,6 +306,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `sslErrorHandler` is used in `onCreate()`, `onSslErrorCancel()`, and `onSslErrorProceed`. private SslErrorHandler sslErrorHandler; + // `httpAuthHandler` is used in `onCreate()`, `onHttpAuthenticationCancel()`, and `onHttpAuthenticationProceed()`. + private static HttpAuthHandler httpAuthHandler; + // `inputMethodManager` is used in `onOptionsItemSelected()`, `loadUrlFromTextBox()`, and `closeFindOnPage()`. private InputMethodManager inputMethodManager; @@ -296,6 +318,10 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // `urlIsLoading` is used in `onCreate()`, `loadUrl()`, and `applyDomainSettings()`. private boolean urlIsLoading; + // `pinnedDomainSslCertificate` is used in `onCreate()` and `applyDomainSettings()`. + private boolean pinnedDomainSslCertificate; + + @Override // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled. The whole premise of Privacy Browser is built around an understanding of these dangers. @SuppressLint("SetJavaScriptEnabled") @@ -709,12 +735,20 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation } } + // Handle HTTP authentication requests. + @Override + public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { + // Store `handler` so it can be accessed from `onHttpAuthenticationCancel()` and `onHttpAuthenticationProceed()`. + httpAuthHandler = handler; + + // Display the HTTP authentication dialog. + AppCompatDialogFragment httpAuthenticationDialogFragment = HttpAuthenticationDialog.displayDialog(host, realm); + httpAuthenticationDialogFragment.show(getSupportFragmentManager(), getString(R.string.http_authentication)); + } + // Update the URL in urlTextBox when the page starts to load. @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { - // Reset `webViewTitle` - webViewTitle = getString(R.string.no_title); - // Check to see if we are waiting on Orbot. if (!waitingForOrbot) { // We are not waiting on Orbot, so we need to process the URL. // We need to update `formattedUrlString` at the beginning of the load, so that if the user toggles JavaScript during the load the new website is reloaded. @@ -793,20 +827,100 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation } } - // Store the SSL certificate so it can be accessed from `ViewSslCertificateDialog`. + // Store the SSL certificate so it can be accessed from `ViewSslCertificateDialog` and `PinnedSslCertificateMismatchDialog`. 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. + if (pinnedDomainSslCertificate && !ignorePinnedSslCertificate) { + // Initialize the current SSL certificate variables. + String currentWebsiteIssuedToCName = ""; + String currentWebsiteIssuedToOName = ""; + String currentWebsiteIssuedToUName = ""; + String currentWebsiteIssuedByCName = ""; + String currentWebsiteIssuedByOName = ""; + String currentWebsiteIssuedByUName = ""; + Date currentWebsiteSslStartDate = null; + Date currentWebsiteSslEndDate = null; + + + // Extract the individual pieces of information from the current website SSL certificate if it is not null. + if (sslCertificate != null) { + currentWebsiteIssuedToCName = sslCertificate.getIssuedTo().getCName(); + currentWebsiteIssuedToOName = sslCertificate.getIssuedTo().getOName(); + currentWebsiteIssuedToUName = sslCertificate.getIssuedTo().getUName(); + currentWebsiteIssuedByCName = sslCertificate.getIssuedBy().getCName(); + currentWebsiteIssuedByOName = sslCertificate.getIssuedBy().getOName(); + currentWebsiteIssuedByUName = sslCertificate.getIssuedBy().getUName(); + currentWebsiteSslStartDate = sslCertificate.getValidNotBeforeDate(); + 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`. + String currentWebsiteSslStartDateString = ""; + String currentWebsiteSslEndDateString = ""; + String pinnedDomainSslStartDateString = ""; + String pinnedDomainSslEndDateString = ""; + + // Convert the `Dates` to `Strings` if they are not `null`. + if (currentWebsiteSslStartDate != null) { + currentWebsiteSslStartDateString = currentWebsiteSslStartDate.toString(); + } + + if (currentWebsiteSslEndDate != null) { + currentWebsiteSslEndDateString = currentWebsiteSslEndDate.toString(); + } + + if (pinnedDomainSslStartDate != null) { + pinnedDomainSslStartDateString = pinnedDomainSslStartDate.toString(); + } + + if (pinnedDomainSslEndDate != null) { + pinnedDomainSslEndDateString = pinnedDomainSslEndDate.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)); + } + } } } // Handle SSL Certificate errors. @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - // Store `handler` so it can be accesses from `onSslErrorCancel()` and `onSslErrorProceed()`. - sslErrorHandler = handler; - - // Display the SSL error `AlertDialog`. - AppCompatDialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error); - sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.ssl_certificate_error)); + // Get the current website SSL certificate. + SslCertificate currentWebsiteSslCertificate = error.getCertificate(); + + // Extract the individual pieces of information from the current website SSL certificate. + String currentWebsiteIssuedToCName = currentWebsiteSslCertificate.getIssuedTo().getCName(); + String currentWebsiteIssuedToOName = currentWebsiteSslCertificate.getIssuedTo().getOName(); + String currentWebsiteIssuedToUName = currentWebsiteSslCertificate.getIssuedTo().getUName(); + String currentWebsiteIssuedByCName = currentWebsiteSslCertificate.getIssuedBy().getCName(); + String currentWebsiteIssuedByOName = currentWebsiteSslCertificate.getIssuedBy().getOName(); + String currentWebsiteIssuedByUName = currentWebsiteSslCertificate.getIssuedBy().getUName(); + Date currentWebsiteSslStartDate = currentWebsiteSslCertificate.getValidNotBeforeDate(); + 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. + 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()`. + sslErrorHandler = handler; + + // Display the SSL error `AlertDialog`. + AppCompatDialogFragment sslCertificateErrorDialogFragment = SslCertificateErrorDialog.displayDialog(error); + sslCertificateErrorDialogFragment.show(getSupportFragmentManager(), getString(R.string.ssl_certificate_error)); + } } }); @@ -908,7 +1022,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { // Show the `DownloadFileDialog` `AlertDialog` and name this instance `@string/download`. AppCompatDialogFragment downloadFileDialogFragment = DownloadFileDialog.fromUrl(url, contentDisposition, contentLength); - downloadFileDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.download)); + downloadFileDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); } }); @@ -924,6 +1038,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Set `mainWebView` to load in overview mode (zoomed out to the maximum width). mainWebView.getSettings().setLoadWithOverviewMode(true); + // Explicitly disable geolocation. + mainWebView.getSettings().setGeolocationEnabled(false); + // Initialize cookieManager. cookieManager = CookieManager.getInstance(); @@ -1160,47 +1277,47 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Prepare the font size title and current size menu item. switch (fontSize) { case 25: - fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.twenty_five_percent); + fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.twenty_five_percent); selectedFontSizeMenuItem = menu.findItem(R.id.font_size_twenty_five_percent); break; case 50: - fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.fifty_percent); + fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.fifty_percent); selectedFontSizeMenuItem = menu.findItem(R.id.font_size_fifty_percent); break; case 75: - fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.seventy_five_percent); + fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.seventy_five_percent); selectedFontSizeMenuItem = menu.findItem(R.id.font_size_seventy_five_percent); break; case 100: - fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent); + fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent); selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent); break; case 125: - fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_twenty_five_percent); + fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_twenty_five_percent); selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_twenty_five_percent); break; case 150: - fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_fifty_percent); + fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_fifty_percent); selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_fifty_percent); break; case 175: - fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_seventy_five_percent); + fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_seventy_five_percent); selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_seventy_five_percent); break; case 200: - fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.two_hundred_percent); + fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.two_hundred_percent); selectedFontSizeMenuItem = menu.findItem(R.id.font_size_two_hundred_percent); break; default: - fontSizeTitle = getResources().getString(R.string.font_size) + " - " + getResources().getString(R.string.one_hundred_percent); + fontSizeTitle = getString(R.string.font_size) + " - " + getString(R.string.one_hundred_percent); selectedFontSizeMenuItem = menu.findItem(R.id.font_size_one_hundred_percent); break; } @@ -1490,12 +1607,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation case R.id.share: // Setup the share string. - String shareString; - if (webViewTitle != null) { - shareString = webViewTitle + " – " + urlTextBox.getText().toString(); - } else { - shareString = urlTextBox.getText().toString(); - } + String shareString = webViewTitle + " – " + urlTextBox.getText().toString(); // Create the share intent. Intent shareIntent = new Intent(); @@ -1514,17 +1626,15 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Show the Find on Page `RelativeLayout`. findOnPageLinearLayout.setVisibility(View.VISIBLE); - // Display the keyboard. We have to wait 200 ms before running the command to work around a bug in Android. - // http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working - findOnPageEditText.postDelayed(new Runnable() - { + // Display the keyboard. We have to wait 200 ms before running the command to work around a bug in Android. http://stackoverflow.com/questions/5520085/android-show-softkeyboard-with-showsoftinput-is-not-working + findOnPageEditText.postDelayed(new Runnable() { @Override public void run() { // Set the focus on `findOnPageEditText`. findOnPageEditText.requestFocus(); - // Display the keyboard. + // Display the keyboard. `0` sets no input flags. inputMethodManager.showSoftInput(findOnPageEditText, 0); } }, 200); @@ -1538,13 +1648,13 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation PrintDocumentAdapter printDocumentAdapter = mainWebView.createPrintDocumentAdapter(); // Print the document. The print attributes are `null`. - printManager.print(getResources().getString(R.string.privacy_browser_web_page), printDocumentAdapter, null); + printManager.print(getString(R.string.privacy_browser_web_page), printDocumentAdapter, null); return true; case R.id.add_to_homescreen: // Show the `CreateHomeScreenShortcutDialog` `AlertDialog` and name this instance `R.string.create_shortcut`. AppCompatDialogFragment createHomeScreenShortcutDialogFragment = new CreateHomeScreenShortcutDialog(); - createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.create_shortcut)); + createHomeScreenShortcutDialogFragment.show(getSupportFragmentManager(), getString(R.string.create_shortcut)); //Everything else will be handled by `CreateHomeScreenShortcutDialog` and the associated listener below. return true; @@ -1596,7 +1706,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Show the `UrlHistoryDialog` `AlertDialog` and name this instance `R.string.history`. `this` is the `Context`. AppCompatDialogFragment urlHistoryDialogFragment = UrlHistoryDialog.loadBackForwardList(this, webBackForwardList); - urlHistoryDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.history)); + urlHistoryDialogFragment.show(getSupportFragmentManager(), getString(R.string.history)); break; case R.id.bookmarks: @@ -1828,7 +1938,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation @Override public boolean onMenuItemClick(MenuItem item) { // Save the link URL in a `ClipData`. - ClipData srcAnchorTypeClipData = ClipData.newPlainText(getResources().getString(R.string.url), linkUrl); + ClipData srcAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), linkUrl); // Set the `ClipData` as the clipboard's primary clip. clipboardManager.setPrimaryClip(srcAnchorTypeClipData); @@ -1871,7 +1981,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation @Override public boolean onMenuItemClick(MenuItem item) { // Save the email address in a `ClipData`. - ClipData srcEmailTypeClipData = ClipData.newPlainText(getResources().getString(R.string.email_address), linkUrl); + ClipData srcEmailTypeClipData = ClipData.newPlainText(getString(R.string.email_address), linkUrl); // Set the `ClipData` as the clipboard's primary clip. clipboardManager.setPrimaryClip(srcEmailTypeClipData); @@ -1906,7 +2016,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation public boolean onMenuItemClick(MenuItem item) { // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`. AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); - downloadImageDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.download)); + downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); return false; } }); @@ -1916,7 +2026,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation @Override public boolean onMenuItemClick(MenuItem item) { // Save the image URL in a `ClipData`. - ClipData srcImageTypeClipData = ClipData.newPlainText(getResources().getString(R.string.url), imageUrl); + ClipData srcImageTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl); // Set the `ClipData` as the clipboard's primary clip. clipboardManager.setPrimaryClip(srcImageTypeClipData); @@ -1952,7 +2062,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation public boolean onMenuItemClick(MenuItem item) { // Show the `DownloadImageDialog` `AlertDialog` and name this instance `@string/download`. AppCompatDialogFragment downloadImageDialogFragment = DownloadImageDialog.imageUrl(imageUrl); - downloadImageDialogFragment.show(getSupportFragmentManager(), getResources().getString(R.string.download)); + downloadImageDialogFragment.show(getSupportFragmentManager(), getString(R.string.download)); return false; } }); @@ -1962,7 +2072,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation @Override public boolean onMenuItemClick(MenuItem item) { // Save the image URL in a `ClipData`. - ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getResources().getString(R.string.url), imageUrl); + ClipData srcImageAnchorTypeClipData = ClipData.newPlainText(getString(R.string.url), imageUrl); // Set the `ClipData` as the clipboard's primary clip. clipboardManager.setPrimaryClip(srcImageAnchorTypeClipData); @@ -2090,10 +2200,26 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation } } + @Override + public void onHttpAuthenticationCancel() { + // Cancel the `HttpAuthHandler`. + httpAuthHandler.cancel(); + } + + @Override + public void onHttpAuthenticationProceed(AppCompatDialogFragment dialogFragment) { + // Get handles for the `EditTexts`. + EditText usernameEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.http_authentication_username); + EditText passwordEditText = (EditText) dialogFragment.getDialog().findViewById(R.id.http_authentication_password); + + // Proceed with the HTTP authentication. + httpAuthHandler.proceed(usernameEditText.getText().toString(), passwordEditText.getText().toString()); + } + public void viewSslCertificate(View view) { // Show the `ViewSslCertificateDialog` `AlertDialog` and name this instance `@string/view_ssl_certificate`. DialogFragment viewSslCertificateDialogFragment = new ViewSslCertificateDialog(); - viewSslCertificateDialogFragment.show(getFragmentManager(), getResources().getString(R.string.view_ssl_certificate)); + viewSslCertificateDialogFragment.show(getFragmentManager(), getString(R.string.view_ssl_certificate)); } @Override @@ -2106,6 +2232,26 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation sslErrorHandler.proceed(); } + @Override + public void onSslMismatchBack() { + if (mainWebView.canGoBack()) { // There is a back page in the history. + // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded. + navigatingHistory = true; + + // Go back. + mainWebView.goBack(); + } else { // There are no pages to go back to. + // Load a blank page + loadUrl(""); + } + } + + @Override + public void onSslMismatchProceed() { + // Do not check the pinned SSL certificate for this domain again until the domain changes. + ignorePinnedSslCertificate = true; + } + @Override public void onUrlHistoryEntrySelected(int moveBackOrForwardSteps) { // Set `navigatingHistory` so that the domain settings are applied when the new URL is loaded. @@ -2415,6 +2561,9 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation // Set the new `hostname` as the `currentDomainName`. currentDomainName = hostName; + // Reset `ignorePinnedSslCertificate`. + ignorePinnedSslCertificate = false; + // Reset `favoriteIconBitmap` and display it in the `appbar`. favoriteIconBitmap = favoriteIconDefaultBitmap; favoriteIconImageView.setImageBitmap(Bitmap.createScaledBitmap(favoriteIconBitmap, 64, 64, true)); @@ -2481,6 +2630,7 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation currentHostDomainSettingsCursor.moveToFirst(); // Get the settings from the cursor. + domainSettingsDatabaseId = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper._ID))); javaScriptEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_JAVASCRIPT)) == 1); firstPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_FIRST_PARTY_COOKIES)) == 1); thirdPartyCookiesEnabled = (currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.ENABLE_THIRD_PARTY_COOKIES)) == 1); @@ -2489,6 +2639,27 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation String userAgentString = currentHostDomainSettingsCursor.getString(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.USER_AGENT)); int fontSize = currentHostDomainSettingsCursor.getInt(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.FONT_SIZE)); 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)); + + // 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; + } else { + pinnedDomainSslStartDate = 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; + } else { + pinnedDomainSslEndDate = new Date(currentHostDomainSettingsCursor.getLong(currentHostDomainSettingsCursor.getColumnIndex(DomainsDatabaseHelper.SSL_END_DATE))); + } // Close `currentHostDomainSettingsCursor`. currentHostDomainSettingsCursor.close(); @@ -2565,6 +2736,18 @@ public class MainWebViewActivity extends AppCompatActivity implements Navigation mainWebView.getSettings().setSaveFormData(saveFormDataEnabled); mainWebView.getSettings().setTextZoom(Integer.valueOf(defaultFontSizeString)); + // Reset the pinned SSL certificate information. + domainSettingsDatabaseId = -1; + pinnedDomainSslCertificate = false; + pinnedDomainSslIssuedToCNameString = ""; + pinnedDomainSslIssuedToONameString = ""; + pinnedDomainSslIssuedToUNameString = ""; + pinnedDomainSslIssuedByCNameString = ""; + pinnedDomainSslIssuedByONameString = ""; + pinnedDomainSslIssuedByUNameString = ""; + pinnedDomainSslStartDate = null; + pinnedDomainSslEndDate = null; + // Set third-party cookies status if API >= 21. if (Build.VERSION.SDK_INT >= 21) { cookieManager.setAcceptThirdPartyCookies(mainWebView, thirdPartyCookiesEnabled);