]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blobdiff - src/helpers/FilterListHelper.cpp
Finish block list implementation.
[PrivacyBrowserPC.git] / src / helpers / FilterListHelper.cpp
index e49af5b20e5f2d7a09a4fa0e517f2b888d31c65a..e4ce25f529d3a2cbce839fa7fcac435b6eaaa055 100644 (file)
 
 // Application headers.
 #include "FilterListHelper.h"
+#include "structs/OverrideStruct.h"
+
+// KDE Framework headers.
+#include <KLocalizedString>
 
 // Qt toolkit headers.
 #include <QDebug>
 #include <QFile>
+#include <QRegularExpression>
 #include <QTextStream>
 
-// KDE Framework headers.
-#include <KLocalizedString>
-
 // Construct the class.
 FilterListHelper::FilterListHelper()
 {
@@ -36,6 +38,11 @@ FilterListHelper::FilterListHelper()
     ALLOWED_STRING = i18nc("Allowed disposition", "Allowed");
     BLOCKED_STRING = i18nc("Blocked disposition", "Blocked");
 
+    // Populate the translated filter option disposition strings.  Translated entries cannot be public static const.
+    FILTER_OPTION_NULL = QString();
+    FILTER_OPTION_APPLY = i18nc("Apply filter option", "Apply");
+    FILTER_OPTION_OVERRIDE = i18nc("Override filter option", "Override");
+
     // 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");
@@ -69,66 +76,663 @@ FilterListHelper::FilterListHelper()
     RESOURCE_TYPE_UNKNOWN = i18nc("Resource type unknown", "Unknown");
 
     // Populate the translated sublist strings.  Translated entries cannot be public static const.
+    MAIN_ALLOWLIST_STRING = i18nc("Main allowlist sublist", "Main Allow List");
     MAIN_BLOCKLIST_STRING = i18nc("Main blocklist sublist", "Main Block List");
+    INITIAL_DOMAIN_BLOCKLIST_STRING = i18nc("Initial domain blocklist string", "Initial Domain Block List");
+    REGULAR_EXPRESSION_BLOCKLIST_STRING = i18nc("Regular expression blocklist string", "Regular Expression Block List");
 
     // Populate the filter lists.
-    ultraListStructPointer = populateFilterList(QLatin1String(":/filterlists/ultralist.txt"));
     ultraPrivacyStructPointer = populateFilterList(QLatin1String(":/filterlists/ultraprivacy.txt"));
-    easyListStructPointer = populateFilterList(QLatin1String(":/filterlists/easylist.txt"));
+    ultraListStructPointer = populateFilterList(QLatin1String(":/filterlists/ultralist.txt"));
     easyPrivacyStructPointer = populateFilterList(QLatin1String(":/filterlists/easyprivacy.txt"));
+    easyListStructPointer = populateFilterList(QLatin1String(":/filterlists/easylist.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;
+    // Initiate a continue checking tracker.  If the tracker changes to false, all processing of the request will be stopped.
+    bool continueChecking = true;
+
+    // Create a URL struct.
+    UrlStruct urlStruct;
+
+    // Get the URLs.
+    QUrl firstPartyUrl = urlRequestInfo.firstPartyUrl();
+    QUrl requestUrl = urlRequestInfo.requestUrl();
+
+    // Get the hosts.
+    QString firstPartyHost = firstPartyUrl.host();
+    urlStruct.fqdn = requestUrl.host();
+
+    // Determine if this is a third-party request.
+    urlStruct.isThirdPartyRequest = (firstPartyHost != urlStruct.fqdn);
+
+    // Get the request URL string.
+    urlStruct.urlString = requestUrl.toString();
+
+    // Create a URL string with separators.
+    urlStruct.urlStringWithSeparators = urlStruct.urlString;
+
+    // Replace the separators characters with `^`.
+    urlStruct.urlStringWithSeparators.replace(QLatin1Char(':'), QLatin1Char('^'));
+    urlStruct.urlStringWithSeparators.replace(QLatin1Char('/'), QLatin1Char('^'));
+    urlStruct.urlStringWithSeparators.replace(QLatin1Char('?'), QLatin1Char('^'));
+    urlStruct.urlStringWithSeparators.replace(QLatin1Char('='), QLatin1Char('^'));
+    urlStruct.urlStringWithSeparators.replace(QLatin1Char('&'), QLatin1Char('^'));
+
+    // Add a `^` to the end of the string it it doesn't already contain one.
+    if (!urlStruct.urlStringWithSeparators.endsWith(QLatin1Char('^')))
+        urlStruct.urlStringWithSeparators.append(QLatin1Char('^'));
+
+    // Create truncated URL strings and initially populate it with the original URL strings.
+    urlStruct.truncatedUrlString = urlStruct.urlString;
+    urlStruct.truncatedUrlStringWithSeparators = urlStruct.urlStringWithSeparators;
+
+    // Get the index of the beginning of the fully qualified domain name.
+    int fqdnIndex = urlStruct.truncatedUrlString.indexOf(QLatin1String("://")) + 3;
+
+    // Truncate the URL to the beginning of the fully qualified domain name.
+    urlStruct.truncatedUrlString.remove(0, fqdnIndex);
+    urlStruct.truncatedUrlStringWithSeparators.remove(0, fqdnIndex);
 
     // Check UltraList.
-    status = checkIndividualList(urlRequestInfo, requestStructPointer, ultraListStructPointer);
+    continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, ultraPrivacyStructPointer);
 
-    // check UltraPrivacy if the status is still true.
-    if (status) {
-        status = checkIndividualList(urlRequestInfo, requestStructPointer, ultraPrivacyStructPointer);
-    }
+    // Check UltraPrivacy.
+    if (continueChecking)
+        continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, ultraListStructPointer);
 
-    // Return the status.
-    return status;
+    // Check EasyPrivacy.
+    if (continueChecking)
+        continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, easyPrivacyStructPointer);
+
+    // Check EasyList.
+    if (continueChecking)
+        continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, easyListStructPointer);
+
+    // Check Fanboy's Annoyance list.
+    if (continueChecking)
+        continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, fanboyAnnoyanceStructPointer);
+
+    // Return the continue checking status.
+    return continueChecking;
 }
 
-bool FilterListHelper::checkIndividualList(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, FilterListStruct *filterListStruct) const
+bool FilterListHelper::checkIndividualList(QWebEngineUrlRequestInfo &urlRequestInfo, UrlStruct &urlStruct, RequestStruct *requestStructPointer, FilterListStruct *filterListStructPointer) const
 {
-    // Get the request URL.
-    QUrl url = urlRequestInfo.requestUrl();
+    // Initiate a continue checking tracker.  If the tracker changes to false, all process of the request will be stopped.
+    bool continueChecking = true;
 
-    // Get the request URL string.
-    QString urlString = url.toString();
+    // Check the main allow list.
+    for (auto filterListEntry = filterListStructPointer->mainAllowListPointer->begin(); filterListEntry != filterListStructPointer->mainAllowListPointer->end(); ++filterListEntry)
+    {
+        // Get the entry struct.
+        EntryStruct *entryStructPointer = *filterListEntry;
+
+        // TODO.  Temporarily ignore empty applied entries.
+        if (!entryStructPointer->appliedEntryList[0].isEmpty())
+        {
+            // Check if the URL string contains the applied entry.  TODO.
+            if (urlStruct.urlString.contains(entryStructPointer->appliedEntryList[0]) || urlStruct.urlStringWithSeparators.contains(entryStructPointer->appliedEntryList[0]))
+            {
+                // Allow the request.
+                urlRequestInfo.block(false);
+
+                // Populate the request struct.
+                populateRequestStruct(requestStructPointer, ALLOWED, filterListStructPointer->title, MAIN_ALLOWLIST, entryStructPointer);
+
+                // Log the allow.
+                //qDebug().noquote().nospace() << "Allowed request:  " << urlStruct.urlString << ", Filter list entry:  " << entryStructPointer->appliedEntry;
+
+                // Returning `false` stops all processing of the request.
+                return false;
+            }
+        }
+    }
+
+    // Get the main block list end.
+    auto mainBlockListEnd = filterListStructPointer->mainBlockListPointer->end();
 
     // Check the main block list.
-    for (auto filterListEntry = filterListStruct->mainBlockList.begin(); filterListEntry != filterListStruct->mainBlockList.end(); ++filterListEntry) {
+    for (auto mainBlockListEntry = filterListStructPointer->mainBlockListPointer->begin(); mainBlockListEntry != mainBlockListEnd; ++mainBlockListEntry)
+    {
+        // Exit the loop if continue checking is false.
+        if (!continueChecking)
+            break;
+
         // Get the entry struct.
-        EntryStruct *entryStructPointer = *filterListEntry;
+        EntryStruct *entryStructPointer = *mainBlockListEntry;
 
-        // Check if the URL string contains the applied entry
-        if (urlString.contains(entryStructPointer->appliedEntry)) {
-            // Block the request.
-            urlRequestInfo.block(true);
+        // Check the applied entries.
+        continueChecking = checkAppliedEntry(urlRequestInfo, urlStruct, requestStructPointer, filterListStructPointer->title, MAIN_BLOCKLIST, entryStructPointer, urlStruct.urlString,
+                                                urlStruct.urlStringWithSeparators);
+    }
 
-            // Populate the request struct.
-            populateRequestStruct(requestStructPointer, BLOCKED, filterListStruct->title, MAIN_BLOCKLIST, entryStructPointer->appliedEntry, entryStructPointer->originalEntry);
+    // Get the initial domain block list end.
+    auto initialDomainBlockListEnd = filterListStructPointer->initialDomainBlockListPointer->end();
 
-            // Log the block.
-            //qDebug().noquote().nospace() << "Blocked request:  " << urlString << ",  Filter list entry:  " << entryStructPointer->appliedEntry;
+    // Check the initial domain block list.
+    for (auto initialDomainBlockListEntry = filterListStructPointer->initialDomainBlockListPointer->begin(); initialDomainBlockListEntry != initialDomainBlockListEnd;
+         ++initialDomainBlockListEntry)
+    {
+        // Exit the loop if continue checking is false.
+        if (!continueChecking)
+            break;
+
+        // Get the entry struct.
+        EntryStruct *entryStructPointer = *initialDomainBlockListEntry;
+
+        // Check the applied entries.
+        continueChecking = checkAppliedEntry(urlRequestInfo, urlStruct, requestStructPointer, filterListStructPointer->title, INITIAL_DOMAIN_BLOCKLIST, entryStructPointer,
+                                             urlStruct.truncatedUrlString, urlStruct.truncatedUrlStringWithSeparators);
+    }
+
+    // Get the regular expression block list end.
+    auto regularExpressionBlockListEnd = filterListStructPointer->regularExpressionBlockListPointer->end();
+
+    // Check the regular expression block list.
+    for (auto regularExpressionBlockListEntry = filterListStructPointer->regularExpressionBlockListPointer->begin(); regularExpressionBlockListEntry != regularExpressionBlockListEnd;
+         ++regularExpressionBlockListEntry)
+    {
+        // Exit the loop if continue checking is false.
+        if (!continueChecking)
+            break;
+
+        // Get the entry struct.
+        EntryStruct *entryStructPointer = *regularExpressionBlockListEntry;
+
+        // Check the applied entries.
+        continueChecking = checkRegularExpression(urlRequestInfo, urlStruct, requestStructPointer, filterListStructPointer->title, REGULAR_EXPRESSION_BLOCKLIST, entryStructPointer);
+    }
 
-            // Returning `false` stops all processing of the request.
-            return false;
+    // Return the continue checking status.
+    return continueChecking;
+}
+
+bool FilterListHelper::checkAppliedEntry(QWebEngineUrlRequestInfo &urlRequestInfo, UrlStruct &urlStruct, RequestStruct *requestStructPointer, const QString &filterListTitle,
+                                         const int sublistInt, EntryStruct *entryStructPointer, QString &urlString, QString &urlStringWithSeparators) const
+{
+    // Check the entries according to the number.
+    if (entryStructPointer->singleAppliedEntry)
+    {
+        // Process initial and final matches.
+        if (entryStructPointer->initialMatch && entryStructPointer->finalMatch)  // This is both an initial and final match.
+        {
+            // Check the URL against the applied entry.
+            if ((urlString == entryStructPointer->appliedEntryList[0]) || (urlStringWithSeparators == entryStructPointer->appliedEntryList[0]))
+            {
+                // Check the domain status.
+                return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+            }
+        }
+        else if (entryStructPointer->initialMatch)  // This is an initial match.
+        {
+            // Check the URL against the applied entry.
+            if (urlString.startsWith(entryStructPointer->appliedEntryList[0]) || urlStringWithSeparators.startsWith(entryStructPointer->appliedEntryList[0]))
+            {
+                // Check the domain status.
+                return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+            }
+        }
+        else if (entryStructPointer->finalMatch)  // This is a final match.
+        {
+            // Check the URL against the applied entry.
+            if (urlString.endsWith(entryStructPointer->appliedEntryList[0]) || urlStringWithSeparators.endsWith(entryStructPointer->appliedEntryList[0]))
+            {
+                // Check the domain status.
+                return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+            }
+        }
+        else  // There is no initial or final matching.
+        {
+            // Check if the URL string contains the applied entry.
+            if (urlString.contains(entryStructPointer->appliedEntryList[0]) || urlStringWithSeparators.contains(entryStructPointer->appliedEntryList[0]))
+            {
+                // Check the domain status.
+                return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+            }
         }
     }
+    else  // There are multiple entries.
+    {
+        // Create a URL matches flag.
+        bool urlMatches = true;
+
+        // Process initial and final matches.
+        if (entryStructPointer->initialMatch && entryStructPointer->finalMatch)  // This is both an initial and final match.
+        {
+            // Check the first entry.
+            if (urlString.startsWith(entryStructPointer->appliedEntryList[0]) ||
+                urlStringWithSeparators.startsWith(entryStructPointer->appliedEntryList[0]))  // The URL string starts with the first applied entry.
+            {
+                // Get the number of characters to remove from the front of the URL strings.
+                int charactersToRemove = entryStructPointer->appliedEntryList[0].size();
 
-    // Return `true` to continue processing the URL request.
+                // Remove the entry from the front of the URL string copies.
+                urlString.remove(0, charactersToRemove);
+                urlStringWithSeparators.remove(0, charactersToRemove);
+            }
+            else  // The URL string does not end with the last applied entry.
+            {
+                // Mark the URL matches flag as false.
+                urlMatches = false;
+            }
+
+            // Check the other entries if the URL still matches.
+            if (urlMatches)
+            {
+                // Calculate the penultimate entry.
+                int penultimateEntryNumber = (entryStructPointer->sizeOfAppliedEntryList - 1);
+                int ultimateEntryIndex = penultimateEntryNumber;
+
+                // Check all the middle entries.
+                for (int i = 1; i < penultimateEntryNumber; ++i)
+                {
+                    // Get the index of the applied entry, which will be `-1` if it doesn't exist.
+                    int stringIndex = urlString.indexOf(entryStructPointer->appliedEntryList[i]);
+                    int stringWithSeparatorsIndex = urlStringWithSeparators.indexOf(entryStructPointer->appliedEntryList[i]);
+
+                    // Get the larger of the two indexes.
+                    int index = std::max(stringIndex, stringWithSeparatorsIndex);
+
+                    // Check if the entry was found.
+                    if (index >= 0)  // The entry is contained in the URL string.
+                    {
+                        // Get the number of characters to remove from the front of the URL strings.
+                        int charactersToRemove = index + entryStructPointer->appliedEntryList[i].size();
+
+                        // Remove the entry from the front of the URL string copies.
+                        urlString.remove(0, charactersToRemove);
+                        urlStringWithSeparators.remove(0, charactersToRemove);
+                    }
+                    else  // The entry is not contained in the URL string.
+                    {
+                        // Mark the URL matches flag as false.
+                        urlMatches = false;
+                    }
+                }
+
+                // Check the final entry if the URL still matches.
+                if (urlMatches)
+                {
+                    if (urlString.endsWith(entryStructPointer->appliedEntryList[ultimateEntryIndex]) ||
+                        urlStringWithSeparators.endsWith(entryStructPointer->appliedEntryList[ultimateEntryIndex]))  // The URL string ends with the last applied entry.
+                    {
+                        // There is no need to modify the URL string copies as no further checks will be performed.
+                    }
+                    else  // The URL string does not end with the last applied entry.
+                    {
+                        // Mark the URL matches flag as false.
+                        urlMatches = false;
+                    }
+                }
+            }
+        }
+        else if (entryStructPointer->initialMatch)  // This is an initial match.
+        {
+            // Check the first entry.
+            if (urlString.startsWith(entryStructPointer->appliedEntryList[0]) ||
+                urlStringWithSeparators.startsWith(entryStructPointer->appliedEntryList[0]))  // The URL string starts with the first applied entry.
+            {
+                // Get the number of characters to remove from the front of the URL strings.
+                int charactersToRemove = entryStructPointer->appliedEntryList[0].size();
+
+                // Remove the entry from the front of the URL string copies.
+                urlString.remove(0, charactersToRemove);
+                urlStringWithSeparators.remove(0, charactersToRemove);
+            }
+            else  // The URL string does not end with the last applied entry.
+            {
+                // Mark the URL matches flag as false.
+                urlMatches = false;
+            }
+
+            // Check the other entries if the URL still matches.
+            if (urlMatches)
+            {
+                for (int i = 1; i < entryStructPointer->sizeOfAppliedEntryList; ++i)
+                {
+                    // Get the index of the applied entry, which will be `-1` if it doesn't exist.
+                    int stringIndex = urlString.indexOf(entryStructPointer->appliedEntryList[i]);
+                    int stringWithSeparatorsIndex = urlStringWithSeparators.indexOf(entryStructPointer->appliedEntryList[i]);
+
+                    // Get the larger of the two indexes.
+                    int index = std::max(stringIndex, stringWithSeparatorsIndex);
+
+                    // Check if the entry was found.
+                    if (index >= 0)  // The entry is contained in the URL string.
+                    {
+                        // Get the number of characters to remove from the front of the URL strings.
+                        int charactersToRemove = index + entryStructPointer->appliedEntryList[i].size();
+
+                        // Remove the entry from the front of the URL string copies.
+                        urlString.remove(0, charactersToRemove);
+                        urlStringWithSeparators.remove(0, charactersToRemove);
+                    }
+                    else  // The entry is not contained in the URL string.
+                    {
+                        // Mark the URL matches flag as false.
+                        urlMatches = false;
+                    }
+                }
+            }
+        }
+        else if (entryStructPointer->finalMatch)  // This is a final match.
+        {
+            // Calculate the penultimate entry.
+            int penultimateEntryNumber = (entryStructPointer->sizeOfAppliedEntryList - 1);
+            int ultimateEntryIndex = penultimateEntryNumber;
+
+            // Check all the entries except the last one.
+            for (int i = 0; i < penultimateEntryNumber; ++i)
+            {
+                // Get the index of the applied entry, which will be `-1` if it doesn't exist.
+                int stringIndex = urlString.indexOf(entryStructPointer->appliedEntryList[i]);
+                int stringWithSeparatorsIndex = urlStringWithSeparators.indexOf(entryStructPointer->appliedEntryList[i]);
+
+                // Get the larger of the two indexes.
+                int index = std::max(stringIndex, stringWithSeparatorsIndex);
+
+                // Check if the entry was found.
+                if (index >= 0)  // The entry is contained in the URL string.
+                {
+                    // Get the number of characters to remove from the front of the URL strings.
+                    int charactersToRemove = index + entryStructPointer->appliedEntryList[i].size();
+
+                    // Remove the entry from the front of the URL string copies.
+                    urlString.remove(0, charactersToRemove);
+                    urlStringWithSeparators.remove(0, charactersToRemove);
+                }
+                else  // The entry is not contained in the URL string.
+                {
+                    // Mark the URL matches flag as false.
+                    urlMatches = false;
+                }
+            }
+
+            // Check the final entry if the URL still matches.
+            if (urlMatches)
+            {
+                if (urlString.endsWith(entryStructPointer->appliedEntryList[ultimateEntryIndex]) ||
+                    urlStringWithSeparators.endsWith(entryStructPointer->appliedEntryList[ultimateEntryIndex]))  // The URL string ends with the last applied entry.
+                {
+                    // There is no need to modify the URL string copies as no further checks will be performed.
+                }
+                else  // The URL string does not end with the last applied entry.
+                {
+                    // Mark the URL matches flag as false.
+                    urlMatches = false;
+                }
+            }
+        }
+        else  // There is no initial or final matching.
+        {
+            for (int i = 0; i < entryStructPointer->sizeOfAppliedEntryList; ++i)
+            {
+                // Get the index of the applied entry, which will be `-1` if it doesn't exist.
+                int stringIndex = urlString.indexOf(entryStructPointer->appliedEntryList[i]);
+                int stringWithSeparatorsIndex = urlStringWithSeparators.indexOf(entryStructPointer->appliedEntryList[i]);
+
+                // Get the larger of the two indexes.
+                int index = std::max(stringIndex, stringWithSeparatorsIndex);
+
+                // Check if the entry was found.
+                if (index >= 0)  // The entry is contained in the URL string.
+                {
+
+                    // Get the number of characters to remove from the front of the URL strings.
+                    int charactersToRemove = index + entryStructPointer->appliedEntryList[i].size();
+
+                    // Remove the entry from the front of the URL string copies.
+                    urlString.remove(0, charactersToRemove);
+                    urlStringWithSeparators.remove(0, charactersToRemove);
+                }
+                else  // The entry is not contained in the URL string.
+                {
+                    // Mark the URL matches flag as false.
+                    urlMatches = false;
+                }
+            }
+        }
+
+        // Check the domain status if the URL matches.
+        if (urlMatches)
+            return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // If the applied entry doesn't match, return `true` to continue processing the URL request.
     return true;
 }
 
+bool FilterListHelper::checkRegularExpression(QWebEngineUrlRequestInfo &urlRequestInfo, UrlStruct &urlStruct, RequestStruct *requestStructPointer, const QString &filterListTitle,
+                                              const int sublistInt, EntryStruct *entryStructPointer) const
+{
+    // Create an applied entry regular expression.
+    QRegularExpression appliedEntryRegularExpression(entryStructPointer->appliedEntryList[0]);
+
+    // Check if the regular expression matches the applied entry.
+    if (urlStruct.urlString.contains(appliedEntryRegularExpression))
+    {
+        // Check the domain status.
+        return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // If the regular expression doesn't match, return `true` to continue processing the URL request.
+    return true;
+}
+
+bool FilterListHelper::checkDomain(QWebEngineUrlRequestInfo &urlRequestInfo, UrlStruct &urlStruct, RequestStruct *requestStructPointer, const QString &filterListTitle, const int sublistInt,
+                                   EntryStruct *entryStructPointer) const
+{
+    // Check domain status.
+    if (entryStructPointer->domain == FilterOptionEnum::Disposition::Null)  // Ignore domain status.
+    {
+        // Check the third-party status.
+        return checkThirdParty(urlRequestInfo, requestStructPointer, urlStruct.isThirdPartyRequest, filterListTitle, sublistInt, entryStructPointer);
+    }
+    else if (entryStructPointer->domain == FilterOptionEnum::Disposition::Apply)  // Block requests from listed domains.
+    {
+        // Check each domain.
+        foreach (QString blockedDomain, entryStructPointer->domainList)
+        {
+            // Check if the request came from a blocked domain.
+            if (urlStruct.fqdn.endsWith(blockedDomain))
+            {
+                // Check the third-party status.
+                return checkThirdParty(urlRequestInfo, requestStructPointer, urlStruct.isThirdPartyRequest, filterListTitle, sublistInt, entryStructPointer);
+            }
+        }
+    }
+    else if (entryStructPointer->domain == FilterOptionEnum::Disposition::Override)  // Block domains that are not overridden.
+    {
+        // Create a block domain flag.
+        bool blockDomain = true;
+
+        // Check each overridden domain.
+        foreach (QString overriddenDomain, entryStructPointer->domainList)
+        {
+            // Check if the request came from an overridden domain.
+            if (urlStruct.fqdn.endsWith(overriddenDomain))
+            {
+                // Don't block the domain.
+                blockDomain = false;
+            }
+        }
+
+        // Continue checking if the domain is blocked.
+        if (blockDomain)
+        {
+            // Check the third-party status.
+            return checkThirdParty(urlRequestInfo, requestStructPointer, urlStruct.isThirdPartyRequest, filterListTitle, sublistInt, entryStructPointer);
+        }
+    }
+
+    // There is a domain specified that doesn't match this request.  Return `true` to continue processing the URL request.
+    return true;
+}
+
+bool FilterListHelper::checkThirdParty(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, const bool isThirdPartyRequest, const QString &filterListTitle,
+                                       const int sublistInt, EntryStruct *entryStructPointer) const
+{
+    // Check third-party status.
+    if (entryStructPointer->thirdParty == FilterOptionEnum::Disposition::Null)  // Ignore third-party status.
+    {
+        // Check if request options are applied.
+        if (entryStructPointer->hasRequestOptions)  // Request options are applied.
+        {
+            // Check the request options.
+            return checkRequestOptions(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+        }
+        else  // Request options are not applied.
+        {
+            // Block the request.
+            return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+        }
+    }
+    else if ((entryStructPointer->thirdParty == FilterOptionEnum::Disposition::Apply) && isThirdPartyRequest)  // Block third-party request.
+    {
+        // Check if request options are applied.
+        if (entryStructPointer->hasRequestOptions)  // Request options are applied.
+        {
+            // Check the request options.
+            return checkRequestOptions(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+        }
+        else  // Request options are not applied.
+        {
+            // Block the request.
+            return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+        }
+    }
+    else if ((entryStructPointer->thirdParty == FilterOptionEnum::Disposition::Override) && !isThirdPartyRequest)  // Block first-party requests.
+    {
+        // Check if request options are applied.
+        if (entryStructPointer->hasRequestOptions)  // Request options are applied.
+        {
+            // Check the request options.
+            return checkRequestOptions(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+        }
+        else  // Request options are not applied.
+        {
+            // Block the request.
+            return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+        }
+    }
+
+    // The third-party option specified doesn't match this request.  Return `true` to continue processing the URL request.
+    return true;
+}
+
+bool FilterListHelper::checkRequestOptions(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, const QString &filterListTitle, const int sublistInt,
+                                           EntryStruct *entryStructPointer) const
+{
+    // Block font requests.
+    if ((entryStructPointer->font == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeFontResource))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block image requests.
+    if ((entryStructPointer->image == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeImage))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block main frame requests.
+    if ((entryStructPointer->mainFrame == FilterOptionEnum::Disposition::Apply) && ((requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeMainFrame) ||
+                                                                                    (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadMainFrame)))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block media requests.
+    if ((entryStructPointer->media == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeMedia))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block object requests.
+    if ((entryStructPointer->object == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeObject))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block other requests.
+    if ((entryStructPointer->other == FilterOptionEnum::Disposition::Apply) && ((requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeSubResource) ||
+                                                                                (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeWorker) ||
+                                                                                (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeSharedWorker) ||
+                                                                                (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypePrefetch) ||
+                                                                                (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeFavicon) ||
+                                                                                (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeServiceWorker) ||
+                                                                                (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeCspReport) ||
+                                                                                (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypePluginResource) ||
+                                                                                (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeUnknown)))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block ping requests
+    if ((entryStructPointer->ping == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypePing))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block script requests.
+    if ((entryStructPointer->script == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeScript))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block style sheet requests.
+    if ((entryStructPointer->styleSheet == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeStylesheet))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block sub resource requests.
+    if ((entryStructPointer->subFrame == FilterOptionEnum::Disposition::Apply) && ((requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeSubFrame) ||
+                                                                                   (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadSubFrame)))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // Block XML HTTP requests.
+    if ((entryStructPointer->xmlHttpRequest == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeXhr))
+    {
+        // Block the request.
+        return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
+    }
+
+    // The request options specified don't match this request.  Return `true` to continue processing the URL request.
+    return true;
+}
+
+bool FilterListHelper::blockRequest(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, const QString &filterListTitle, const int sublistInt,
+                                    EntryStruct *entryStructPointer) const
+{
+    // Block the request.
+    urlRequestInfo.block(true);
+
+    // Populate the request struct.
+    populateRequestStruct(requestStructPointer, BLOCKED, filterListTitle, sublistInt, entryStructPointer);
+
+    // Log the block.
+    //qDebug().noquote().nospace() << "Blocked request:  " << urlRequestInfo.firstPartyUrl() << ",  Filter list entry:  " << entryStructPointer->appliedEntry;
+
+    // Returning `false` stops all processing of the request.
+    return false;
+}
+
 QString FilterListHelper::getDispositionString(int dispositionInt) const
 {
     // Return the translated disposition string.
@@ -155,6 +759,17 @@ QString FilterListHelper::getNavigationTypeString(int navigationTypeInt) const
     }
 }
 
+QString FilterListHelper::getRequestOptionDispositionString(const FilterOptionEnum::Disposition filterOptionDisposition) const
+{
+    // Return the translated filter option disposition string.
+    switch (filterOptionDisposition)
+    {
+        case FilterOptionEnum::Disposition::Apply: return FILTER_OPTION_APPLY;
+        case FilterOptionEnum::Disposition::Override: return FILTER_OPTION_OVERRIDE;
+        default: return FILTER_OPTION_NULL;
+    }
+}
+
 QString FilterListHelper::getResourceTypeString(int resourceTypeInt) const
 {
     // Return the translated resource type string.
@@ -189,7 +804,10 @@ QString FilterListHelper::getSublistName(int sublistInt) const
     // Return the name of the requested sublist.
     switch (sublistInt)
     {
+        case MAIN_ALLOWLIST: return MAIN_ALLOWLIST_STRING;
         case MAIN_BLOCKLIST: return MAIN_BLOCKLIST_STRING;
+        case INITIAL_DOMAIN_BLOCKLIST: return INITIAL_DOMAIN_BLOCKLIST_STRING;
+        case REGULAR_EXPRESSION_BLOCKLIST: return REGULAR_EXPRESSION_BLOCKLIST_STRING;
         default: return QString();  // The default return should never be reached.
     }
 }
@@ -223,12 +841,32 @@ FilterListStruct* FilterListHelper::populateFilterList(const QString &filterList
         entryStructPointer->originalEntry = filterListString;
 
         // Process the entry.
-        if (filterListString.startsWith(QLatin1Char('['))) {  // The line starts with `[`, which is the file format.
+        if (filterListString.isEmpty())  // Ignore empty lines.
+        {
+            // Do nothing.
+
+            // Log the dropping of the line.
+            //qDebug().noquote().nospace() << filterListString << " NOT added from " << filterListFileName << " (empty line).";
+        }
+        else 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 << " (file format).";
+        }
+        else if (filterListString.contains(QLatin1String("##")) ||
+                 filterListString.contains(QLatin1String("#?#")) ||
+                 filterListString.contains(QLatin1String("#@#")) ||
+                 filterListString.contains(QLatin1String("#$#")))  // The line contains unimplemented content filtering.
+        {
             // 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.
+            //qDebug().noquote().nospace() << filterListString << " NOT added from " << filterListFileName << " (content filtering).";
+        }
+        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.
@@ -250,15 +888,369 @@ FilterListStruct* FilterListHelper::populateFilterList(const QString &filterList
 
             // 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;
+        }
+        else  // Process the filter options.
+        {
+            // Get the index of the last dollar sign.
+            int indexOfLastDollarSign = filterListString.lastIndexOf(QLatin1Char('$'));
+
+            // Process the filter options if they exist.
+            if (indexOfLastDollarSign > -1)
+            {
+                // Get the filter options.
+                entryStructPointer->originalFilterOptions = filterListString.section(QLatin1Char('$'), -1);
+
+                // Store the entry without the filter options as the filter list string.
+                filterListString.truncate(indexOfLastDollarSign);
+
+                // Split the filter options.
+                QStringList originalFilterOptionsList = entryStructPointer->originalFilterOptions.split(QLatin1Char(','));
+
+                // Create an applied filter options list.
+                QStringList appliedFilterOptionsList;
+
+                // Populate the applied filter options list.
+                foreach (QString filterOption, originalFilterOptionsList)
+                {
+                    // Only add filter options that are handled by Privacy Browser.
+                    if (!(filterOption.startsWith(QLatin1String("csp=")) ||
+                          filterOption.startsWith(QLatin1String("method=")) ||
+                          filterOption.startsWith(QLatin1String("redirect=")) ||
+                          filterOption.startsWith(QLatin1String("rewrite="))))
+                        appliedFilterOptionsList.append(filterOption);
+                }
+
+                // Store the applied filter options list.
+                entryStructPointer->appliedFilterOptionsList = appliedFilterOptionsList;
+
+                // Initialize an override struct.
+                OverrideStruct overrideStruct;
+
+                // Populate the filter options entries.
+                foreach (QString filterOption, appliedFilterOptionsList)
+                {
+                    // Parse the filter options.
+                    if (filterOption.startsWith(QLatin1String("domain=")))  // Domain.
+                    {
+                        // Remove `domain=` from the filter option.
+                        filterOption.remove(0, 7);
+
+                        // Store the domain list.
+                        entryStructPointer->domainList = filterOption.split(QLatin1Char('|'));
+
+                        // Set the disposition.
+                        if (entryStructPointer->domainList[0].startsWith(QLatin1Char('~')))  // Override domains.
+                        {
+                            // Populate the domain filter disposition.
+                            entryStructPointer->domain = FilterOptionEnum::Disposition::Override;
+
+                            // Remove the initial `~` from each domain.
+                            entryStructPointer->domainList.replaceInStrings(QLatin1String("~"), QLatin1String(""));
+                        }
+                        else  // Standard domains.
+                        {
+                            // Populate the domain filter disposition.
+                            entryStructPointer->domain = FilterOptionEnum::Disposition::Apply;
+                        }
+                    }
+                    else if (filterOption == QLatin1String("third-party"))  // Third-party.
+                    {
+                        // Populate the third-party filter disposition.
+                        entryStructPointer->thirdParty = FilterOptionEnum::Disposition::Apply;
+                    }
+                    else if (filterOption == QLatin1String("~third-party"))  // Third-party override.
+                    {
+                        // Populate the third-party filter disposition.
+                        entryStructPointer->thirdParty = FilterOptionEnum::Disposition::Override;
+                    }
+                    else if ((filterOption == QLatin1String("document")) || (filterOption == QLatin1String("popup")))  // Document (and popup).
+                    {
+                        // Populate the main frame disposition.
+                        entryStructPointer->mainFrame = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("font"))  // Font.
+                    {
+                        // Populate the font disposition.
+                        entryStructPointer->font = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("image"))  // Image.
+                    {
+                        // Populate the image disposition.
+                        entryStructPointer->image = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("media"))  // Media.
+                    {
+                        // Populate the media disposition.
+                        entryStructPointer->media = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("object"))  // Object.
+                    {
+                        // Populate the object disposition.
+                        entryStructPointer->object = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if ((filterOption == QLatin1String("other")) || (filterOption == QLatin1String("webrtc")) || (filterOption == QLatin1String("websocket")))  // Other.
+                    {  // `websocket` will get its own section in Qt6.
+                        // Populate the other disposition.
+                        entryStructPointer->other = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("ping"))  // Ping.
+                    {
+                        // Populate the ping disposition.
+                        entryStructPointer->ping = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("script"))  // Script.
+                    {
+                        // Populate the script disposition.
+                        entryStructPointer->script = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("stylesheet"))  // Style sheet.
+                    {
+                        // Populate the script disposition.
+                        entryStructPointer->styleSheet = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("subdocument"))  // Sub document.
+                    {
+                        // Populate the sub resource disposition.
+                        entryStructPointer->subFrame = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("xmlhttprequest"))  // XML HTTP request.
+                    {
+                        //Populate the XML HTTP request disposition.
+                        entryStructPointer->xmlHttpRequest = FilterOptionEnum::Disposition::Apply;
+
+                        // Set the has request options flag.
+                        entryStructPointer->hasRequestOptions = true;
+                    }
+                    else if (filterOption == QLatin1String("~document"))  // Document override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.mainFrame = true;
+                    }
+                    else if (filterOption == QLatin1String("~font"))  // Font override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.font = true;
+                    }
+                    else if (filterOption == QLatin1String("~image"))  // Image override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.image = true;
+                    }
+                    else if (filterOption == QLatin1String("~media"))  // Media override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.media = true;
+                    }
+                    else if (filterOption == QLatin1String("~object"))  // Object override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.object = true;
+                    }
+                    else if ((filterOption == QLatin1String("~other")) || (filterOption == QLatin1String("~webrtc")) || (filterOption == QLatin1String("~websocket")))  // Other override.
+                    {  // `websocket` will get its own section in Qt6.
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.other = true;
+                    }
+                    else if (filterOption == QLatin1String("~ping"))  // Ping override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.ping = true;
+                    }
+                    else if (filterOption == QLatin1String("~script"))  // Script override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.script = true;
+                    }
+                    else if (filterOption == QLatin1String("~stylesheet"))  // Style sheet override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.styleSheet = true;
+                    }
+                    else if (filterOption == QLatin1String("~subdocument"))  // Sub document override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.subFrame = true;
+                    }
+                    else if (filterOption == QLatin1String("~xmlhttprequest"))  // XML HTTP request override.
+                    {
+                        // Populate the override struct.
+                        overrideStruct.hasOverride = true;
+                        overrideStruct.xmlHttpRequest = true;
+                    }
+                }
+
+                // Apply the overrides.
+                if (overrideStruct.hasOverride)
+                {
+                    // Set the has request options flag.
+                    entryStructPointer->hasRequestOptions = true;
+
+                    // Font.
+                    if (overrideStruct.font)
+                        entryStructPointer->font = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->font = FilterOptionEnum::Disposition::Apply;
+
+                    // Image.
+                    if (overrideStruct.image)
+                        entryStructPointer->image = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->image = FilterOptionEnum::Disposition::Apply;
+
+                    // Main Frame (document).
+                    if (overrideStruct.mainFrame)
+                        entryStructPointer->mainFrame = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->mainFrame = FilterOptionEnum::Disposition::Apply;
+
+                    // Media.
+                    if (overrideStruct.media)
+                        entryStructPointer->media = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->media = FilterOptionEnum::Disposition::Apply;
+
+                    // Object.
+                    if (overrideStruct.object)
+                        entryStructPointer->object = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->object = FilterOptionEnum::Disposition::Apply;
+
+                    // Other.
+                    if (overrideStruct.other)
+                        entryStructPointer->other = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->other = FilterOptionEnum::Disposition::Apply;
+
+                    // Ping.
+                    if (overrideStruct.ping)
+                        entryStructPointer->ping = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->ping = FilterOptionEnum::Disposition::Apply;
+
+                    //  Script.
+                    if (overrideStruct.script)
+                        entryStructPointer->script = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->script = FilterOptionEnum::Disposition::Apply;
+
+                    // Style Sheet.
+                    if (overrideStruct.styleSheet)
+                        entryStructPointer->styleSheet = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->styleSheet = FilterOptionEnum::Disposition::Apply;
+
+                    // Sub Resource.
+                    if (overrideStruct.subFrame)
+                        entryStructPointer->subFrame = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->subFrame = FilterOptionEnum::Disposition::Apply;
 
-            // Add the filter list entry struct to the main block list.
-            filterListStructPointer->mainBlockList.push_front(entryStructPointer);
+                    // XML HTTP Request.
+                    if (overrideStruct.xmlHttpRequest)
+                        entryStructPointer->xmlHttpRequest = FilterOptionEnum::Disposition::Override;
+                    else
+                        entryStructPointer->xmlHttpRequest = FilterOptionEnum::Disposition::Apply;
+                }
+            }  // Finish processing filter options.
 
-            // Log the addition to the filter list.
-            //qDebug().noquote().nospace() << originalFilterListString << " added from " << filterListFileName;
+
+            if (filterListString.isEmpty() && !entryStructPointer->hasRequestOptions)  // There are no applied entries and no request options.
+            {
+                // Ignore these entries as they will block all requests generally or for a specified domain.  Typically these are left over after removing `csp=` filter options.
+
+                // Log the dropping of the entry.
+                //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " NOT added from " << filterListFileName << ".";
+            }
+            else if (filterListString.startsWith(QLatin1String("@@")))  // Process an allow list entry.
+            {
+                // Remove the initial `@@`.
+                filterListString.remove(0, 2);
+
+                // Prepare the filter list string.  TODO.  Initial allow lists must be processed first.
+                prepareFilterListString(filterListString, entryStructPointer);
+
+                // Add the entry struct to the main allow list.
+                filterListStructPointer->mainAllowListPointer->push_front(entryStructPointer);
+
+                // Log the addition to the filter list.
+                //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " added to Main Allow List from " << filterListFileName << ".";
+            }
+            else if (filterListString.startsWith(QLatin1String("||")))  // Process an initial domain block list entry.
+            {
+                // Remove the initial `||`.
+                filterListString.remove(0, 2);
+
+                // Set the initial match flag.
+                entryStructPointer->initialMatch = true;
+
+                // Prepare the filter list string.
+                prepareFilterListString(filterListString, entryStructPointer);
+
+                // Add the entry struct to the initial domain block list.
+                filterListStructPointer->initialDomainBlockListPointer->push_front(entryStructPointer);
+
+                // Log the addition to the filter list.
+                //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " added to Initial Domain Block List from " << filterListFileName << ".";
+            }
+            else if (filterListString.contains(QLatin1String("\\")))  // Process a regular expression block list entry.
+            {
+                // Add the regular expression to the applied entry list.
+                entryStructPointer->appliedEntryList.append(filterListString);
+
+                // Add the entry struct to the regular expression block list.
+                filterListStructPointer->regularExpressionBlockListPointer->push_front(entryStructPointer);
+            }
+            else  // Process a main block list entry.
+            {
+                // Prepare the filter list string.
+                prepareFilterListString(filterListString, entryStructPointer);
+
+                // Add the entry struct to the main block list.
+                filterListStructPointer->mainBlockListPointer->push_front(entryStructPointer);
+
+                // Log the addition to the filter list.
+                //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " added to Main Block List from " << filterListFileName << ".";
+            }
         }
     }
 
@@ -269,13 +1261,52 @@ FilterListStruct* FilterListHelper::populateFilterList(const QString &filterList
     return filterListStructPointer;
 }
 
-void FilterListHelper::populateRequestStruct(RequestStruct *requestStructPointer, const int disposition, const QString &filterListTitle, const int sublistInt, const QString &appliedEntry,
-                                             const QString &originalEntry) const
+void FilterListHelper::populateRequestStruct(RequestStruct *requestStructPointer, const int disposition, const QString &filterListTitle, const int sublistInt, EntryStruct *entryStructPointer) const
 {
     // Populate the request struct.
     requestStructPointer->dispositionInt = disposition;
     requestStructPointer->filterListTitle = filterListTitle;
     requestStructPointer->sublistInt = sublistInt;
-    requestStructPointer->entryStruct.appliedEntry = appliedEntry;
-    requestStructPointer->entryStruct.originalEntry = originalEntry;
+    requestStructPointer->entryStruct.appliedEntryList = entryStructPointer->appliedEntryList;
+    requestStructPointer->entryStruct.originalEntry = entryStructPointer->originalEntry;
+}
+
+void FilterListHelper::prepareFilterListString(QString &filterListString, EntryStruct *entryStructPointer) const
+{
+    // Check if this is an initial match.
+    if (filterListString.startsWith(QLatin1Char('|')))
+    {
+        // Strip the initial `|`.
+        filterListString.remove(0, 1);
+
+        // Set the initial match flag.
+        entryStructPointer->initialMatch = true;
+    }
+
+    // Check if this is a final match.
+    if (filterListString.endsWith(QLatin1Char('|')))
+    {
+        // Strip the final `|`.
+        filterListString.chop(1);
+
+        // Set the final match flag.
+        entryStructPointer->finalMatch = true;
+    }
+
+    // Remove the initial asterisk if it exists.
+    if (filterListString.startsWith(QLatin1Char('*')))
+        filterListString.remove(0, 1);
+
+    // Remove the final asterisk if it exists.
+    if (filterListString.endsWith(QLatin1Char('*')))
+        filterListString.chop(1);
+
+    // Split the filter list string and set it as the applied entry list.
+    entryStructPointer->appliedEntryList = filterListString.split(QLatin1Char('*'));
+
+    // Store the size of the applied entry list.
+    entryStructPointer->sizeOfAppliedEntryList = entryStructPointer->appliedEntryList.size();
+
+    // Determine if this is a single applied entry (including an empty entry).
+    entryStructPointer->singleAppliedEntry = (entryStructPointer->sizeOfAppliedEntryList == 1);
 }