]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blobdiff - src/helpers/FilterListHelper.cpp
Partial filter list implementation.
[PrivacyBrowserPC.git] / src / helpers / FilterListHelper.cpp
diff --git a/src/helpers/FilterListHelper.cpp b/src/helpers/FilterListHelper.cpp
new file mode 100644 (file)
index 0000000..e49af5b
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * 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 "FilterListHelper.h"
+
+// Qt toolkit headers.
+#include <QDebug>
+#include <QFile>
+#include <QTextStream>
+
+// KDE Framework headers.
+#include <KLocalizedString>
+
+// Construct the class.
+FilterListHelper::FilterListHelper()
+{
+    // Populate the translated disposition strings.  Translated entries cannot be public static const.
+    DEFAULT_STRING = i18nc("Default disposition", "Default - Allowed");
+    ALLOWED_STRING = i18nc("Allowed disposition", "Allowed");
+    BLOCKED_STRING = i18nc("Blocked disposition", "Blocked");
+
+    // Populate the translated navigation type strings.  Translated entries cannot be public static const.
+    NAVIGATION_TYPE_LINK = i18nc("Navigation type link", "Link");
+    NAVIGATION_TYPE_TYPED = i18nc("Navigation type typed", "Typed");
+    NAVIGATION_TYPE_FORM_SUBMITTED = i18nc("Navigation type form submitted", "Form Submitted");
+    NAVIGATION_TYPE_BACK_FORWARD = i18nc("Navigation type back/forward", "Back/Forward");
+    NAVIGATION_TYPE_RELOAD = i18nc("Navigation type reload", "Reload");
+    NAVIGATION_TYPE_REDIRECT = i18nc("Navigation type redirect", "Redirect");
+    NAVIGATION_TYPE_OTHER = i18nc("Navigation type other", "Other");
+
+    // Populate the translated resource type strings.  Translated entries cannot be public static const.
+    RESOURCE_TYPE_MAIN_FRAME = i18nc("Resource type main frame", "Main Frame");
+    RESOURCE_TYPE_SUB_FRAME = i18nc("Resource type sub frame", "Sub Frame");
+    RESOURCE_TYPE_STYLESHEET = i18nc("Resource type stylesheet", "Stylesheet");
+    RESOURCE_TYPE_SCRIPT = i18nc("Resource type script", "Script");
+    RESOURCE_TYPE_IMAGE = i18nc("Resource type image", "Image");
+    RESOURCE_TYPE_FONT_RESOURCE = i18nc("Resource type font", "Font");
+    RESOURCE_TYPE_SUB_RESOURCE = i18nc("Resource type sub resource", "Sub Resource");
+    RESOURCE_TYPE_OBJECT = i18nc("Resource type object", "Object");
+    RESOURCE_TYPE_MEDIA = i18nc("Resource type media", "Media");
+    RESOURCE_TYPE_WORKER = i18nc("Resource type worker", "Worker");
+    RESOURCE_TYPE_SHARED_WORKER = i18nc("Resource type shared worker", "Shared Worker");
+    RESOURCE_TYPE_PREFETCH = i18nc("Resource type prefetch", "Prefetch");
+    RESOURCE_TYPE_FAVICON = i18nc("Resource type favicon", "Favicon");
+    RESOURCE_TYPE_XHR = i18nc("Resource type XML HTTP request", "XML HTTP Request");
+    RESOURCE_TYPE_PING = i18nc("Resource type HTTP ping", "HTTP Ping");
+    RESOURCE_TYPE_SERVICE_WORKER = i18nc("Resource type service worker", "Service Worker");
+    RESOURCE_TYPE_CSP_REPORT = i18nc("Resource type content security policy report", "Content Security Policy Report");
+    RESOURCE_TYPE_PLUGIN_RESOURCE = i18nc("Resource type plugin request", "Plugin Request");
+    RESOURCE_TYPE_NAVIGATION_PRELOAD_MAIN_FRAME = i18nc("Resource type preload main frame", "Preload Main Frame");
+    RESOURCE_TYPE_NAVIGATION_PRELOAD_SUB_FRAME = i18nc("Resource type preload sub frame", "Preload Sub Frame");
+    RESOURCE_TYPE_UNKNOWN = i18nc("Resource type unknown", "Unknown");
+
+    // Populate the translated sublist strings.  Translated entries cannot be public static const.
+    MAIN_BLOCKLIST_STRING = i18nc("Main blocklist sublist", "Main Block List");
+
+    // Populate the filter lists.
+    ultraListStructPointer = populateFilterList(QLatin1String(":/filterlists/ultralist.txt"));
+    ultraPrivacyStructPointer = populateFilterList(QLatin1String(":/filterlists/ultraprivacy.txt"));
+    easyListStructPointer = populateFilterList(QLatin1String(":/filterlists/easylist.txt"));
+    easyPrivacyStructPointer = populateFilterList(QLatin1String(":/filterlists/easyprivacy.txt"));
+    fanboyAnnoyanceStructPointer = populateFilterList(QLatin1String(":/filterlists/fanboy-annoyance.txt"));
+}
+
+bool FilterListHelper::checkFilterLists(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer) const
+{
+    // Initiate a status tracker.  If the tracker changes to false, all process of the request will be stopped.
+    bool status = true;
+
+    // Check UltraList.
+    status = checkIndividualList(urlRequestInfo, requestStructPointer, ultraListStructPointer);
+
+    // check UltraPrivacy if the status is still true.
+    if (status) {
+        status = checkIndividualList(urlRequestInfo, requestStructPointer, ultraPrivacyStructPointer);
+    }
+
+    // Return the status.
+    return status;
+}
+
+bool FilterListHelper::checkIndividualList(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, FilterListStruct *filterListStruct) const
+{
+    // Get the request URL.
+    QUrl url = urlRequestInfo.requestUrl();
+
+    // Get the request URL string.
+    QString urlString = url.toString();
+
+    // Check the main block list.
+    for (auto filterListEntry = filterListStruct->mainBlockList.begin(); filterListEntry != filterListStruct->mainBlockList.end(); ++filterListEntry) {
+        // Get the entry struct.
+        EntryStruct *entryStructPointer = *filterListEntry;
+
+        // Check if the URL string contains the applied entry
+        if (urlString.contains(entryStructPointer->appliedEntry)) {
+            // Block the request.
+            urlRequestInfo.block(true);
+
+            // Populate the request struct.
+            populateRequestStruct(requestStructPointer, BLOCKED, filterListStruct->title, MAIN_BLOCKLIST, entryStructPointer->appliedEntry, entryStructPointer->originalEntry);
+
+            // Log the block.
+            //qDebug().noquote().nospace() << "Blocked request:  " << urlString << ",  Filter list entry:  " << entryStructPointer->appliedEntry;
+
+            // Returning `false` stops all processing of the request.
+            return false;
+        }
+    }
+
+    // Return `true` to continue processing the URL request.
+    return true;
+}
+
+QString FilterListHelper::getDispositionString(int dispositionInt) const
+{
+    // Return the translated disposition string.
+    switch (dispositionInt)
+    {
+        case ALLOWED: return ALLOWED_STRING;
+        case BLOCKED: return BLOCKED_STRING;
+        default: return DEFAULT_STRING;
+    }
+}
+
+QString FilterListHelper::getNavigationTypeString(int navigationTypeInt) const
+{
+    // Return the translated navigation type string.
+    switch (navigationTypeInt)
+    {
+        case QWebEngineUrlRequestInfo::NavigationTypeLink: return NAVIGATION_TYPE_LINK;
+        case QWebEngineUrlRequestInfo::NavigationTypeTyped: return NAVIGATION_TYPE_TYPED;
+        case QWebEngineUrlRequestInfo::NavigationTypeFormSubmitted: return NAVIGATION_TYPE_FORM_SUBMITTED;
+        case QWebEngineUrlRequestInfo::NavigationTypeBackForward: return NAVIGATION_TYPE_BACK_FORWARD;
+        case QWebEngineUrlRequestInfo::NavigationTypeReload: return NAVIGATION_TYPE_RELOAD;
+        case QWebEngineUrlRequestInfo::NavigationTypeRedirect: return NAVIGATION_TYPE_REDIRECT;
+        default: return NAVIGATION_TYPE_OTHER;
+    }
+}
+
+QString FilterListHelper::getResourceTypeString(int resourceTypeInt) const
+{
+    // Return the translated resource type string.
+    switch (resourceTypeInt)
+    {
+        case QWebEngineUrlRequestInfo::ResourceTypeMainFrame: return RESOURCE_TYPE_MAIN_FRAME;
+        case QWebEngineUrlRequestInfo::ResourceTypeSubFrame: return RESOURCE_TYPE_SUB_FRAME;
+        case QWebEngineUrlRequestInfo::ResourceTypeStylesheet: return RESOURCE_TYPE_STYLESHEET;
+        case QWebEngineUrlRequestInfo::ResourceTypeScript: return RESOURCE_TYPE_SCRIPT;
+        case QWebEngineUrlRequestInfo::ResourceTypeImage: return RESOURCE_TYPE_IMAGE;
+        case QWebEngineUrlRequestInfo::ResourceTypeFontResource: return RESOURCE_TYPE_FONT_RESOURCE;
+        case QWebEngineUrlRequestInfo::ResourceTypeSubResource: return RESOURCE_TYPE_SUB_RESOURCE;
+        case QWebEngineUrlRequestInfo::ResourceTypeObject: return RESOURCE_TYPE_OBJECT;
+        case QWebEngineUrlRequestInfo::ResourceTypeMedia: return RESOURCE_TYPE_MEDIA;
+        case QWebEngineUrlRequestInfo::ResourceTypeWorker: return RESOURCE_TYPE_WORKER;
+        case QWebEngineUrlRequestInfo::ResourceTypeSharedWorker: return RESOURCE_TYPE_SHARED_WORKER;
+        case QWebEngineUrlRequestInfo::ResourceTypePrefetch: return RESOURCE_TYPE_PREFETCH;
+        case QWebEngineUrlRequestInfo::ResourceTypeFavicon: return RESOURCE_TYPE_FAVICON;
+        case QWebEngineUrlRequestInfo::ResourceTypeXhr: return RESOURCE_TYPE_XHR;
+        case QWebEngineUrlRequestInfo::ResourceTypePing: return RESOURCE_TYPE_PING;
+        case QWebEngineUrlRequestInfo::ResourceTypeServiceWorker: return RESOURCE_TYPE_SERVICE_WORKER;
+        case QWebEngineUrlRequestInfo::ResourceTypeCspReport: return RESOURCE_TYPE_CSP_REPORT;
+        case QWebEngineUrlRequestInfo::ResourceTypePluginResource: return RESOURCE_TYPE_PLUGIN_RESOURCE;
+        case QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadMainFrame: return RESOURCE_TYPE_NAVIGATION_PRELOAD_MAIN_FRAME;
+        case QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadSubFrame: return RESOURCE_TYPE_NAVIGATION_PRELOAD_SUB_FRAME;
+        default: return RESOURCE_TYPE_UNKNOWN;
+    }
+}
+
+QString FilterListHelper::getSublistName(int sublistInt) const
+{
+    // Return the name of the requested sublist.
+    switch (sublistInt)
+    {
+        case MAIN_BLOCKLIST: return MAIN_BLOCKLIST_STRING;
+        default: return QString();  // The default return should never be reached.
+    }
+}
+
+FilterListStruct* FilterListHelper::populateFilterList(const QString &filterListFileName) const
+{
+    // Get the filter list file.
+    QFile filterListFile(filterListFileName);
+
+    // Open the filter list file.
+    filterListFile.open(QIODevice::ReadOnly);
+
+    // Create a filter list text stream.
+    QTextStream filterListTextStream(&filterListFile);
+
+    // Create a filter list struct.
+    FilterListStruct *filterListStructPointer = new FilterListStruct;
+
+    // Populate the filter list file name.
+    filterListStructPointer->filePath = filterListFileName;
+
+    // Create a filter list string.
+    QString filterListString;
+
+    // Process each line of the filter list.
+    while (filterListTextStream.readLineInto(&filterListString)) {
+        // Create an entry struct.
+        EntryStruct *entryStructPointer = new EntryStruct;
+
+        // Store the original entry.
+        entryStructPointer->originalEntry = filterListString;
+
+        // Process the entry.
+        if (filterListString.startsWith(QLatin1Char('['))) {  // The line starts with `[`, which is the file format.
+            // Do nothing.
+
+            // Log the dropping of the line.
+            //qDebug().noquote().nospace() << filterListString << " NOT added from " << filterListFileName;
+        } else if (filterListString.startsWith(QLatin1Char('!'))) {  // The line starts with `!`, which are comments.
+            if (filterListString.startsWith(QLatin1String("! Title: ")))  // The line contains the title.
+            {
+                // Add the title to the filter list struct.
+                filterListStructPointer->title = filterListString.remove(0, 9);
+
+                // Log the addition of the filter list title.
+                //qDebug().noquote().nospace() << "Filter list title:  " << filterListString << " added from " << filterListFileName;
+            }
+            else if (filterListString.startsWith(QLatin1String("! Version: ")))  // The line contains the version.
+            {
+                // Add the version to the filter list struct.
+                filterListStructPointer->version = filterListString.remove(0, 11);
+
+                // Log the addition of the filter list version.
+                //qDebug().noquote().nospace() << "Filter list version:  " << filterListString << " added from " << filterListFileName;
+            }
+
+            // Else do nothing.
+
+            // Log the dropping of the line.
+            //qDebug().noquote().nospace() << originalFilterListString << " NOT added from " << filterListFileName;
+        } else {  // Process the entry.
+            // Add the applied entry to the struct.
+            entryStructPointer->appliedEntry = filterListString;
+
+            // Add the filter list entry struct to the main block list.
+            filterListStructPointer->mainBlockList.push_front(entryStructPointer);
+
+            // Log the addition to the filter list.
+            //qDebug().noquote().nospace() << originalFilterListString << " added from " << filterListFileName;
+        }
+    }
+
+    // Close the filter list file.
+    filterListFile.close();
+
+    // Return the filter list pair.
+    return filterListStructPointer;
+}
+
+void FilterListHelper::populateRequestStruct(RequestStruct *requestStructPointer, const int disposition, const QString &filterListTitle, const int sublistInt, const QString &appliedEntry,
+                                             const QString &originalEntry) const
+{
+    // Populate the request struct.
+    requestStructPointer->dispositionInt = disposition;
+    requestStructPointer->filterListTitle = filterListTitle;
+    requestStructPointer->sublistInt = sublistInt;
+    requestStructPointer->entryStruct.appliedEntry = appliedEntry;
+    requestStructPointer->entryStruct.originalEntry = originalEntry;
+}