]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/commitdiff
Handle HTTP authentication. https://redmine.stoutner.com/issues/898
authorSoren Stoutner <soren@stoutner.com>
Mon, 11 Mar 2024 22:03:14 +0000 (15:03 -0700)
committerSoren Stoutner <soren@stoutner.com>
Mon, 11 Mar 2024 22:03:14 +0000 (15:03 -0700)
src/CMakeLists.txt
src/dialogs/AddBookmarkDialog.cpp
src/dialogs/CMakeLists.txt
src/dialogs/HttpAuthenticationDialog.cpp [new file with mode: 0644]
src/dialogs/HttpAuthenticationDialog.h [new file with mode: 0644]
src/uis/HttpAuthenticationDialog.ui [new file with mode: 0644]
src/widgets/PrivacyWebEngineView.cpp
src/widgets/PrivacyWebEngineView.h
src/widgets/UrlLineEdit.cpp

index 16bf76c362d862ddde3aeb60e6eb335efd5c6e0b..837e42a9b952fbff1f06278774cc9b3d641a16b3 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 2022-2023 Soren Stoutner <soren@stoutner.com>.
+# Copyright 2022-2024 Soren Stoutner <soren@stoutner.com>.
 #
 # This file is part of Privacy Browser PC <https://www.stoutner.com/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
index e35759b0929393e36b9305d46a1bcf23890ee93a..041ca3d33df3db53b761d1ab9717c18936783b06 100644 (file)
@@ -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.
index 32ac52343234e9c25b3218b75ce29669cc37a31e..94b84aa406a509b02a9f82ab75dd7bd4d1beeb49 100644 (file)
@@ -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 (file)
index 0000000..650ce99
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2024 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+// Application headers.
+#include "HttpAuthenticationDialog.h"
+#include "ui_HttpAuthenticationDialog.h"
+
+// Qt toolkit headers.
+#include <QPushButton>
+#include <QUrl>
+
+// 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 (file)
index 0000000..201067d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2024 Soren Stoutner <soren@stoutner.com>.
+ *
+ * This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HTTPAUTHENTICATIONDIALOG_H
+#define HTTPAUTHENTICATIONDIALOG_H
+
+// KDE framework headers.
+#include <KLineEdit>
+#include <KPasswordLineEdit>
+
+// Qt toolkit headers.
+#include <QAuthenticator>
+#include <QDialog>
+
+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 (file)
index 0000000..d5d9de0
--- /dev/null
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Copyright 2024 Soren Stoutner <soren@stoutner.com>.
+
+  This file is part of Privacy Browser PC <https://www.stoutner.com/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 <http://www.gnu.org/licenses/>. -->
+
+<ui version="4.0">
+    <class>HttpAuthenticationDialog</class>
+
+    <widget class="QWidget">
+        <!-- Dialog body. -->
+        <layout class="QVBoxLayout">
+            <item>
+                <widget class="QLabel" name="realmLabel">
+                    <property name="font">
+                        <font>
+                            <pointsize>24</pointsize>
+                            <bold>true</bold>
+                        </font>
+                    </property>
+
+                    <property name="alignment">
+                        <set>Qt::AlignCenter</set>
+                    </property>
+                </widget>
+            </item>
+
+            <item>
+                <!-- Host. -->
+                <layout class="QFormLayout">
+                    <property name="formAlignment">
+                        <set>Qt::AlignHCenter|Qt::AlignTop</set>
+                    </property>
+
+                    <item row="0" column="0">
+                        <widget class="QLabel">
+                            <property name="text">
+                                <string>Host:</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item row="0" column ="1">
+                        <widget class="QLabel" name="hostLabel" />
+                    </item>
+                </layout>
+            </item>
+
+            <item>
+                <layout class="QFormLayout">
+                    <!-- Username. -->
+                    <item row="0" column="0">
+                        <widget class="QLabel">
+                            <property name="text">
+                                <string>Username</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item row="0" column="1">
+                        <widget class="KLineEdit" name="usernameLineEdit">
+                            <property name="minimumSize">
+                                <size>
+                                    <width>300</width>
+                                    <height>0</height>
+                                </size>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <!-- Password. -->
+                    <item row="1" column="0">
+                        <widget class="QLabel">
+                            <property name="text">
+                                <string>Password</string>
+                            </property>
+                        </widget>
+                    </item>
+
+                    <item row="1" column="1">
+                        <widget class="KPasswordLineEdit" name="passwordLineEdit" />
+                    </item>
+                </layout>
+            </item>
+
+            <!-- Spacer. -->
+            <item>
+                <spacer>
+                    <property name="orientation">
+                        <enum>Qt::Vertical</enum>
+                    </property>
+                </spacer>
+            </item>
+
+            <!-- Buttons. -->
+            <item>
+                <widget class="QDialogButtonBox" name="dialogButtonBox">
+                    <property name="standardButtons">
+                        <set>QDialogButtonBox::Ok | QDialogButtonBox::Cancel</set>
+                    </property>
+                </widget>
+            </item>
+        </layout>
+    </widget>
+</ui>
index 05c765925d8aa6da639c4f4f019634ffc3d956f5..6080288e4f5b4238404e8e13795e305f2fd9c65e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022-2023 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2022-2024 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
  *
 // 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();
index 171ca5e6397e4ae4daabeb77c9009a802bc63503..4f621ce86097009c2b680d668285f35c1b3b69d3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022-2023 Soren Stoutner <soren@stoutner.com>.
+ * Copyright 2022-2024 Soren Stoutner <soren@stoutner.com>.
  *
  * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc>.
  *
@@ -20,6 +20,9 @@
 #ifndef PRIVACYWEBENGINEVIEW_H
 #define PRIVACYWEBENGINEVIEW_H
 
+// KDE framework headers.
+#include <KLineEdit>
+
 // Qt toolkit headers.
 #include <QIcon>
 #include <QNetworkCookie>
@@ -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;
 
index a06cf973b6f434f0b6d5fc1c65a8df6084e9fe6b..04de23bbe74711d015191e82c1c3567217fde2b1 100644 (file)
@@ -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<QInputMethodEvent::Attribute> 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<QInputMethodEvent::Attribute> 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);