X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fdialogs%2FPinnedSslCertificateMismatchDialog.java;fp=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacybrowser%2Fdialogs%2FPinnedSslCertificateMismatchDialog.java;h=aa5b6279d8ae2b9e670bdaa094c0ef7f06f4edd0;hb=5fb34c1fa70b7c42a0fc3c0b5af8e856d3af2695;hp=0000000000000000000000000000000000000000;hpb=500012f6332dee70b77f7a2c3f69a78cdac435af;p=PrivacyBrowserAndroid.git diff --git a/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedSslCertificateMismatchDialog.java b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedSslCertificateMismatchDialog.java new file mode 100644 index 00000000..aa5b6279 --- /dev/null +++ b/app/src/main/java/com/stoutner/privacybrowser/dialogs/PinnedSslCertificateMismatchDialog.java @@ -0,0 +1,390 @@ +/* + * Copyright © 2017 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; +// We have to use `AppCompatDialogFragment` instead of `DialogFragment` or an error is produced 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.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; + +// `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`. +@SuppressLint("InflateParams") +public class PinnedSslCertificateMismatchDialog extends AppCompatDialogFragment { + // `layoutInflater` is used in `onCreateDialog()` and `pagerAdapter`. + private LayoutInflater layoutInflater; + + // The current website SSL certificate variables are used in `onCreateDialog()` and `pagerAdapter()`. + 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(); + } + + // `sslCertificateErrorListener` is used in `onAttach` and `onCreateDialog`. + private PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener pinnedSslCertificateMismatchListener; + + // Check to make sure that the parent activity implements the listener. + public void onAttach(Context context) { + super.onAttach(context); + + try { + pinnedSslCertificateMismatchListener = (PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener) context; + } catch(ClassCastException exception) { + throw new ClassCastException(context.toString() + " must implement PinnedSslCertificateMismatchListener"); + } + } + + @NonNull + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Get the activity's layout inflater. + layoutInflater = getActivity().getLayoutInflater(); + + // Use `AlertDialog.Builder` to create the `AlertDialog`. + 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, new DialogInterface.OnClickListener() { + @Override + public void onClick(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 two `nulls` do not specify the database name or a `CursorFactory`. 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, new DialogInterface.OnClickListener() { + @Override + public void onClick(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, new DialogInterface.OnClickListener() { + @Override + public void onClick(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 `AlertDialog` from the `AlertDialog.Builder` + final AlertDialog alertDialog = dialogBuilder.create(); + + // Show the `AlertDialog` so the items in the layout can be modified. + alertDialog.show(); + + // Setup `wrapVerticalContentViewPager`. + WrapVerticalContentViewPager wrapVerticalContentViewPager = (WrapVerticalContentViewPager) alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_viewpager); + wrapVerticalContentViewPager.setAdapter(new pagerAdapter()); + + // Setup the `TabLayout` and connect it to the `WrapVerticalContentViewPager`. + TabLayout 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(View view, 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 + public Object instantiateItem(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 = (TextView) tabViewGroup.findViewById(R.id.issued_to_cname); + TextView issuedToONameTextView = (TextView) tabViewGroup.findViewById(R.id.issued_to_oname); + TextView issuedToUNameTextView = (TextView) tabViewGroup.findViewById(R.id.issued_to_uname); + TextView issuedByCNameTextView = (TextView) tabViewGroup.findViewById(R.id.issued_by_cname); + TextView issuedByONameTextView = (TextView) tabViewGroup.findViewById(R.id.issued_by_oname); + TextView issuedByUNameTextView = (TextView) tabViewGroup.findViewById(R.id.issued_by_uname); + TextView startDateTextView = (TextView) tabViewGroup.findViewById(R.id.start_date); + TextView endDateTextView = (TextView) 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; + } + } +}