From 3ea5ede1fd0721bea6813f36388ba6387bdbfcfe Mon Sep 17 00:00:00 2001 From: Soren Stoutner Date: Mon, 11 Mar 2024 15:03:14 -0700 Subject: [PATCH] Handle HTTP authentication. https://redmine.stoutner.com/issues/898 --- src/CMakeLists.txt | 3 +- src/dialogs/AddBookmarkDialog.cpp | 2 +- src/dialogs/CMakeLists.txt | 1 + src/dialogs/HttpAuthenticationDialog.cpp | 91 +++++++++++++++++ src/dialogs/HttpAuthenticationDialog.h | 54 ++++++++++ src/uis/HttpAuthenticationDialog.ui | 119 +++++++++++++++++++++++ src/widgets/PrivacyWebEngineView.cpp | 16 ++- src/widgets/PrivacyWebEngineView.h | 8 +- src/widgets/UrlLineEdit.cpp | 72 +++++++------- 9 files changed, 328 insertions(+), 38 deletions(-) create mode 100644 src/dialogs/HttpAuthenticationDialog.cpp create mode 100644 src/dialogs/HttpAuthenticationDialog.h create mode 100644 src/uis/HttpAuthenticationDialog.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 16bf76c..837e42a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2022-2023 Soren Stoutner . +# Copyright 2022-2024 Soren Stoutner . # # This file is part of Privacy Browser PC . # @@ -45,6 +45,7 @@ ki18n_wrap_ui(privacybrowser uis/DurableCookiesDialog.ui uis/EditBookmarkDialog.ui uis/EditFolderDialog.ui + uis/HttpAuthenticationDialog.ui uis/SaveDialog.ui uis/SettingsGeneral.ui uis/SettingsPrivacy.ui diff --git a/src/dialogs/AddBookmarkDialog.cpp b/src/dialogs/AddBookmarkDialog.cpp index e35759b..041ca3d 100644 --- a/src/dialogs/AddBookmarkDialog.cpp +++ b/src/dialogs/AddBookmarkDialog.cpp @@ -169,7 +169,7 @@ void AddBookmarkDialog::browse() void AddBookmarkDialog::updateUi() { - // Determine if both line edits are populated. + // Update the add button status if (bookmarkNameLineEditPointer->text().isEmpty() || bookmarkUrlLineEditPointer->text().isEmpty()) // At least one of the line edits is empty. { // Disable the add button. diff --git a/src/dialogs/CMakeLists.txt b/src/dialogs/CMakeLists.txt index 32ac523..94b84aa 100644 --- a/src/dialogs/CMakeLists.txt +++ b/src/dialogs/CMakeLists.txt @@ -27,6 +27,7 @@ target_sources(privacybrowser PRIVATE DurableCookiesDialog.cpp EditBookmarkDialog.cpp EditFolderDialog.cpp + HttpAuthenticationDialog.cpp SaveDialog.cpp SettingsDialog.cpp ) diff --git a/src/dialogs/HttpAuthenticationDialog.cpp b/src/dialogs/HttpAuthenticationDialog.cpp new file mode 100644 index 0000000..650ce99 --- /dev/null +++ b/src/dialogs/HttpAuthenticationDialog.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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 PC 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 PC. If not, see . + */ + +// Application headers. +#include "HttpAuthenticationDialog.h" +#include "ui_HttpAuthenticationDialog.h" + +// Qt toolkit headers. +#include +#include + +// Construct the class. +HttpAuthenticationDialog::HttpAuthenticationDialog(QWidget *parentWidgetPointer, const QUrl &requestUrl, QAuthenticator *authenticatorPointer) : + QDialog(parentWidgetPointer), authenticatorPointer(authenticatorPointer) +{ + // Set the window title. + setWindowTitle(i18nc("The HTTP authentication dialog window title.", "HTTP Authentication")); + + // Set the window modality. + setWindowModality(Qt::WindowModality::ApplicationModal); + + // Instantiate the HTTP authentication dialog UI. + Ui::HttpAuthenticationDialog httpAuthenticationDialogUi; + + // Setup the UI. + httpAuthenticationDialogUi.setupUi(this); + + // Get handles for the widgets. + QLabel *realmLabelPointer = httpAuthenticationDialogUi.realmLabel; + QLabel *hostLabelPointer = httpAuthenticationDialogUi.hostLabel; + usernameLineEditPointer = httpAuthenticationDialogUi.usernameLineEdit; + passwordLineEditPointer = httpAuthenticationDialogUi.passwordLineEdit; + QDialogButtonBox *dialogButtonBoxPointer = httpAuthenticationDialogUi.dialogButtonBox; + okButtonPointer = dialogButtonBoxPointer->button(QDialogButtonBox::Ok); + + // Display the labels. + realmLabelPointer->setText(authenticatorPointer->realm()); + hostLabelPointer->setText(requestUrl.host()); + + // Connect the buttons. + connect(dialogButtonBoxPointer, SIGNAL(accepted()), this, SLOT(authenticate())); + connect(dialogButtonBoxPointer, SIGNAL(rejected()), this, SLOT(reject())); + + // Update the UI when the line edits change. + connect(usernameLineEditPointer, SIGNAL(textEdited(const QString&)), this, SLOT(updateUi())); + connect(passwordLineEditPointer, SIGNAL(passwordChanged(const QString&)), this, SLOT(updateUi())); + + // Initially disable the OK button. + okButtonPointer->setEnabled(false); +} + +void HttpAuthenticationDialog::authenticate() +{ + // Populate the authenticator. + authenticatorPointer->setUser(usernameLineEditPointer->text()); + authenticatorPointer->setPassword(passwordLineEditPointer->password()); + + // Close the dialog. + close(); +} + +void HttpAuthenticationDialog::updateUi() +{ + // Update the OK button status + if (usernameLineEditPointer->text().isEmpty() || passwordLineEditPointer->password().isEmpty()) // At least one of the line edits is empty. + { + // Disable the OK button. + okButtonPointer->setEnabled(false); + } + else // Both of the line edits are populated. + { + // Enable the OK button. + okButtonPointer->setEnabled(true); + } +} diff --git a/src/dialogs/HttpAuthenticationDialog.h b/src/dialogs/HttpAuthenticationDialog.h new file mode 100644 index 0000000..201067d --- /dev/null +++ b/src/dialogs/HttpAuthenticationDialog.h @@ -0,0 +1,54 @@ +/* + * Copyright 2024 Soren Stoutner . + * + * This file is part of Privacy Browser PC . + * + * Privacy Browser PC 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 PC 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 PC. If not, see . + */ + +#ifndef HTTPAUTHENTICATIONDIALOG_H +#define HTTPAUTHENTICATIONDIALOG_H + +// KDE framework headers. +#include +#include + +// Qt toolkit headers. +#include +#include + +class HttpAuthenticationDialog : public QDialog +{ + // Include the Q_OBJECT macro. + Q_OBJECT + +public: + // The primary constructor. + explicit HttpAuthenticationDialog(QWidget *parentWidgetPointer, const QUrl &requestUrl, QAuthenticator *authenticatorPointer); + +private Q_SLOTS: + // The private slots. + void authenticate(); + void updateUi(); + +private: + // The private variables. + QAuthenticator *authenticatorPointer; + + // The private widgets. + QPushButton *okButtonPointer; + KPasswordLineEdit *passwordLineEditPointer; + KLineEdit *usernameLineEditPointer; +}; +#endif diff --git a/src/uis/HttpAuthenticationDialog.ui b/src/uis/HttpAuthenticationDialog.ui new file mode 100644 index 0000000..d5d9de0 --- /dev/null +++ b/src/uis/HttpAuthenticationDialog.ui @@ -0,0 +1,119 @@ + + + + + + HttpAuthenticationDialog + + + + + + + + + 24 + true + + + + + Qt::AlignCenter + + + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + Host: + + + + + + + + + + + + + + + + + Username + + + + + + + + + 300 + 0 + + + + + + + + + + Password + + + + + + + + + + + + + + + Qt::Vertical + + + + + + + + + QDialogButtonBox::Ok | QDialogButtonBox::Cancel + + + + + + diff --git a/src/widgets/PrivacyWebEngineView.cpp b/src/widgets/PrivacyWebEngineView.cpp index 05c7659..6080288 100644 --- a/src/widgets/PrivacyWebEngineView.cpp +++ b/src/widgets/PrivacyWebEngineView.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -20,8 +20,10 @@ // Application headers. #include "PrivacyWebEngineView.h" #include "Settings.h" +#include "ui_HttpAuthenticationDialog.h" #include "databases/CookiesDatabase.h" #include "databases/DomainsDatabase.h" +#include "dialogs/HttpAuthenticationDialog.h" #include "interceptors/UrlRequestInterceptor.h" #include "windows/BrowserWindow.h" @@ -55,6 +57,9 @@ PrivacyWebEngineView::PrivacyWebEngineView(QWidget *parentWidgetPointer) : QWebE // Display HTTP Ping blocked dialogs. connect(urlRequestInterceptorPointer, SIGNAL(displayHttpPingDialog(const QString&)), this, SLOT(displayHttpPingDialog(const QString&))); + + // Handle HTTP authentication requests. + connect(webEnginePagePointer, SIGNAL(authenticationRequired(const QUrl&, QAuthenticator*)), this, SLOT(handleAuthenticationRequest(const QUrl&, QAuthenticator*))); } void PrivacyWebEngineView::addCookieToList(const QNetworkCookie &cookie) const @@ -298,6 +303,15 @@ void PrivacyWebEngineView::displayHttpPingDialog(const QString &httpPingUrl) con emit displayHttpPingBlockedDialog(httpPingUrl); } +void PrivacyWebEngineView::handleAuthenticationRequest(const QUrl &requestUrl, QAuthenticator *authenticatorPointer) +{ + // Instantiate an HTTP authentication dialog. + HttpAuthenticationDialog *httpAuthenticationDialogPointer = new HttpAuthenticationDialog(parentWidget(), requestUrl, authenticatorPointer); + + // Display the dialog. This must be `exec()` instead of `show()` so that the website doesn't proceed before populating the authentication pointer. + httpAuthenticationDialogPointer->exec(); +} + void PrivacyWebEngineView::removeCookieFromList(const QNetworkCookie &cookie) const { //qDebug() << "Remove cookie: " << cookie.toRawForm(); diff --git a/src/widgets/PrivacyWebEngineView.h b/src/widgets/PrivacyWebEngineView.h index 171ca5e..4f621ce 100644 --- a/src/widgets/PrivacyWebEngineView.h +++ b/src/widgets/PrivacyWebEngineView.h @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 Soren Stoutner . + * Copyright 2022-2024 Soren Stoutner . * * This file is part of Privacy Browser PC . * @@ -20,6 +20,9 @@ #ifndef PRIVACYWEBENGINEVIEW_H #define PRIVACYWEBENGINEVIEW_H +// KDE framework headers. +#include + // Qt toolkit headers. #include #include @@ -65,9 +68,12 @@ private Q_SLOTS: // The private slots. void applyDomainSettingsWithoutReloading(const QString &hostname); void displayHttpPingDialog(const QString &httpPingUrl) const; + void handleAuthenticationRequest(const QUrl &requestUrl, QAuthenticator *authenticatorPointer); private: // The private variables. + KLineEdit *passwordLineEditPointer; + KLineEdit *usernameLineEditPointer; QWebEngineProfile *webEngineProfilePointer; QWebEngineSettings *webEngineSettingsPointer; diff --git a/src/widgets/UrlLineEdit.cpp b/src/widgets/UrlLineEdit.cpp index a06cf97..04de23b 100644 --- a/src/widgets/UrlLineEdit.cpp +++ b/src/widgets/UrlLineEdit.cpp @@ -55,53 +55,57 @@ void UrlLineEdit::setText(const QString &urlString) // Wipe the currently displayed text. KLineEdit::setText(QLatin1String("")); - // Create an input method event attribute list. - QList inputMethodEventAttributeList; + // Only highlight the URL if it starts with a known schema. + if (urlString.startsWith(QLatin1String("https://")) || urlString.startsWith(QLatin1String("http://")) || urlString.startsWith(QLatin1String("view-source:"))) + { + // Create an input method event attribute list. + QList inputMethodEventAttributeList; - // Calculate the URL string length. - int urlStringLength = urlString.length(); + // Calculate the URL string length. + int urlStringLength = urlString.length(); - // Get the index of end of the schema. - int endOfSchemaIndex = urlString.indexOf(QLatin1String("//")) + 2; + // Get the index of end of the schema. + int endOfSchemaIndex = urlString.indexOf(QLatin1String("//")) + 2; - // Get the index of the last `.` in the domain. - int lastDotIndex = urlString.lastIndexOf(QLatin1Char('.')); + // Get the index of the `/` immediately after the domain name. If this doesn't exit it will be set to `-1`. + int endOfDomainNameIndex = urlString.indexOf(QLatin1Char('/'), endOfSchemaIndex); - // Get the index of the penultimate `.` in the domain. If this doesn't exist it will be set to `-1`. - int penultimateDotIndex = urlString.lastIndexOf(QLatin1Char('.'), lastDotIndex - urlStringLength - 1); + // Get the index of the last `.` in the domain. + int lastDotIndex = urlString.lastIndexOf(QLatin1Char('.'), endOfDomainNameIndex - urlStringLength); - // Get the index of the `/` immediately after the domain name. If this doesn't exit it will be set to `-1`. - int endOfDomainNameIndex = urlString.indexOf(QLatin1Char('/'), endOfSchemaIndex); + // Get the index of the penultimate `.` in the domain. If this doesn't exist it will be set to `-1`. + int penultimateDotIndex = urlString.lastIndexOf(QLatin1Char('.'), lastDotIndex - urlStringLength - 1); - // Create the text character formats. - QTextCharFormat grayTextCharFormat; - QTextCharFormat schemaTextCharFormat; + // Create the text character formats. + QTextCharFormat grayTextCharFormat; + QTextCharFormat schemaTextCharFormat; - // Set the text character format colors. - grayTextCharFormat.setForeground(Qt::darkGray); + // Set the text character format colors. + grayTextCharFormat.setForeground(Qt::darkGray); - // Set the schema text character format color. - if (urlString.startsWith(QLatin1String("http://")) || urlString.startsWith(QLatin1String("view-source:http://"))) - schemaTextCharFormat.setForeground(Qt::red); - else - schemaTextCharFormat = grayTextCharFormat; + // Set the schema text character format color. + if (urlString.startsWith(QLatin1String("http://")) || urlString.startsWith(QLatin1String("view-source:http://"))) + schemaTextCharFormat.setForeground(Qt::red); + else + schemaTextCharFormat = grayTextCharFormat; - // Append the schema text format format. - inputMethodEventAttributeList.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, endOfSchemaIndex, schemaTextCharFormat)); + // Append the schema text format format. + inputMethodEventAttributeList.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, endOfSchemaIndex, schemaTextCharFormat)); - // Append the subdomain format if the subdomain exists. If it doesn't, the penultimate dot index will be `-1`. - if (penultimateDotIndex > 0) - inputMethodEventAttributeList.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, endOfSchemaIndex, penultimateDotIndex + 1 - endOfSchemaIndex, grayTextCharFormat)); + // Append the subdomain format if the subdomain exists. If it doesn't, the penultimate dot index will be `-1`. + if (penultimateDotIndex > 0) + inputMethodEventAttributeList.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, endOfSchemaIndex, penultimateDotIndex + 1 - endOfSchemaIndex, grayTextCharFormat)); - // Append the post domain formatting if text exists after the domain name. If it doesn't, the end of domain name index will be `-1`. - if (endOfDomainNameIndex > 0) - inputMethodEventAttributeList.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, endOfDomainNameIndex, urlStringLength - endOfDomainNameIndex, grayTextCharFormat)); + // Append the post domain formatting if text exists after the domain name. If it doesn't, the end of domain name index will be `-1`. + if (endOfDomainNameIndex > 0) + inputMethodEventAttributeList.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, endOfDomainNameIndex, urlStringLength - endOfDomainNameIndex, grayTextCharFormat)); - // Create an input method event with an empty pre-edit string. - QInputMethodEvent inputMethodEvent(QString(""), inputMethodEventAttributeList); + // Create an input method event with an empty pre-edit string. + QInputMethodEvent inputMethodEvent(QString(""), inputMethodEventAttributeList); - // Apply the URL highlighting. - event(&inputMethodEvent); + // Apply the URL highlighting. + event(&inputMethodEvent); + } // Run the default commands. KLineEdit::setText(urlString); -- 2.45.2