2 * Copyright © 2017-2018 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
6 * Privacy Browser is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.dialogs;
22 import android.annotation.SuppressLint;
23 import android.app.AlertDialog;
24 import android.app.Dialog;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.net.http.SslCertificate;
28 import android.os.Bundle;
29 import android.support.annotation.NonNull;
30 import android.support.design.widget.TabLayout;
31 import android.support.v4.view.PagerAdapter;
32 // `AppCompatDialogFragment` is used instead of `DialogFragment` to avoid an error on API <=22.
33 import android.support.v7.app.AppCompatDialogFragment;
34 import android.text.SpannableStringBuilder;
35 import android.text.Spanned;
36 import android.text.style.ForegroundColorSpan;
37 import android.view.LayoutInflater;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.WindowManager;
41 import android.widget.TextView;
43 import com.stoutner.privacybrowser.R;
44 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
45 import com.stoutner.privacybrowser.definitions.WrapVerticalContentViewPager;
46 import com.stoutner.privacybrowser.helpers.DomainsDatabaseHelper;
48 import java.text.DateFormat;
49 import java.util.Date;
51 public class PinnedSslCertificateMismatchDialog extends AppCompatDialogFragment {
52 // `layoutInflater` is used in `onCreateDialog()` and `pagerAdapter`.
53 private LayoutInflater layoutInflater;
55 // The current website SSL certificate variables are used in `onCreateDialog()` and `pagerAdapter()`.
56 private String currentSslIssuedToCNameString;
57 private String currentSslIssuedToONameString;
58 private String currentSslIssuedToUNameString;
59 private String currentSslIssuedByCNameString;
60 private String currentSslIssuedByONameString;
61 private String currentSslIssuedByUNameString;
62 private Date currentSslStartDate;
63 private Date currentSslEndDate;
65 // The public interface is used to send information back to the parent activity.
66 public interface PinnedSslCertificateMismatchListener {
67 void onSslMismatchBack();
69 void onSslMismatchProceed();
72 // `sslCertificateErrorListener` is used in `onAttach` and `onCreateDialog`.
73 private PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener pinnedSslCertificateMismatchListener;
75 // Check to make sure that the parent activity implements the listener.
76 public void onAttach(Context context) {
77 super.onAttach(context);
80 pinnedSslCertificateMismatchListener = (PinnedSslCertificateMismatchDialog.PinnedSslCertificateMismatchListener) context;
81 } catch(ClassCastException exception) {
82 throw new ClassCastException(context.toString() + " must implement PinnedSslCertificateMismatchListener");
86 // `@SuppressLing("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
87 @SuppressLint("InflateParams")
90 public Dialog onCreateDialog(Bundle savedInstanceState) {
91 // Remove the incorrect lint warning that `getActivity()` might be null.
92 assert getActivity() != null;
94 // Get the activity's layout inflater.
95 layoutInflater = getActivity().getLayoutInflater();
97 // Use an alert dialog builder to create the alert dialog.
98 AlertDialog.Builder dialogBuilder;
100 // Set the style according to the theme.
101 if (MainWebViewActivity.darkTheme) {
102 // Set the dialog theme.
103 dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogDark);
106 dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_dark);
108 // Set the dialog theme.
109 dialogBuilder = new AlertDialog.Builder(getActivity(), R.style.PrivacyBrowserAlertDialogLight);
112 dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_light);
115 // Setup the neutral button.
116 dialogBuilder.setNeutralButton(R.string.update_ssl, (DialogInterface dialog, int which) -> {
117 // Initialize the `long` date variables. If the date is `null`, a long value of `0` will be stored in the Domains database entry.
118 long currentSslStartDateLong = 0;
119 long currentSslEndDateLong = 0;
121 // Convert the `Dates` into `longs`.
122 if (currentSslStartDate != null) {
123 currentSslStartDateLong = currentSslStartDate.getTime();
126 if (currentSslEndDate != null) {
127 currentSslEndDateLong = currentSslEndDate.getTime();
130 // Initialize the database handler. The `0` specifies the database version, but that is ignored and set instead using a constant in `DomainsDatabaseHelper`.
131 DomainsDatabaseHelper domainsDatabaseHelper = new DomainsDatabaseHelper(getContext(), null, null, 0);
133 // Update the pinned SSL certificate for this domain.
134 domainsDatabaseHelper.updateCertificate(MainWebViewActivity.domainSettingsDatabaseId, currentSslIssuedToCNameString, currentSslIssuedToONameString, currentSslIssuedToUNameString,
135 currentSslIssuedByCNameString, currentSslIssuedByONameString, currentSslIssuedByUNameString, currentSslStartDateLong, currentSslEndDateLong);
137 // Update the pinned SSL certificate global variables to match the information that is now in the database.
138 MainWebViewActivity.pinnedDomainSslIssuedToCNameString = currentSslIssuedToCNameString;
139 MainWebViewActivity.pinnedDomainSslIssuedToONameString = currentSslIssuedToONameString;
140 MainWebViewActivity.pinnedDomainSslIssuedToUNameString = currentSslIssuedToUNameString;
141 MainWebViewActivity.pinnedDomainSslIssuedByCNameString = currentSslIssuedByCNameString;
142 MainWebViewActivity.pinnedDomainSslIssuedByONameString = currentSslIssuedByONameString;
143 MainWebViewActivity.pinnedDomainSslIssuedByUNameString = currentSslIssuedByUNameString;
144 MainWebViewActivity.pinnedDomainSslStartDate = currentSslStartDate;
145 MainWebViewActivity.pinnedDomainSslEndDate = currentSslEndDate;
148 // Setup the negative button.
149 dialogBuilder.setNegativeButton(R.string.back, (DialogInterface dialog, int which) -> {
150 // Call the `onSslMismatchBack` public interface to send the `WebView` back one page.
151 pinnedSslCertificateMismatchListener.onSslMismatchBack();
154 // Setup the positive button.
155 dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
156 // Call the `onSslMismatchProceed` public interface.
157 pinnedSslCertificateMismatchListener.onSslMismatchProceed();
161 dialogBuilder.setTitle(R.string.ssl_certificate_mismatch);
163 // Set the layout. The parent view is `null` because it will be assigned by `AlertDialog`.
164 dialogBuilder.setView(layoutInflater.inflate(R.layout.pinned_ssl_certificate_mismatch_linearlayout, null));
166 // Create an alert dialog from the alert dialog builder.
167 final AlertDialog alertDialog = dialogBuilder.create();
169 // Disable screenshots if not allowed.
170 if (!MainWebViewActivity.allowScreenshots) {
171 // Remove the warning below that `getWindow()` might be null.
172 assert alertDialog.getWindow() != null;
174 // Disable screenshots.
175 alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
178 // Show the alert dialog so the items in the layout can be modified.
181 // Setup the view pager.
182 WrapVerticalContentViewPager wrapVerticalContentViewPager = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_viewpager);
183 wrapVerticalContentViewPager.setAdapter(new pagerAdapter());
185 // Setup the tab layout and connect it to the view pager.
186 TabLayout tabLayout = alertDialog.findViewById(R.id.pinned_ssl_certificate_mismatch_tablayout);
187 tabLayout.setupWithViewPager(wrapVerticalContentViewPager);
189 // `onCreateDialog()` requires the return of an `AlertDialog`.
193 private class pagerAdapter extends PagerAdapter {
195 public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
196 // Check to see if the `View` and the `Object` are the same.
197 return (view == object);
201 public int getCount() {
202 // There are two tabs.
207 public CharSequence getPageTitle(int position) {
208 // Return the current tab title.
209 if (position == 0) { // The current SSL certificate tab.
210 return getString(R.string.current_ssl);
211 } else { // The pinned SSL certificate tab.
212 return getString(R.string.pinned_ssl);
218 public Object instantiateItem(@NonNull ViewGroup container, int position) {
219 // Inflate the `ScrollView` for this tab.
220 ViewGroup tabViewGroup = (ViewGroup) layoutInflater.inflate(R.layout.pinned_ssl_certificate_mismatch_scrollview, container, false);
222 // Get handles for the `TextViews`.
223 TextView issuedToCNameTextView = tabViewGroup.findViewById(R.id.issued_to_cname);
224 TextView issuedToONameTextView = tabViewGroup.findViewById(R.id.issued_to_oname);
225 TextView issuedToUNameTextView = tabViewGroup.findViewById(R.id.issued_to_uname);
226 TextView issuedByCNameTextView = tabViewGroup.findViewById(R.id.issued_by_cname);
227 TextView issuedByONameTextView = tabViewGroup.findViewById(R.id.issued_by_oname);
228 TextView issuedByUNameTextView = tabViewGroup.findViewById(R.id.issued_by_uname);
229 TextView startDateTextView = tabViewGroup.findViewById(R.id.start_date);
230 TextView endDateTextView = tabViewGroup.findViewById(R.id.end_date);
233 String cNameLabel = getString(R.string.common_name) + " ";
234 String oNameLabel = getString(R.string.organization) + " ";
235 String uNameLabel = getString(R.string.organizational_unit) + " ";
236 String startDateLabel = getString(R.string.start_date) + " ";
237 String endDateLabel = getString(R.string.end_date) + " ";
239 // Get the current website SSL certificate.
240 SslCertificate sslCertificate = MainWebViewActivity.sslCertificate;
242 // Extract the individual pieces of information from the current website SSL certificate if it is not null.
243 if (sslCertificate != null) {
244 currentSslIssuedToCNameString = sslCertificate.getIssuedTo().getCName();
245 currentSslIssuedToONameString = sslCertificate.getIssuedTo().getOName();
246 currentSslIssuedToUNameString = sslCertificate.getIssuedTo().getUName();
247 currentSslIssuedByCNameString = sslCertificate.getIssuedBy().getCName();
248 currentSslIssuedByONameString = sslCertificate.getIssuedBy().getOName();
249 currentSslIssuedByUNameString = sslCertificate.getIssuedBy().getUName();
250 currentSslStartDate = sslCertificate.getValidNotBeforeDate();
251 currentSslEndDate = sslCertificate.getValidNotAfterDate();
253 // Initialize the current website SSL certificate variables with blank information.
254 currentSslIssuedToCNameString = "";
255 currentSslIssuedToONameString = "";
256 currentSslIssuedToUNameString = "";
257 currentSslIssuedByCNameString = "";
258 currentSslIssuedByONameString = "";
259 currentSslIssuedByUNameString = "";
262 // Initialize the `SpannableStringBuilders`.
263 SpannableStringBuilder issuedToCNameStringBuilder;
264 SpannableStringBuilder issuedToONameStringBuilder;
265 SpannableStringBuilder issuedToUNameStringBuilder;
266 SpannableStringBuilder issuedByCNameStringBuilder;
267 SpannableStringBuilder issuedByONameStringBuilder;
268 SpannableStringBuilder issuedByUNameStringBuilder;
269 SpannableStringBuilder startDateStringBuilder;
270 SpannableStringBuilder endDateStringBuilder;
272 // Setup the `SpannableStringBuilders` for each tab.
273 if (position == 0) { // Setup the current SSL certificate tab.
274 issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedToCNameString);
275 issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedToONameString);
276 issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedToUNameString);
277 issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + currentSslIssuedByCNameString);
278 issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + currentSslIssuedByONameString);
279 issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + currentSslIssuedByUNameString);
281 // Set the dates if they aren't `null`.
282 if (currentSslStartDate == null) {
283 startDateStringBuilder = new SpannableStringBuilder(startDateLabel);
285 startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslStartDate));
288 if (currentSslEndDate == null) {
289 endDateStringBuilder = new SpannableStringBuilder(endDateLabel);
291 endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(currentSslEndDate));
293 } else { // Setup the pinned SSL certificate tab.
294 issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + MainWebViewActivity.pinnedDomainSslIssuedToCNameString);
295 issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + MainWebViewActivity.pinnedDomainSslIssuedToONameString);
296 issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + MainWebViewActivity.pinnedDomainSslIssuedToUNameString);
297 issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + MainWebViewActivity.pinnedDomainSslIssuedByCNameString);
298 issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + MainWebViewActivity.pinnedDomainSslIssuedByONameString);
299 issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + MainWebViewActivity.pinnedDomainSslIssuedByUNameString);
301 // Set the dates if they aren't `null`.
302 if (MainWebViewActivity.pinnedDomainSslStartDate == null) {
303 startDateStringBuilder = new SpannableStringBuilder(startDateLabel);
305 startDateStringBuilder = new SpannableStringBuilder(startDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG)
306 .format(MainWebViewActivity.pinnedDomainSslStartDate));
309 if (MainWebViewActivity.pinnedDomainSslEndDate == null) {
310 endDateStringBuilder = new SpannableStringBuilder(endDateLabel);
312 endDateStringBuilder = new SpannableStringBuilder(endDateLabel + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(MainWebViewActivity.pinnedDomainSslEndDate));
316 // Create a red `ForegroundColorSpan`. We have to use the deprecated `getColor` until API >= 23.
317 @SuppressWarnings("deprecation") ForegroundColorSpan redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
319 // Create a blue `ForegroundColorSpan`.
320 ForegroundColorSpan blueColorSpan;
322 // Set `blueColorSpan` according to the theme. We have to use the deprecated `getColor()` until API >= 23.
323 if (MainWebViewActivity.darkTheme) {
324 //noinspection deprecation
325 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_400));
327 //noinspection deprecation
328 blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
331 // Configure the spans to display conflicting information in red. `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
332 if (currentSslIssuedToCNameString.equals(MainWebViewActivity.pinnedDomainSslIssuedToCNameString)) {
333 issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
335 issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
338 if (currentSslIssuedToONameString.equals(MainWebViewActivity.pinnedDomainSslIssuedToONameString)) {
339 issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
341 issuedToONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
344 if (currentSslIssuedToUNameString.equals(MainWebViewActivity.pinnedDomainSslIssuedToUNameString)) {
345 issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
347 issuedToUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
350 if (currentSslIssuedByCNameString.equals(MainWebViewActivity.pinnedDomainSslIssuedByCNameString)) {
351 issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
353 issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
356 if (currentSslIssuedByONameString.equals(MainWebViewActivity.pinnedDomainSslIssuedByONameString)) {
357 issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
359 issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
362 if (currentSslIssuedByUNameString.equals(MainWebViewActivity.pinnedDomainSslIssuedByUNameString)) {
363 issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
365 issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
368 if ((currentSslStartDate != null) && (MainWebViewActivity.pinnedDomainSslStartDate != null) && currentSslStartDate.equals(MainWebViewActivity.pinnedDomainSslStartDate)) {
369 startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
371 startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
374 if ((currentSslEndDate != null) && (MainWebViewActivity.pinnedDomainSslEndDate != null) && currentSslEndDate.equals(MainWebViewActivity.pinnedDomainSslEndDate)) {
375 endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
377 endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
380 // Display the strings.
381 issuedToCNameTextView.setText(issuedToCNameStringBuilder);
382 issuedToONameTextView.setText(issuedToONameStringBuilder);
383 issuedToUNameTextView.setText(issuedToUNameStringBuilder);
384 issuedByCNameTextView.setText(issuedByCNameStringBuilder);
385 issuedByONameTextView.setText(issuedByONameStringBuilder);
386 issuedByUNameTextView.setText(issuedByUNameStringBuilder);
387 startDateTextView.setText(startDateStringBuilder);
388 endDateTextView.setText(endDateStringBuilder);
391 container.addView(tabViewGroup);