File indexing completed on 2024-05-19 05:01:19

0001 /*
0002     This file is part of the KDE project.
0003 
0004     SPDX-FileCopyrightText: 2021 Stefano Crocco <stefano.crocco@alice.it>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "certificateerrordialogmanager.h"
0010 #include "webenginepage.h"
0011 #include "webenginepartcertificateerrordlg.h"
0012 #include "webengineview.h"
0013 
0014 #include <algorithm>
0015 
0016 
0017 #include <KSharedConfig>
0018 #include <KConfigGroup>
0019 
0020 using namespace KonqWebEnginePart;
0021 
0022 CertificateErrorDialogManager::CertificateErrorDialogManager(QObject *parent) : QObject(parent)
0023 {
0024 }
0025 
0026 CertificateErrorDialogManager::~CertificateErrorDialogManager()
0027 {
0028 }
0029 
0030 bool CertificateErrorDialogManager::handleCertificateError(const QWebEngineCertificateError& _ce, WebEnginePage* page)
0031 {
0032     QWebEngineCertificateError ce(_ce);
0033     if (!ce.isOverridable()) {
0034         ce.rejectCertificate();
0035         return false;
0036     }
0037     bool ignore = userAlreadyChoseToIgnoreError(ce);
0038     if (ignore) {
0039 #if QT_VERSION_MAJOR < 6
0040         ce.ignoreCertificateError();
0041 #else
0042         ce.acceptCertificate();
0043 #endif
0044     } else {
0045         ce.defer();
0046         QPointer<WebEnginePage> ptr(page);
0047         CertificateErrorData data{ce, ptr};
0048         if (!displayDialogIfPossible(data)) {
0049             m_certificates.append(data);
0050         }
0051     }
0052     return true;
0053 }
0054 
0055 bool CertificateErrorDialogManager::userAlreadyChoseToIgnoreError(const QWebEngineCertificateError &ce)
0056 {
0057 #if QT_VERSION_MAJOR < 6
0058     int error = static_cast<int>(ce.error());
0059 #else
0060     int error = static_cast<int>(ce.type());
0061 #endif
0062     QString url = ce.url().url();
0063     KConfigGroup grp(KSharedConfig::openConfig(), "CertificateExceptions");
0064     QList<int> exceptionsForUrl = grp.readEntry(url, QList<int>{});
0065     return (exceptionsForUrl.contains(error));
0066 }
0067 
0068 QWidget* CertificateErrorDialogManager::windowForPage(WebEnginePage* page)
0069 {
0070     if (page) {
0071         QWidget *view = page->view();
0072         if (view) {
0073             return view->window();
0074         }
0075     }
0076     return nullptr;
0077 }
0078 
0079 bool CertificateErrorDialogManager::displayDialogIfPossible(const CertificateErrorDialogManager::CertificateErrorData& data)
0080 {
0081     QWidget *window = windowForPage(data.page);
0082     if (m_dialogs.contains(window)) {
0083         return false;
0084     } else {
0085         displayDialog(data, window);
0086         return true;
0087     }
0088 }
0089 
0090 void CertificateErrorDialogManager::displayDialog(const CertificateErrorDialogManager::CertificateErrorData& data, QWidget *window)
0091 {
0092     if (!window) {
0093         window = windowForPage(data.page);
0094     }
0095     Q_ASSERT(!m_dialogs.contains(window));
0096 
0097     WebEnginePartCertificateErrorDlg *dlg = new WebEnginePartCertificateErrorDlg(data.error, data.page, window);
0098     connect(dlg, &WebEnginePartCertificateErrorDlg::finished, this, [this, dlg](int){
0099         applyUserChoice(dlg);});
0100     connect(dlg, &WebEnginePartCertificateErrorDlg::destroyed, this, &CertificateErrorDialogManager::removeDestroyedDialog);
0101     connect(window, &QWidget::destroyed, this, &CertificateErrorDialogManager::removeDestroyedWindow);
0102     m_dialogs.insert(window, dlg);
0103     dlg->open();
0104 }
0105 
0106 void CertificateErrorDialogManager::displayNextDialog(QWidget *window)
0107 {
0108     if (!window) {
0109         return;
0110     }
0111     auto findNext = [window](const CertificateErrorData &data) {
0112         return windowForPage(data.page) == window;
0113     };
0114     auto it = std::find_if(m_certificates.begin(), m_certificates.end(), findNext);
0115     if (it == m_certificates.end()) {
0116         return;
0117     }
0118     displayDialog(*it, window);
0119     m_certificates.erase(it);
0120 }
0121 
0122 void CertificateErrorDialogManager::applyUserChoice(WebEnginePartCertificateErrorDlg *dlg)
0123 {
0124     QWebEngineCertificateError error = dlg->certificateError();
0125     WebEnginePartCertificateErrorDlg::UserChoice choice = dlg->userChoice();
0126     if (choice == WebEnginePartCertificateErrorDlg::UserChoice::DontIgnoreError) {
0127         error.rejectCertificate();
0128     } else {
0129 #if QT_VERSION_MAJOR < 6
0130         error.ignoreCertificateError();
0131 #else
0132         error.acceptCertificate();
0133 #endif
0134         if (choice == WebEnginePartCertificateErrorDlg::UserChoice::IgnoreErrorForever) {
0135             recordIgnoreForeverChoice(error);
0136         }
0137     }
0138     dlg->deleteLater();
0139 }
0140 
0141 void CertificateErrorDialogManager::removeDestroyedDialog(QObject *dlg)
0142 {
0143     auto findItemForDialog = [dlg](const std::pair<QObject*, QObject*>& pair){return pair.second == dlg;};
0144     auto it = std::find_if(m_dialogs.constKeyValueBegin(), m_dialogs.constKeyValueEnd(), findItemForDialog);
0145     if (it == m_dialogs.constKeyValueEnd()) {
0146         return;
0147     }
0148     QWidget *window = qobject_cast<QWidget*>(it->first);
0149     m_dialogs.remove(it->first);
0150     if (window) {
0151         disconnect(window, nullptr, this, nullptr);
0152         displayNextDialog(window);
0153     }
0154 }
0155 
0156 void CertificateErrorDialogManager::removeDestroyedWindow(QObject *window)
0157 {
0158     if (!window) {
0159         return;
0160     }
0161     m_dialogs.remove(window);
0162 }
0163 
0164 void CertificateErrorDialogManager::recordIgnoreForeverChoice(const QWebEngineCertificateError& ce)
0165 {
0166     KConfigGroup grp(KSharedConfig::openConfig(), "CertificateExceptions");
0167     QString url = ce.url().url();
0168 #if QT_VERSION_MAJOR < 6
0169     int error = ce.error();
0170 #else
0171     int error = ce.type();
0172 #endif
0173     QList<int> exceptionsForUrl = grp.readEntry(url, QList<int>{});
0174     exceptionsForUrl.append(error);
0175     grp.writeEntry(url, exceptionsForUrl);
0176     grp.sync();
0177 }
0178 
0179 
0180