]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/dialogs/SslCertificateErrorDialog.kt
Switch the FragmentPagerAdapters to FragmentStateAdapters. https://redmine.stoutner...
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / SslCertificateErrorDialog.kt
1 /*
2  * Copyright 2016-2023 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
5  *
6  * Privacy Browser Android 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.
10  *
11  * Privacy Browser Android 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.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser Android.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.dialogs
21
22 import android.app.Dialog
23 import android.content.DialogInterface
24 import android.net.Uri
25 import android.net.http.SslError
26 import android.os.Bundle
27 import android.text.SpannableStringBuilder
28 import android.text.Spanned
29 import android.text.style.ForegroundColorSpan
30 import android.view.WindowManager
31 import android.widget.TextView
32
33 import androidx.appcompat.app.AlertDialog
34 import androidx.fragment.app.DialogFragment
35 import androidx.preference.PreferenceManager
36
37 import com.stoutner.privacybrowser.R
38 import com.stoutner.privacybrowser.activities.MainWebViewActivity
39 import com.stoutner.privacybrowser.coroutines.GetHostIpAddressesCoroutine
40 import com.stoutner.privacybrowser.views.NestedScrollWebView
41
42 import java.text.DateFormat
43
44 // Define the class constants.
45 private const val PRIMARY_ERROR_INT = "primary_error_int"
46 private const val URL_WITH_ERRORS = "url_with_errors"
47 private const val ISSUED_TO_CNAME = "issued_to_cname"
48 private const val ISSUED_TO_ONAME = "issued_to_oname"
49 private const val ISSUED_TO_UNAME = "issued_to_uname"
50 private const val ISSUED_BY_CNAME = "issued_by_cname"
51 private const val ISSUED_BY_ONAME = "issued_by_oname"
52 private const val ISSUED_BY_UNAME = "issued_by_uname"
53 private const val START_DATE = "start_date"
54 private const val END_DATE = "end_date"
55 private const val WEBVIEW_FRAGMENT_ID = "webview_fragment_id"
56
57 class SslCertificateErrorDialog : DialogFragment() {
58     companion object {
59         fun displayDialog(sslError: SslError, webViewFragmentId: Long): SslCertificateErrorDialog {
60             // Get the various components of the SSL error message.
61             val primaryErrorInt = sslError.primaryError
62             val urlWithErrors = sslError.url
63             val sslCertificate = sslError.certificate
64             val issuedToCName = sslCertificate.issuedTo.cName
65             val issuedToOName = sslCertificate.issuedTo.oName
66             val issuedToUName = sslCertificate.issuedTo.uName
67             val issuedByCName = sslCertificate.issuedBy.cName
68             val issuedByOName = sslCertificate.issuedBy.oName
69             val issuedByUName = sslCertificate.issuedBy.uName
70             val startDate = sslCertificate.validNotBeforeDate
71             val endDate = sslCertificate.validNotAfterDate
72
73             // Create an arguments bundle.
74             val argumentsBundle = Bundle()
75
76             // Store the SSL error message components in the bundle.
77             argumentsBundle.putInt(PRIMARY_ERROR_INT, primaryErrorInt)
78             argumentsBundle.putString(URL_WITH_ERRORS, urlWithErrors)
79             argumentsBundle.putString(ISSUED_TO_CNAME, issuedToCName)
80             argumentsBundle.putString(ISSUED_TO_ONAME, issuedToOName)
81             argumentsBundle.putString(ISSUED_TO_UNAME, issuedToUName)
82             argumentsBundle.putString(ISSUED_BY_CNAME, issuedByCName)
83             argumentsBundle.putString(ISSUED_BY_ONAME, issuedByOName)
84             argumentsBundle.putString(ISSUED_BY_UNAME, issuedByUName)
85             argumentsBundle.putString(START_DATE, DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(startDate))
86             argumentsBundle.putString(END_DATE, DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(endDate))
87             argumentsBundle.putLong(WEBVIEW_FRAGMENT_ID, webViewFragmentId)
88
89             // Create a new instance of the SSL certificate error dialog.
90             val thisSslCertificateErrorDialog = SslCertificateErrorDialog()
91
92             // Add the arguments bundle to the new dialog.
93             thisSslCertificateErrorDialog.arguments = argumentsBundle
94
95             // Return the new dialog.
96             return thisSslCertificateErrorDialog
97         }
98     }
99
100     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
101         // Get the variables from the bundle.
102         val primaryErrorInt = requireArguments().getInt(PRIMARY_ERROR_INT)
103         val urlWithErrors = requireArguments().getString(URL_WITH_ERRORS)
104         val issuedToCName = requireArguments().getString(ISSUED_TO_CNAME)
105         val issuedToOName = requireArguments().getString(ISSUED_TO_ONAME)
106         val issuedToUName = requireArguments().getString(ISSUED_TO_UNAME)
107         val issuedByCName = requireArguments().getString(ISSUED_BY_CNAME)
108         val issuedByOName = requireArguments().getString(ISSUED_BY_ONAME)
109         val issuedByUName = requireArguments().getString(ISSUED_BY_UNAME)
110         val startDate = requireArguments().getString(START_DATE)
111         val endDate = requireArguments().getString(END_DATE)
112         val webViewFragmentId = requireArguments().getLong(WEBVIEW_FRAGMENT_ID)
113
114         // Get the current position of this WebView fragment.
115         val webViewPosition = MainWebViewActivity.webViewStateAdapter!!.getPositionForId(webViewFragmentId)
116
117         // Get the WebView tab fragment.
118         val webViewTabFragment = MainWebViewActivity.webViewStateAdapter!!.getPageFragment(webViewPosition)
119
120         // Get the fragment view.
121         val fragmentView = webViewTabFragment.requireView()
122
123         // Get a handle for the current WebView.
124         val nestedScrollWebView: NestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview)
125
126         // Get a handle for the SSL error handler.
127         val sslErrorHandler = nestedScrollWebView.sslErrorHandler
128
129         // Use an alert dialog builder to create the alert dialog.
130         val dialogBuilder = AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog)
131
132         // Set the icon.
133         dialogBuilder.setIcon(R.drawable.ssl_certificate)
134
135         // Set the title.
136         dialogBuilder.setTitle(R.string.ssl_certificate_error)
137
138         // Set the view.
139         dialogBuilder.setView(R.layout.ssl_certificate_error)
140
141         // Set the cancel button listener.
142         dialogBuilder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int ->
143             // Check to make sure the SSL error handler is not null.  This might happen if multiple dialogs are displayed at once.
144             if (sslErrorHandler != null) {
145                 // Cancel the request.
146                 sslErrorHandler.cancel()
147
148                 // Reset the SSL error handler.
149                 nestedScrollWebView.resetSslErrorHandler()
150             }
151         }
152
153         // Set the proceed button listener.
154         dialogBuilder.setPositiveButton(R.string.proceed) { _: DialogInterface?, _: Int ->
155             // Check to make sure the SSL error handler is not null.  This might happen if multiple dialogs are displayed at once.
156             if (sslErrorHandler != null) {
157                 // Proceed to the website.
158                 sslErrorHandler.proceed()
159
160                 // Reset the SSL error handler.
161                 nestedScrollWebView.resetSslErrorHandler()
162             }
163         }
164
165         // Create an alert dialog from the builder.
166         val alertDialog = dialogBuilder.create()
167
168         // Get a handle for the shared preferences.
169         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
170
171         // Get the screenshot preference.
172         val allowScreenshots = sharedPreferences.getBoolean(getString(R.string.allow_screenshots_key), false)
173
174         // Disable screenshots if not allowed.
175         if (!allowScreenshots) {
176             // Disable screenshots.
177             alertDialog.window!!.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
178         }
179
180         // Get a URI for the URL with errors.
181         val uriWithErrors = Uri.parse(urlWithErrors)
182
183         // The alert dialog must be shown before the contents can be modified.
184         alertDialog.show()
185
186         // Get handles for the views.
187         val primaryErrorTextView = alertDialog.findViewById<TextView>(R.id.primary_error)!!
188         val urlTextView = alertDialog.findViewById<TextView>(R.id.url)!!
189         val ipAddressesTextView = alertDialog.findViewById<TextView>(R.id.ip_addresses)!!
190         val issuedToCNameTextView = alertDialog.findViewById<TextView>(R.id.issued_to_cname)!!
191         val issuedToONameTextView = alertDialog.findViewById<TextView>(R.id.issued_to_oname)!!
192         val issuedToUNameTextView = alertDialog.findViewById<TextView>(R.id.issued_to_uname)!!
193         val issuedByTextView = alertDialog.findViewById<TextView>(R.id.issued_by_textview)!!
194         val issuedByCNameTextView = alertDialog.findViewById<TextView>(R.id.issued_by_cname)!!
195         val issuedByONameTextView = alertDialog.findViewById<TextView>(R.id.issued_by_oname)!!
196         val issuedByUNameTextView = alertDialog.findViewById<TextView>(R.id.issued_by_uname)!!
197         val validDatesTextView = alertDialog.findViewById<TextView>(R.id.valid_dates_textview)!!
198         val startDateTextView = alertDialog.findViewById<TextView>(R.id.start_date)!!
199         val endDateTextView = alertDialog.findViewById<TextView>(R.id.end_date)!!
200
201         // Define the color spans.
202         val blueColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.alt_blue_text))
203         val redColorSpan = ForegroundColorSpan(requireContext().getColor(R.color.red_text))
204
205         // Get the IP Addresses for the URI.
206         GetHostIpAddressesCoroutine.getAddresses(uriWithErrors.host!!, getString(R.string.ip_addresses), blueColorSpan, ipAddressesTextView)
207
208         // Setup the common strings.
209         val urlLabel = getString(R.string.url_label)
210         val cNameLabel = getString(R.string.common_name)
211         val oNameLabel = getString(R.string.organization)
212         val uNameLabel = getString(R.string.organizational_unit)
213         val startDateLabel = getString(R.string.start_date)
214         val endDateLabel = getString(R.string.end_date)
215
216         // Create a spannable string builder for each text view that needs multiple colors of text.
217         val urlStringBuilder = SpannableStringBuilder(urlLabel + urlWithErrors)
218         val issuedToCNameStringBuilder = SpannableStringBuilder(cNameLabel + issuedToCName)
219         val issuedToONameStringBuilder = SpannableStringBuilder(oNameLabel + issuedToOName)
220         val issuedToUNameStringBuilder = SpannableStringBuilder(uNameLabel + issuedToUName)
221         val issuedByCNameStringBuilder = SpannableStringBuilder(cNameLabel + issuedByCName)
222         val issuedByONameStringBuilder = SpannableStringBuilder(oNameLabel + issuedByOName)
223         val issuedByUNameStringBuilder = SpannableStringBuilder(uNameLabel + issuedByUName)
224         val startDateStringBuilder = SpannableStringBuilder(startDateLabel + startDate)
225         val endDateStringBuilder = SpannableStringBuilder(endDateLabel + endDate)
226
227         // Setup the spans to display the certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
228         urlStringBuilder.setSpan(blueColorSpan, urlLabel.length, urlStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
229         issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
230         issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedToONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
231         issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedToUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
232         issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
233         issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
234         issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
235         startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
236         endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
237
238         // Define the primary error string.
239         var primaryErrorString = ""
240
241         // Highlight the primary error in red and store it in the primary error string.
242         when (primaryErrorInt) {
243             SslError.SSL_IDMISMATCH -> {
244                 // Change the URL span colors to red.
245                 urlStringBuilder.setSpan(redColorSpan, urlLabel.length, urlStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
246                 issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedToCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
247
248                 // Store the primary error string.
249                 primaryErrorString = getString(R.string.cn_mismatch)
250             }
251
252             SslError.SSL_UNTRUSTED -> {
253                 // Change the issued by text view text to red.
254                 issuedByTextView.setTextColor(requireContext().getColor(R.color.red_text))
255
256                 // Change the issued by span color to red.
257                 issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length, issuedByCNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
258                 issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length, issuedByONameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
259                 issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length, issuedByUNameStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
260
261                 // Store the primary error string.
262                 primaryErrorString = getString(R.string.untrusted)
263             }
264
265             SslError.SSL_DATE_INVALID -> {
266                 // Change the valid dates text view text to red.
267                 validDatesTextView.setTextColor(requireContext().getColor(R.color.red_text))
268
269                 // Change the date span colors to red.
270                 startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
271                 endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
272
273                 // Store the primary error string.
274                 primaryErrorString = getString(R.string.invalid_date)
275             }
276
277             SslError.SSL_NOTYETVALID -> {
278                 // Change the start date span color to red.
279                 startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length, startDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
280
281                 // Store the primary error string.
282                 primaryErrorString = getString(R.string.future_certificate)
283             }
284
285             SslError.SSL_EXPIRED -> {
286                 // Change the end date span color to red.
287                 endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length, endDateStringBuilder.length, Spanned.SPAN_INCLUSIVE_INCLUSIVE)
288
289                 // Store the primary error string.
290                 primaryErrorString = getString(R.string.expired_certificate)
291             }
292
293             SslError.SSL_INVALID ->
294                 // Store the primary error string.
295                 primaryErrorString = getString(R.string.invalid_certificate)
296         }
297
298         // Display the strings.
299         primaryErrorTextView.text = primaryErrorString
300         urlTextView.text = urlStringBuilder
301         issuedToCNameTextView.text = issuedToCNameStringBuilder
302         issuedToONameTextView.text = issuedToONameStringBuilder
303         issuedToUNameTextView.text = issuedToUNameStringBuilder
304         issuedByCNameTextView.text = issuedByCNameStringBuilder
305         issuedByONameTextView.text = issuedByONameStringBuilder
306         issuedByUNameTextView.text = issuedByUNameStringBuilder
307         startDateTextView.text = startDateStringBuilder
308         endDateTextView.text = endDateStringBuilder
309
310         // Return the alert dialog.
311         return alertDialog
312     }
313 }