]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/helpers/FilterListHelper.cpp
Finish block list implementation.
[PrivacyBrowserPC.git] / src / helpers / FilterListHelper.cpp
1 /*
2  * Copyright 2024 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc/>.
5  *
6  * Privacy Browser PC is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Browser PC is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser PC.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 // Application headers.
21 #include "FilterListHelper.h"
22 #include "structs/OverrideStruct.h"
23
24 // KDE Framework headers.
25 #include <KLocalizedString>
26
27 // Qt toolkit headers.
28 #include <QDebug>
29 #include <QFile>
30 #include <QRegularExpression>
31 #include <QTextStream>
32
33 // Construct the class.
34 FilterListHelper::FilterListHelper()
35 {
36     // Populate the translated disposition strings.  Translated entries cannot be public static const.
37     DEFAULT_STRING = i18nc("Default disposition", "Default - Allowed");
38     ALLOWED_STRING = i18nc("Allowed disposition", "Allowed");
39     BLOCKED_STRING = i18nc("Blocked disposition", "Blocked");
40
41     // Populate the translated filter option disposition strings.  Translated entries cannot be public static const.
42     FILTER_OPTION_NULL = QString();
43     FILTER_OPTION_APPLY = i18nc("Apply filter option", "Apply");
44     FILTER_OPTION_OVERRIDE = i18nc("Override filter option", "Override");
45
46     // Populate the translated navigation type strings.  Translated entries cannot be public static const.
47     NAVIGATION_TYPE_LINK = i18nc("Navigation type link", "Link");
48     NAVIGATION_TYPE_TYPED = i18nc("Navigation type typed", "Typed");
49     NAVIGATION_TYPE_FORM_SUBMITTED = i18nc("Navigation type form submitted", "Form Submitted");
50     NAVIGATION_TYPE_BACK_FORWARD = i18nc("Navigation type back/forward", "Back/Forward");
51     NAVIGATION_TYPE_RELOAD = i18nc("Navigation type reload", "Reload");
52     NAVIGATION_TYPE_REDIRECT = i18nc("Navigation type redirect", "Redirect");
53     NAVIGATION_TYPE_OTHER = i18nc("Navigation type other", "Other");
54
55     // Populate the translated resource type strings.  Translated entries cannot be public static const.
56     RESOURCE_TYPE_MAIN_FRAME = i18nc("Resource type main frame", "Main Frame");
57     RESOURCE_TYPE_SUB_FRAME = i18nc("Resource type sub frame", "Sub Frame");
58     RESOURCE_TYPE_STYLESHEET = i18nc("Resource type stylesheet", "Stylesheet");
59     RESOURCE_TYPE_SCRIPT = i18nc("Resource type script", "Script");
60     RESOURCE_TYPE_IMAGE = i18nc("Resource type image", "Image");
61     RESOURCE_TYPE_FONT_RESOURCE = i18nc("Resource type font", "Font");
62     RESOURCE_TYPE_SUB_RESOURCE = i18nc("Resource type sub resource", "Sub Resource");
63     RESOURCE_TYPE_OBJECT = i18nc("Resource type object", "Object");
64     RESOURCE_TYPE_MEDIA = i18nc("Resource type media", "Media");
65     RESOURCE_TYPE_WORKER = i18nc("Resource type worker", "Worker");
66     RESOURCE_TYPE_SHARED_WORKER = i18nc("Resource type shared worker", "Shared Worker");
67     RESOURCE_TYPE_PREFETCH = i18nc("Resource type prefetch", "Prefetch");
68     RESOURCE_TYPE_FAVICON = i18nc("Resource type favicon", "Favicon");
69     RESOURCE_TYPE_XHR = i18nc("Resource type XML HTTP request", "XML HTTP Request");
70     RESOURCE_TYPE_PING = i18nc("Resource type HTTP ping", "HTTP Ping");
71     RESOURCE_TYPE_SERVICE_WORKER = i18nc("Resource type service worker", "Service Worker");
72     RESOURCE_TYPE_CSP_REPORT = i18nc("Resource type content security policy report", "Content Security Policy Report");
73     RESOURCE_TYPE_PLUGIN_RESOURCE = i18nc("Resource type plugin request", "Plugin Request");
74     RESOURCE_TYPE_NAVIGATION_PRELOAD_MAIN_FRAME = i18nc("Resource type preload main frame", "Preload Main Frame");
75     RESOURCE_TYPE_NAVIGATION_PRELOAD_SUB_FRAME = i18nc("Resource type preload sub frame", "Preload Sub Frame");
76     RESOURCE_TYPE_UNKNOWN = i18nc("Resource type unknown", "Unknown");
77
78     // Populate the translated sublist strings.  Translated entries cannot be public static const.
79     MAIN_ALLOWLIST_STRING = i18nc("Main allowlist sublist", "Main Allow List");
80     MAIN_BLOCKLIST_STRING = i18nc("Main blocklist sublist", "Main Block List");
81     INITIAL_DOMAIN_BLOCKLIST_STRING = i18nc("Initial domain blocklist string", "Initial Domain Block List");
82     REGULAR_EXPRESSION_BLOCKLIST_STRING = i18nc("Regular expression blocklist string", "Regular Expression Block List");
83
84     // Populate the filter lists.
85     ultraPrivacyStructPointer = populateFilterList(QLatin1String(":/filterlists/ultraprivacy.txt"));
86     ultraListStructPointer = populateFilterList(QLatin1String(":/filterlists/ultralist.txt"));
87     easyPrivacyStructPointer = populateFilterList(QLatin1String(":/filterlists/easyprivacy.txt"));
88     easyListStructPointer = populateFilterList(QLatin1String(":/filterlists/easylist.txt"));
89     fanboyAnnoyanceStructPointer = populateFilterList(QLatin1String(":/filterlists/fanboy-annoyance.txt"));
90 }
91
92 bool FilterListHelper::checkFilterLists(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer) const
93 {
94     // Initiate a continue checking tracker.  If the tracker changes to false, all processing of the request will be stopped.
95     bool continueChecking = true;
96
97     // Create a URL struct.
98     UrlStruct urlStruct;
99
100     // Get the URLs.
101     QUrl firstPartyUrl = urlRequestInfo.firstPartyUrl();
102     QUrl requestUrl = urlRequestInfo.requestUrl();
103
104     // Get the hosts.
105     QString firstPartyHost = firstPartyUrl.host();
106     urlStruct.fqdn = requestUrl.host();
107
108     // Determine if this is a third-party request.
109     urlStruct.isThirdPartyRequest = (firstPartyHost != urlStruct.fqdn);
110
111     // Get the request URL string.
112     urlStruct.urlString = requestUrl.toString();
113
114     // Create a URL string with separators.
115     urlStruct.urlStringWithSeparators = urlStruct.urlString;
116
117     // Replace the separators characters with `^`.
118     urlStruct.urlStringWithSeparators.replace(QLatin1Char(':'), QLatin1Char('^'));
119     urlStruct.urlStringWithSeparators.replace(QLatin1Char('/'), QLatin1Char('^'));
120     urlStruct.urlStringWithSeparators.replace(QLatin1Char('?'), QLatin1Char('^'));
121     urlStruct.urlStringWithSeparators.replace(QLatin1Char('='), QLatin1Char('^'));
122     urlStruct.urlStringWithSeparators.replace(QLatin1Char('&'), QLatin1Char('^'));
123
124     // Add a `^` to the end of the string it it doesn't already contain one.
125     if (!urlStruct.urlStringWithSeparators.endsWith(QLatin1Char('^')))
126         urlStruct.urlStringWithSeparators.append(QLatin1Char('^'));
127
128     // Create truncated URL strings and initially populate it with the original URL strings.
129     urlStruct.truncatedUrlString = urlStruct.urlString;
130     urlStruct.truncatedUrlStringWithSeparators = urlStruct.urlStringWithSeparators;
131
132     // Get the index of the beginning of the fully qualified domain name.
133     int fqdnIndex = urlStruct.truncatedUrlString.indexOf(QLatin1String("://")) + 3;
134
135     // Truncate the URL to the beginning of the fully qualified domain name.
136     urlStruct.truncatedUrlString.remove(0, fqdnIndex);
137     urlStruct.truncatedUrlStringWithSeparators.remove(0, fqdnIndex);
138
139     // Check UltraList.
140     continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, ultraPrivacyStructPointer);
141
142     // Check UltraPrivacy.
143     if (continueChecking)
144         continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, ultraListStructPointer);
145
146     // Check EasyPrivacy.
147     if (continueChecking)
148         continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, easyPrivacyStructPointer);
149
150     // Check EasyList.
151     if (continueChecking)
152         continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, easyListStructPointer);
153
154     // Check Fanboy's Annoyance list.
155     if (continueChecking)
156         continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, fanboyAnnoyanceStructPointer);
157
158     // Return the continue checking status.
159     return continueChecking;
160 }
161
162 bool FilterListHelper::checkIndividualList(QWebEngineUrlRequestInfo &urlRequestInfo, UrlStruct &urlStruct, RequestStruct *requestStructPointer, FilterListStruct *filterListStructPointer) const
163 {
164     // Initiate a continue checking tracker.  If the tracker changes to false, all process of the request will be stopped.
165     bool continueChecking = true;
166
167     // Check the main allow list.
168     for (auto filterListEntry = filterListStructPointer->mainAllowListPointer->begin(); filterListEntry != filterListStructPointer->mainAllowListPointer->end(); ++filterListEntry)
169     {
170         // Get the entry struct.
171         EntryStruct *entryStructPointer = *filterListEntry;
172
173         // TODO.  Temporarily ignore empty applied entries.
174         if (!entryStructPointer->appliedEntryList[0].isEmpty())
175         {
176             // Check if the URL string contains the applied entry.  TODO.
177             if (urlStruct.urlString.contains(entryStructPointer->appliedEntryList[0]) || urlStruct.urlStringWithSeparators.contains(entryStructPointer->appliedEntryList[0]))
178             {
179                 // Allow the request.
180                 urlRequestInfo.block(false);
181
182                 // Populate the request struct.
183                 populateRequestStruct(requestStructPointer, ALLOWED, filterListStructPointer->title, MAIN_ALLOWLIST, entryStructPointer);
184
185                 // Log the allow.
186                 //qDebug().noquote().nospace() << "Allowed request:  " << urlStruct.urlString << ", Filter list entry:  " << entryStructPointer->appliedEntry;
187
188                 // Returning `false` stops all processing of the request.
189                 return false;
190             }
191         }
192     }
193
194     // Get the main block list end.
195     auto mainBlockListEnd = filterListStructPointer->mainBlockListPointer->end();
196
197     // Check the main block list.
198     for (auto mainBlockListEntry = filterListStructPointer->mainBlockListPointer->begin(); mainBlockListEntry != mainBlockListEnd; ++mainBlockListEntry)
199     {
200         // Exit the loop if continue checking is false.
201         if (!continueChecking)
202             break;
203
204         // Get the entry struct.
205         EntryStruct *entryStructPointer = *mainBlockListEntry;
206
207         // Check the applied entries.
208         continueChecking = checkAppliedEntry(urlRequestInfo, urlStruct, requestStructPointer, filterListStructPointer->title, MAIN_BLOCKLIST, entryStructPointer, urlStruct.urlString,
209                                                 urlStruct.urlStringWithSeparators);
210     }
211
212     // Get the initial domain block list end.
213     auto initialDomainBlockListEnd = filterListStructPointer->initialDomainBlockListPointer->end();
214
215     // Check the initial domain block list.
216     for (auto initialDomainBlockListEntry = filterListStructPointer->initialDomainBlockListPointer->begin(); initialDomainBlockListEntry != initialDomainBlockListEnd;
217          ++initialDomainBlockListEntry)
218     {
219         // Exit the loop if continue checking is false.
220         if (!continueChecking)
221             break;
222
223         // Get the entry struct.
224         EntryStruct *entryStructPointer = *initialDomainBlockListEntry;
225
226         // Check the applied entries.
227         continueChecking = checkAppliedEntry(urlRequestInfo, urlStruct, requestStructPointer, filterListStructPointer->title, INITIAL_DOMAIN_BLOCKLIST, entryStructPointer,
228                                              urlStruct.truncatedUrlString, urlStruct.truncatedUrlStringWithSeparators);
229     }
230
231     // Get the regular expression block list end.
232     auto regularExpressionBlockListEnd = filterListStructPointer->regularExpressionBlockListPointer->end();
233
234     // Check the regular expression block list.
235     for (auto regularExpressionBlockListEntry = filterListStructPointer->regularExpressionBlockListPointer->begin(); regularExpressionBlockListEntry != regularExpressionBlockListEnd;
236          ++regularExpressionBlockListEntry)
237     {
238         // Exit the loop if continue checking is false.
239         if (!continueChecking)
240             break;
241
242         // Get the entry struct.
243         EntryStruct *entryStructPointer = *regularExpressionBlockListEntry;
244
245         // Check the applied entries.
246         continueChecking = checkRegularExpression(urlRequestInfo, urlStruct, requestStructPointer, filterListStructPointer->title, REGULAR_EXPRESSION_BLOCKLIST, entryStructPointer);
247     }
248
249     // Return the continue checking status.
250     return continueChecking;
251 }
252
253 bool FilterListHelper::checkAppliedEntry(QWebEngineUrlRequestInfo &urlRequestInfo, UrlStruct &urlStruct, RequestStruct *requestStructPointer, const QString &filterListTitle,
254                                          const int sublistInt, EntryStruct *entryStructPointer, QString &urlString, QString &urlStringWithSeparators) const
255 {
256     // Check the entries according to the number.
257     if (entryStructPointer->singleAppliedEntry)
258     {
259         // Process initial and final matches.
260         if (entryStructPointer->initialMatch && entryStructPointer->finalMatch)  // This is both an initial and final match.
261         {
262             // Check the URL against the applied entry.
263             if ((urlString == entryStructPointer->appliedEntryList[0]) || (urlStringWithSeparators == entryStructPointer->appliedEntryList[0]))
264             {
265                 // Check the domain status.
266                 return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
267             }
268         }
269         else if (entryStructPointer->initialMatch)  // This is an initial match.
270         {
271             // Check the URL against the applied entry.
272             if (urlString.startsWith(entryStructPointer->appliedEntryList[0]) || urlStringWithSeparators.startsWith(entryStructPointer->appliedEntryList[0]))
273             {
274                 // Check the domain status.
275                 return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
276             }
277         }
278         else if (entryStructPointer->finalMatch)  // This is a final match.
279         {
280             // Check the URL against the applied entry.
281             if (urlString.endsWith(entryStructPointer->appliedEntryList[0]) || urlStringWithSeparators.endsWith(entryStructPointer->appliedEntryList[0]))
282             {
283                 // Check the domain status.
284                 return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
285             }
286         }
287         else  // There is no initial or final matching.
288         {
289             // Check if the URL string contains the applied entry.
290             if (urlString.contains(entryStructPointer->appliedEntryList[0]) || urlStringWithSeparators.contains(entryStructPointer->appliedEntryList[0]))
291             {
292                 // Check the domain status.
293                 return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
294             }
295         }
296     }
297     else  // There are multiple entries.
298     {
299         // Create a URL matches flag.
300         bool urlMatches = true;
301
302         // Process initial and final matches.
303         if (entryStructPointer->initialMatch && entryStructPointer->finalMatch)  // This is both an initial and final match.
304         {
305             // Check the first entry.
306             if (urlString.startsWith(entryStructPointer->appliedEntryList[0]) ||
307                 urlStringWithSeparators.startsWith(entryStructPointer->appliedEntryList[0]))  // The URL string starts with the first applied entry.
308             {
309                 // Get the number of characters to remove from the front of the URL strings.
310                 int charactersToRemove = entryStructPointer->appliedEntryList[0].size();
311
312                 // Remove the entry from the front of the URL string copies.
313                 urlString.remove(0, charactersToRemove);
314                 urlStringWithSeparators.remove(0, charactersToRemove);
315             }
316             else  // The URL string does not end with the last applied entry.
317             {
318                 // Mark the URL matches flag as false.
319                 urlMatches = false;
320             }
321
322             // Check the other entries if the URL still matches.
323             if (urlMatches)
324             {
325                 // Calculate the penultimate entry.
326                 int penultimateEntryNumber = (entryStructPointer->sizeOfAppliedEntryList - 1);
327                 int ultimateEntryIndex = penultimateEntryNumber;
328
329                 // Check all the middle entries.
330                 for (int i = 1; i < penultimateEntryNumber; ++i)
331                 {
332                     // Get the index of the applied entry, which will be `-1` if it doesn't exist.
333                     int stringIndex = urlString.indexOf(entryStructPointer->appliedEntryList[i]);
334                     int stringWithSeparatorsIndex = urlStringWithSeparators.indexOf(entryStructPointer->appliedEntryList[i]);
335
336                     // Get the larger of the two indexes.
337                     int index = std::max(stringIndex, stringWithSeparatorsIndex);
338
339                     // Check if the entry was found.
340                     if (index >= 0)  // The entry is contained in the URL string.
341                     {
342                         // Get the number of characters to remove from the front of the URL strings.
343                         int charactersToRemove = index + entryStructPointer->appliedEntryList[i].size();
344
345                         // Remove the entry from the front of the URL string copies.
346                         urlString.remove(0, charactersToRemove);
347                         urlStringWithSeparators.remove(0, charactersToRemove);
348                     }
349                     else  // The entry is not contained in the URL string.
350                     {
351                         // Mark the URL matches flag as false.
352                         urlMatches = false;
353                     }
354                 }
355
356                 // Check the final entry if the URL still matches.
357                 if (urlMatches)
358                 {
359                     if (urlString.endsWith(entryStructPointer->appliedEntryList[ultimateEntryIndex]) ||
360                         urlStringWithSeparators.endsWith(entryStructPointer->appliedEntryList[ultimateEntryIndex]))  // The URL string ends with the last applied entry.
361                     {
362                         // There is no need to modify the URL string copies as no further checks will be performed.
363                     }
364                     else  // The URL string does not end with the last applied entry.
365                     {
366                         // Mark the URL matches flag as false.
367                         urlMatches = false;
368                     }
369                 }
370             }
371         }
372         else if (entryStructPointer->initialMatch)  // This is an initial match.
373         {
374             // Check the first entry.
375             if (urlString.startsWith(entryStructPointer->appliedEntryList[0]) ||
376                 urlStringWithSeparators.startsWith(entryStructPointer->appliedEntryList[0]))  // The URL string starts with the first applied entry.
377             {
378                 // Get the number of characters to remove from the front of the URL strings.
379                 int charactersToRemove = entryStructPointer->appliedEntryList[0].size();
380
381                 // Remove the entry from the front of the URL string copies.
382                 urlString.remove(0, charactersToRemove);
383                 urlStringWithSeparators.remove(0, charactersToRemove);
384             }
385             else  // The URL string does not end with the last applied entry.
386             {
387                 // Mark the URL matches flag as false.
388                 urlMatches = false;
389             }
390
391             // Check the other entries if the URL still matches.
392             if (urlMatches)
393             {
394                 for (int i = 1; i < entryStructPointer->sizeOfAppliedEntryList; ++i)
395                 {
396                     // Get the index of the applied entry, which will be `-1` if it doesn't exist.
397                     int stringIndex = urlString.indexOf(entryStructPointer->appliedEntryList[i]);
398                     int stringWithSeparatorsIndex = urlStringWithSeparators.indexOf(entryStructPointer->appliedEntryList[i]);
399
400                     // Get the larger of the two indexes.
401                     int index = std::max(stringIndex, stringWithSeparatorsIndex);
402
403                     // Check if the entry was found.
404                     if (index >= 0)  // The entry is contained in the URL string.
405                     {
406                         // Get the number of characters to remove from the front of the URL strings.
407                         int charactersToRemove = index + entryStructPointer->appliedEntryList[i].size();
408
409                         // Remove the entry from the front of the URL string copies.
410                         urlString.remove(0, charactersToRemove);
411                         urlStringWithSeparators.remove(0, charactersToRemove);
412                     }
413                     else  // The entry is not contained in the URL string.
414                     {
415                         // Mark the URL matches flag as false.
416                         urlMatches = false;
417                     }
418                 }
419             }
420         }
421         else if (entryStructPointer->finalMatch)  // This is a final match.
422         {
423             // Calculate the penultimate entry.
424             int penultimateEntryNumber = (entryStructPointer->sizeOfAppliedEntryList - 1);
425             int ultimateEntryIndex = penultimateEntryNumber;
426
427             // Check all the entries except the last one.
428             for (int i = 0; i < penultimateEntryNumber; ++i)
429             {
430                 // Get the index of the applied entry, which will be `-1` if it doesn't exist.
431                 int stringIndex = urlString.indexOf(entryStructPointer->appliedEntryList[i]);
432                 int stringWithSeparatorsIndex = urlStringWithSeparators.indexOf(entryStructPointer->appliedEntryList[i]);
433
434                 // Get the larger of the two indexes.
435                 int index = std::max(stringIndex, stringWithSeparatorsIndex);
436
437                 // Check if the entry was found.
438                 if (index >= 0)  // The entry is contained in the URL string.
439                 {
440                     // Get the number of characters to remove from the front of the URL strings.
441                     int charactersToRemove = index + entryStructPointer->appliedEntryList[i].size();
442
443                     // Remove the entry from the front of the URL string copies.
444                     urlString.remove(0, charactersToRemove);
445                     urlStringWithSeparators.remove(0, charactersToRemove);
446                 }
447                 else  // The entry is not contained in the URL string.
448                 {
449                     // Mark the URL matches flag as false.
450                     urlMatches = false;
451                 }
452             }
453
454             // Check the final entry if the URL still matches.
455             if (urlMatches)
456             {
457                 if (urlString.endsWith(entryStructPointer->appliedEntryList[ultimateEntryIndex]) ||
458                     urlStringWithSeparators.endsWith(entryStructPointer->appliedEntryList[ultimateEntryIndex]))  // The URL string ends with the last applied entry.
459                 {
460                     // There is no need to modify the URL string copies as no further checks will be performed.
461                 }
462                 else  // The URL string does not end with the last applied entry.
463                 {
464                     // Mark the URL matches flag as false.
465                     urlMatches = false;
466                 }
467             }
468         }
469         else  // There is no initial or final matching.
470         {
471             for (int i = 0; i < entryStructPointer->sizeOfAppliedEntryList; ++i)
472             {
473                 // Get the index of the applied entry, which will be `-1` if it doesn't exist.
474                 int stringIndex = urlString.indexOf(entryStructPointer->appliedEntryList[i]);
475                 int stringWithSeparatorsIndex = urlStringWithSeparators.indexOf(entryStructPointer->appliedEntryList[i]);
476
477                 // Get the larger of the two indexes.
478                 int index = std::max(stringIndex, stringWithSeparatorsIndex);
479
480                 // Check if the entry was found.
481                 if (index >= 0)  // The entry is contained in the URL string.
482                 {
483
484                     // Get the number of characters to remove from the front of the URL strings.
485                     int charactersToRemove = index + entryStructPointer->appliedEntryList[i].size();
486
487                     // Remove the entry from the front of the URL string copies.
488                     urlString.remove(0, charactersToRemove);
489                     urlStringWithSeparators.remove(0, charactersToRemove);
490                 }
491                 else  // The entry is not contained in the URL string.
492                 {
493                     // Mark the URL matches flag as false.
494                     urlMatches = false;
495                 }
496             }
497         }
498
499         // Check the domain status if the URL matches.
500         if (urlMatches)
501             return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
502     }
503
504     // If the applied entry doesn't match, return `true` to continue processing the URL request.
505     return true;
506 }
507
508 bool FilterListHelper::checkRegularExpression(QWebEngineUrlRequestInfo &urlRequestInfo, UrlStruct &urlStruct, RequestStruct *requestStructPointer, const QString &filterListTitle,
509                                               const int sublistInt, EntryStruct *entryStructPointer) const
510 {
511     // Create an applied entry regular expression.
512     QRegularExpression appliedEntryRegularExpression(entryStructPointer->appliedEntryList[0]);
513
514     // Check if the regular expression matches the applied entry.
515     if (urlStruct.urlString.contains(appliedEntryRegularExpression))
516     {
517         // Check the domain status.
518         return checkDomain(urlRequestInfo, urlStruct, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
519     }
520
521     // If the regular expression doesn't match, return `true` to continue processing the URL request.
522     return true;
523 }
524
525 bool FilterListHelper::checkDomain(QWebEngineUrlRequestInfo &urlRequestInfo, UrlStruct &urlStruct, RequestStruct *requestStructPointer, const QString &filterListTitle, const int sublistInt,
526                                    EntryStruct *entryStructPointer) const
527 {
528     // Check domain status.
529     if (entryStructPointer->domain == FilterOptionEnum::Disposition::Null)  // Ignore domain status.
530     {
531         // Check the third-party status.
532         return checkThirdParty(urlRequestInfo, requestStructPointer, urlStruct.isThirdPartyRequest, filterListTitle, sublistInt, entryStructPointer);
533     }
534     else if (entryStructPointer->domain == FilterOptionEnum::Disposition::Apply)  // Block requests from listed domains.
535     {
536         // Check each domain.
537         foreach (QString blockedDomain, entryStructPointer->domainList)
538         {
539             // Check if the request came from a blocked domain.
540             if (urlStruct.fqdn.endsWith(blockedDomain))
541             {
542                 // Check the third-party status.
543                 return checkThirdParty(urlRequestInfo, requestStructPointer, urlStruct.isThirdPartyRequest, filterListTitle, sublistInt, entryStructPointer);
544             }
545         }
546     }
547     else if (entryStructPointer->domain == FilterOptionEnum::Disposition::Override)  // Block domains that are not overridden.
548     {
549         // Create a block domain flag.
550         bool blockDomain = true;
551
552         // Check each overridden domain.
553         foreach (QString overriddenDomain, entryStructPointer->domainList)
554         {
555             // Check if the request came from an overridden domain.
556             if (urlStruct.fqdn.endsWith(overriddenDomain))
557             {
558                 // Don't block the domain.
559                 blockDomain = false;
560             }
561         }
562
563         // Continue checking if the domain is blocked.
564         if (blockDomain)
565         {
566             // Check the third-party status.
567             return checkThirdParty(urlRequestInfo, requestStructPointer, urlStruct.isThirdPartyRequest, filterListTitle, sublistInt, entryStructPointer);
568         }
569     }
570
571     // There is a domain specified that doesn't match this request.  Return `true` to continue processing the URL request.
572     return true;
573 }
574
575 bool FilterListHelper::checkThirdParty(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, const bool isThirdPartyRequest, const QString &filterListTitle,
576                                        const int sublistInt, EntryStruct *entryStructPointer) const
577 {
578     // Check third-party status.
579     if (entryStructPointer->thirdParty == FilterOptionEnum::Disposition::Null)  // Ignore third-party status.
580     {
581         // Check if request options are applied.
582         if (entryStructPointer->hasRequestOptions)  // Request options are applied.
583         {
584             // Check the request options.
585             return checkRequestOptions(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
586         }
587         else  // Request options are not applied.
588         {
589             // Block the request.
590             return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
591         }
592     }
593     else if ((entryStructPointer->thirdParty == FilterOptionEnum::Disposition::Apply) && isThirdPartyRequest)  // Block third-party request.
594     {
595         // Check if request options are applied.
596         if (entryStructPointer->hasRequestOptions)  // Request options are applied.
597         {
598             // Check the request options.
599             return checkRequestOptions(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
600         }
601         else  // Request options are not applied.
602         {
603             // Block the request.
604             return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
605         }
606     }
607     else if ((entryStructPointer->thirdParty == FilterOptionEnum::Disposition::Override) && !isThirdPartyRequest)  // Block first-party requests.
608     {
609         // Check if request options are applied.
610         if (entryStructPointer->hasRequestOptions)  // Request options are applied.
611         {
612             // Check the request options.
613             return checkRequestOptions(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
614         }
615         else  // Request options are not applied.
616         {
617             // Block the request.
618             return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
619         }
620     }
621
622     // The third-party option specified doesn't match this request.  Return `true` to continue processing the URL request.
623     return true;
624 }
625
626 bool FilterListHelper::checkRequestOptions(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, const QString &filterListTitle, const int sublistInt,
627                                            EntryStruct *entryStructPointer) const
628 {
629     // Block font requests.
630     if ((entryStructPointer->font == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeFontResource))
631     {
632         // Block the request.
633         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
634     }
635
636     // Block image requests.
637     if ((entryStructPointer->image == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeImage))
638     {
639         // Block the request.
640         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
641     }
642
643     // Block main frame requests.
644     if ((entryStructPointer->mainFrame == FilterOptionEnum::Disposition::Apply) && ((requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeMainFrame) ||
645                                                                                     (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadMainFrame)))
646     {
647         // Block the request.
648         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
649     }
650
651     // Block media requests.
652     if ((entryStructPointer->media == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeMedia))
653     {
654         // Block the request.
655         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
656     }
657
658     // Block object requests.
659     if ((entryStructPointer->object == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeObject))
660     {
661         // Block the request.
662         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
663     }
664
665     // Block other requests.
666     if ((entryStructPointer->other == FilterOptionEnum::Disposition::Apply) && ((requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeSubResource) ||
667                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeWorker) ||
668                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeSharedWorker) ||
669                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypePrefetch) ||
670                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeFavicon) ||
671                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeServiceWorker) ||
672                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeCspReport) ||
673                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypePluginResource) ||
674                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeUnknown)))
675     {
676         // Block the request.
677         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
678     }
679
680     // Block ping requests
681     if ((entryStructPointer->ping == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypePing))
682     {
683         // Block the request.
684         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
685     }
686
687     // Block script requests.
688     if ((entryStructPointer->script == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeScript))
689     {
690         // Block the request.
691         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
692     }
693
694     // Block style sheet requests.
695     if ((entryStructPointer->styleSheet == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeStylesheet))
696     {
697         // Block the request.
698         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
699     }
700
701     // Block sub resource requests.
702     if ((entryStructPointer->subFrame == FilterOptionEnum::Disposition::Apply) && ((requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeSubFrame) ||
703                                                                                    (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadSubFrame)))
704     {
705         // Block the request.
706         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
707     }
708
709     // Block XML HTTP requests.
710     if ((entryStructPointer->xmlHttpRequest == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeXhr))
711     {
712         // Block the request.
713         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
714     }
715
716     // The request options specified don't match this request.  Return `true` to continue processing the URL request.
717     return true;
718 }
719
720 bool FilterListHelper::blockRequest(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, const QString &filterListTitle, const int sublistInt,
721                                     EntryStruct *entryStructPointer) const
722 {
723     // Block the request.
724     urlRequestInfo.block(true);
725
726     // Populate the request struct.
727     populateRequestStruct(requestStructPointer, BLOCKED, filterListTitle, sublistInt, entryStructPointer);
728
729     // Log the block.
730     //qDebug().noquote().nospace() << "Blocked request:  " << urlRequestInfo.firstPartyUrl() << ",  Filter list entry:  " << entryStructPointer->appliedEntry;
731
732     // Returning `false` stops all processing of the request.
733     return false;
734 }
735
736 QString FilterListHelper::getDispositionString(int dispositionInt) const
737 {
738     // Return the translated disposition string.
739     switch (dispositionInt)
740     {
741         case ALLOWED: return ALLOWED_STRING;
742         case BLOCKED: return BLOCKED_STRING;
743         default: return DEFAULT_STRING;
744     }
745 }
746
747 QString FilterListHelper::getNavigationTypeString(int navigationTypeInt) const
748 {
749     // Return the translated navigation type string.
750     switch (navigationTypeInt)
751     {
752         case QWebEngineUrlRequestInfo::NavigationTypeLink: return NAVIGATION_TYPE_LINK;
753         case QWebEngineUrlRequestInfo::NavigationTypeTyped: return NAVIGATION_TYPE_TYPED;
754         case QWebEngineUrlRequestInfo::NavigationTypeFormSubmitted: return NAVIGATION_TYPE_FORM_SUBMITTED;
755         case QWebEngineUrlRequestInfo::NavigationTypeBackForward: return NAVIGATION_TYPE_BACK_FORWARD;
756         case QWebEngineUrlRequestInfo::NavigationTypeReload: return NAVIGATION_TYPE_RELOAD;
757         case QWebEngineUrlRequestInfo::NavigationTypeRedirect: return NAVIGATION_TYPE_REDIRECT;
758         default: return NAVIGATION_TYPE_OTHER;
759     }
760 }
761
762 QString FilterListHelper::getRequestOptionDispositionString(const FilterOptionEnum::Disposition filterOptionDisposition) const
763 {
764     // Return the translated filter option disposition string.
765     switch (filterOptionDisposition)
766     {
767         case FilterOptionEnum::Disposition::Apply: return FILTER_OPTION_APPLY;
768         case FilterOptionEnum::Disposition::Override: return FILTER_OPTION_OVERRIDE;
769         default: return FILTER_OPTION_NULL;
770     }
771 }
772
773 QString FilterListHelper::getResourceTypeString(int resourceTypeInt) const
774 {
775     // Return the translated resource type string.
776     switch (resourceTypeInt)
777     {
778         case QWebEngineUrlRequestInfo::ResourceTypeMainFrame: return RESOURCE_TYPE_MAIN_FRAME;
779         case QWebEngineUrlRequestInfo::ResourceTypeSubFrame: return RESOURCE_TYPE_SUB_FRAME;
780         case QWebEngineUrlRequestInfo::ResourceTypeStylesheet: return RESOURCE_TYPE_STYLESHEET;
781         case QWebEngineUrlRequestInfo::ResourceTypeScript: return RESOURCE_TYPE_SCRIPT;
782         case QWebEngineUrlRequestInfo::ResourceTypeImage: return RESOURCE_TYPE_IMAGE;
783         case QWebEngineUrlRequestInfo::ResourceTypeFontResource: return RESOURCE_TYPE_FONT_RESOURCE;
784         case QWebEngineUrlRequestInfo::ResourceTypeSubResource: return RESOURCE_TYPE_SUB_RESOURCE;
785         case QWebEngineUrlRequestInfo::ResourceTypeObject: return RESOURCE_TYPE_OBJECT;
786         case QWebEngineUrlRequestInfo::ResourceTypeMedia: return RESOURCE_TYPE_MEDIA;
787         case QWebEngineUrlRequestInfo::ResourceTypeWorker: return RESOURCE_TYPE_WORKER;
788         case QWebEngineUrlRequestInfo::ResourceTypeSharedWorker: return RESOURCE_TYPE_SHARED_WORKER;
789         case QWebEngineUrlRequestInfo::ResourceTypePrefetch: return RESOURCE_TYPE_PREFETCH;
790         case QWebEngineUrlRequestInfo::ResourceTypeFavicon: return RESOURCE_TYPE_FAVICON;
791         case QWebEngineUrlRequestInfo::ResourceTypeXhr: return RESOURCE_TYPE_XHR;
792         case QWebEngineUrlRequestInfo::ResourceTypePing: return RESOURCE_TYPE_PING;
793         case QWebEngineUrlRequestInfo::ResourceTypeServiceWorker: return RESOURCE_TYPE_SERVICE_WORKER;
794         case QWebEngineUrlRequestInfo::ResourceTypeCspReport: return RESOURCE_TYPE_CSP_REPORT;
795         case QWebEngineUrlRequestInfo::ResourceTypePluginResource: return RESOURCE_TYPE_PLUGIN_RESOURCE;
796         case QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadMainFrame: return RESOURCE_TYPE_NAVIGATION_PRELOAD_MAIN_FRAME;
797         case QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadSubFrame: return RESOURCE_TYPE_NAVIGATION_PRELOAD_SUB_FRAME;
798         default: return RESOURCE_TYPE_UNKNOWN;
799     }
800 }
801
802 QString FilterListHelper::getSublistName(int sublistInt) const
803 {
804     // Return the name of the requested sublist.
805     switch (sublistInt)
806     {
807         case MAIN_ALLOWLIST: return MAIN_ALLOWLIST_STRING;
808         case MAIN_BLOCKLIST: return MAIN_BLOCKLIST_STRING;
809         case INITIAL_DOMAIN_BLOCKLIST: return INITIAL_DOMAIN_BLOCKLIST_STRING;
810         case REGULAR_EXPRESSION_BLOCKLIST: return REGULAR_EXPRESSION_BLOCKLIST_STRING;
811         default: return QString();  // The default return should never be reached.
812     }
813 }
814
815 FilterListStruct* FilterListHelper::populateFilterList(const QString &filterListFileName) const
816 {
817     // Get the filter list file.
818     QFile filterListFile(filterListFileName);
819
820     // Open the filter list file.
821     filterListFile.open(QIODevice::ReadOnly);
822
823     // Create a filter list text stream.
824     QTextStream filterListTextStream(&filterListFile);
825
826     // Create a filter list struct.
827     FilterListStruct *filterListStructPointer = new FilterListStruct;
828
829     // Populate the filter list file name.
830     filterListStructPointer->filePath = filterListFileName;
831
832     // Create a filter list string.
833     QString filterListString;
834
835     // Process each line of the filter list.
836     while (filterListTextStream.readLineInto(&filterListString)) {
837         // Create an entry struct.
838         EntryStruct *entryStructPointer = new EntryStruct;
839
840         // Store the original entry.
841         entryStructPointer->originalEntry = filterListString;
842
843         // Process the entry.
844         if (filterListString.isEmpty())  // Ignore empty lines.
845         {
846             // Do nothing.
847
848             // Log the dropping of the line.
849             //qDebug().noquote().nospace() << filterListString << " NOT added from " << filterListFileName << " (empty line).";
850         }
851         else if (filterListString.startsWith(QLatin1Char('[')))  // The line starts with `[`, which is the file format.
852         {
853             // Do nothing.
854
855             // Log the dropping of the line.
856             //qDebug().noquote().nospace() << filterListString << " NOT added from " << filterListFileName << " (file format).";
857         }
858         else if (filterListString.contains(QLatin1String("##")) ||
859                  filterListString.contains(QLatin1String("#?#")) ||
860                  filterListString.contains(QLatin1String("#@#")) ||
861                  filterListString.contains(QLatin1String("#$#")))  // The line contains unimplemented content filtering.
862         {
863             // Do nothing.
864
865             // Log the dropping of the line.
866             //qDebug().noquote().nospace() << filterListString << " NOT added from " << filterListFileName << " (content filtering).";
867         }
868         else if (filterListString.startsWith(QLatin1Char('!')))  // The line starts with `!`, which are comments.
869         {
870             if (filterListString.startsWith(QLatin1String("! Title: ")))  // The line contains the title.
871             {
872                 // Add the title to the filter list struct.
873                 filterListStructPointer->title = filterListString.remove(0, 9);
874
875                 // Log the addition of the filter list title.
876                 //qDebug().noquote().nospace() << "Filter list title:  " << filterListString << " added from " << filterListFileName;
877             }
878             else if (filterListString.startsWith(QLatin1String("! Version: ")))  // The line contains the version.
879             {
880                 // Add the version to the filter list struct.
881                 filterListStructPointer->version = filterListString.remove(0, 11);
882
883                 // Log the addition of the filter list version.
884                 //qDebug().noquote().nospace() << "Filter list version:  " << filterListString << " added from " << filterListFileName;
885             }
886
887             // Else do nothing.
888
889             // Log the dropping of the line.
890             //qDebug().noquote().nospace() << originalFilterListString << " NOT added from " << filterListFileName;
891         }
892         else  // Process the filter options.
893         {
894             // Get the index of the last dollar sign.
895             int indexOfLastDollarSign = filterListString.lastIndexOf(QLatin1Char('$'));
896
897             // Process the filter options if they exist.
898             if (indexOfLastDollarSign > -1)
899             {
900                 // Get the filter options.
901                 entryStructPointer->originalFilterOptions = filterListString.section(QLatin1Char('$'), -1);
902
903                 // Store the entry without the filter options as the filter list string.
904                 filterListString.truncate(indexOfLastDollarSign);
905
906                 // Split the filter options.
907                 QStringList originalFilterOptionsList = entryStructPointer->originalFilterOptions.split(QLatin1Char(','));
908
909                 // Create an applied filter options list.
910                 QStringList appliedFilterOptionsList;
911
912                 // Populate the applied filter options list.
913                 foreach (QString filterOption, originalFilterOptionsList)
914                 {
915                     // Only add filter options that are handled by Privacy Browser.
916                     if (!(filterOption.startsWith(QLatin1String("csp=")) ||
917                           filterOption.startsWith(QLatin1String("method=")) ||
918                           filterOption.startsWith(QLatin1String("redirect=")) ||
919                           filterOption.startsWith(QLatin1String("rewrite="))))
920                         appliedFilterOptionsList.append(filterOption);
921                 }
922
923                 // Store the applied filter options list.
924                 entryStructPointer->appliedFilterOptionsList = appliedFilterOptionsList;
925
926                 // Initialize an override struct.
927                 OverrideStruct overrideStruct;
928
929                 // Populate the filter options entries.
930                 foreach (QString filterOption, appliedFilterOptionsList)
931                 {
932                     // Parse the filter options.
933                     if (filterOption.startsWith(QLatin1String("domain=")))  // Domain.
934                     {
935                         // Remove `domain=` from the filter option.
936                         filterOption.remove(0, 7);
937
938                         // Store the domain list.
939                         entryStructPointer->domainList = filterOption.split(QLatin1Char('|'));
940
941                         // Set the disposition.
942                         if (entryStructPointer->domainList[0].startsWith(QLatin1Char('~')))  // Override domains.
943                         {
944                             // Populate the domain filter disposition.
945                             entryStructPointer->domain = FilterOptionEnum::Disposition::Override;
946
947                             // Remove the initial `~` from each domain.
948                             entryStructPointer->domainList.replaceInStrings(QLatin1String("~"), QLatin1String(""));
949                         }
950                         else  // Standard domains.
951                         {
952                             // Populate the domain filter disposition.
953                             entryStructPointer->domain = FilterOptionEnum::Disposition::Apply;
954                         }
955                     }
956                     else if (filterOption == QLatin1String("third-party"))  // Third-party.
957                     {
958                         // Populate the third-party filter disposition.
959                         entryStructPointer->thirdParty = FilterOptionEnum::Disposition::Apply;
960                     }
961                     else if (filterOption == QLatin1String("~third-party"))  // Third-party override.
962                     {
963                         // Populate the third-party filter disposition.
964                         entryStructPointer->thirdParty = FilterOptionEnum::Disposition::Override;
965                     }
966                     else if ((filterOption == QLatin1String("document")) || (filterOption == QLatin1String("popup")))  // Document (and popup).
967                     {
968                         // Populate the main frame disposition.
969                         entryStructPointer->mainFrame = FilterOptionEnum::Disposition::Apply;
970
971                         // Set the has request options flag.
972                         entryStructPointer->hasRequestOptions = true;
973                     }
974                     else if (filterOption == QLatin1String("font"))  // Font.
975                     {
976                         // Populate the font disposition.
977                         entryStructPointer->font = FilterOptionEnum::Disposition::Apply;
978
979                         // Set the has request options flag.
980                         entryStructPointer->hasRequestOptions = true;
981                     }
982                     else if (filterOption == QLatin1String("image"))  // Image.
983                     {
984                         // Populate the image disposition.
985                         entryStructPointer->image = FilterOptionEnum::Disposition::Apply;
986
987                         // Set the has request options flag.
988                         entryStructPointer->hasRequestOptions = true;
989                     }
990                     else if (filterOption == QLatin1String("media"))  // Media.
991                     {
992                         // Populate the media disposition.
993                         entryStructPointer->media = FilterOptionEnum::Disposition::Apply;
994
995                         // Set the has request options flag.
996                         entryStructPointer->hasRequestOptions = true;
997                     }
998                     else if (filterOption == QLatin1String("object"))  // Object.
999                     {
1000                         // Populate the object disposition.
1001                         entryStructPointer->object = FilterOptionEnum::Disposition::Apply;
1002
1003                         // Set the has request options flag.
1004                         entryStructPointer->hasRequestOptions = true;
1005                     }
1006                     else if ((filterOption == QLatin1String("other")) || (filterOption == QLatin1String("webrtc")) || (filterOption == QLatin1String("websocket")))  // Other.
1007                     {  // `websocket` will get its own section in Qt6.
1008                         // Populate the other disposition.
1009                         entryStructPointer->other = FilterOptionEnum::Disposition::Apply;
1010
1011                         // Set the has request options flag.
1012                         entryStructPointer->hasRequestOptions = true;
1013                     }
1014                     else if (filterOption == QLatin1String("ping"))  // Ping.
1015                     {
1016                         // Populate the ping disposition.
1017                         entryStructPointer->ping = FilterOptionEnum::Disposition::Apply;
1018
1019                         // Set the has request options flag.
1020                         entryStructPointer->hasRequestOptions = true;
1021                     }
1022                     else if (filterOption == QLatin1String("script"))  // Script.
1023                     {
1024                         // Populate the script disposition.
1025                         entryStructPointer->script = FilterOptionEnum::Disposition::Apply;
1026
1027                         // Set the has request options flag.
1028                         entryStructPointer->hasRequestOptions = true;
1029                     }
1030                     else if (filterOption == QLatin1String("stylesheet"))  // Style sheet.
1031                     {
1032                         // Populate the script disposition.
1033                         entryStructPointer->styleSheet = FilterOptionEnum::Disposition::Apply;
1034
1035                         // Set the has request options flag.
1036                         entryStructPointer->hasRequestOptions = true;
1037                     }
1038                     else if (filterOption == QLatin1String("subdocument"))  // Sub document.
1039                     {
1040                         // Populate the sub resource disposition.
1041                         entryStructPointer->subFrame = FilterOptionEnum::Disposition::Apply;
1042
1043                         // Set the has request options flag.
1044                         entryStructPointer->hasRequestOptions = true;
1045                     }
1046                     else if (filterOption == QLatin1String("xmlhttprequest"))  // XML HTTP request.
1047                     {
1048                         //Populate the XML HTTP request disposition.
1049                         entryStructPointer->xmlHttpRequest = FilterOptionEnum::Disposition::Apply;
1050
1051                         // Set the has request options flag.
1052                         entryStructPointer->hasRequestOptions = true;
1053                     }
1054                     else if (filterOption == QLatin1String("~document"))  // Document override.
1055                     {
1056                         // Populate the override struct.
1057                         overrideStruct.hasOverride = true;
1058                         overrideStruct.mainFrame = true;
1059                     }
1060                     else if (filterOption == QLatin1String("~font"))  // Font override.
1061                     {
1062                         // Populate the override struct.
1063                         overrideStruct.hasOverride = true;
1064                         overrideStruct.font = true;
1065                     }
1066                     else if (filterOption == QLatin1String("~image"))  // Image override.
1067                     {
1068                         // Populate the override struct.
1069                         overrideStruct.hasOverride = true;
1070                         overrideStruct.image = true;
1071                     }
1072                     else if (filterOption == QLatin1String("~media"))  // Media override.
1073                     {
1074                         // Populate the override struct.
1075                         overrideStruct.hasOverride = true;
1076                         overrideStruct.media = true;
1077                     }
1078                     else if (filterOption == QLatin1String("~object"))  // Object override.
1079                     {
1080                         // Populate the override struct.
1081                         overrideStruct.hasOverride = true;
1082                         overrideStruct.object = true;
1083                     }
1084                     else if ((filterOption == QLatin1String("~other")) || (filterOption == QLatin1String("~webrtc")) || (filterOption == QLatin1String("~websocket")))  // Other override.
1085                     {  // `websocket` will get its own section in Qt6.
1086                         // Populate the override struct.
1087                         overrideStruct.hasOverride = true;
1088                         overrideStruct.other = true;
1089                     }
1090                     else if (filterOption == QLatin1String("~ping"))  // Ping override.
1091                     {
1092                         // Populate the override struct.
1093                         overrideStruct.hasOverride = true;
1094                         overrideStruct.ping = true;
1095                     }
1096                     else if (filterOption == QLatin1String("~script"))  // Script override.
1097                     {
1098                         // Populate the override struct.
1099                         overrideStruct.hasOverride = true;
1100                         overrideStruct.script = true;
1101                     }
1102                     else if (filterOption == QLatin1String("~stylesheet"))  // Style sheet override.
1103                     {
1104                         // Populate the override struct.
1105                         overrideStruct.hasOverride = true;
1106                         overrideStruct.styleSheet = true;
1107                     }
1108                     else if (filterOption == QLatin1String("~subdocument"))  // Sub document override.
1109                     {
1110                         // Populate the override struct.
1111                         overrideStruct.hasOverride = true;
1112                         overrideStruct.subFrame = true;
1113                     }
1114                     else if (filterOption == QLatin1String("~xmlhttprequest"))  // XML HTTP request override.
1115                     {
1116                         // Populate the override struct.
1117                         overrideStruct.hasOverride = true;
1118                         overrideStruct.xmlHttpRequest = true;
1119                     }
1120                 }
1121
1122                 // Apply the overrides.
1123                 if (overrideStruct.hasOverride)
1124                 {
1125                     // Set the has request options flag.
1126                     entryStructPointer->hasRequestOptions = true;
1127
1128                     // Font.
1129                     if (overrideStruct.font)
1130                         entryStructPointer->font = FilterOptionEnum::Disposition::Override;
1131                     else
1132                         entryStructPointer->font = FilterOptionEnum::Disposition::Apply;
1133
1134                     // Image.
1135                     if (overrideStruct.image)
1136                         entryStructPointer->image = FilterOptionEnum::Disposition::Override;
1137                     else
1138                         entryStructPointer->image = FilterOptionEnum::Disposition::Apply;
1139
1140                     // Main Frame (document).
1141                     if (overrideStruct.mainFrame)
1142                         entryStructPointer->mainFrame = FilterOptionEnum::Disposition::Override;
1143                     else
1144                         entryStructPointer->mainFrame = FilterOptionEnum::Disposition::Apply;
1145
1146                     // Media.
1147                     if (overrideStruct.media)
1148                         entryStructPointer->media = FilterOptionEnum::Disposition::Override;
1149                     else
1150                         entryStructPointer->media = FilterOptionEnum::Disposition::Apply;
1151
1152                     // Object.
1153                     if (overrideStruct.object)
1154                         entryStructPointer->object = FilterOptionEnum::Disposition::Override;
1155                     else
1156                         entryStructPointer->object = FilterOptionEnum::Disposition::Apply;
1157
1158                     // Other.
1159                     if (overrideStruct.other)
1160                         entryStructPointer->other = FilterOptionEnum::Disposition::Override;
1161                     else
1162                         entryStructPointer->other = FilterOptionEnum::Disposition::Apply;
1163
1164                     // Ping.
1165                     if (overrideStruct.ping)
1166                         entryStructPointer->ping = FilterOptionEnum::Disposition::Override;
1167                     else
1168                         entryStructPointer->ping = FilterOptionEnum::Disposition::Apply;
1169
1170                     //  Script.
1171                     if (overrideStruct.script)
1172                         entryStructPointer->script = FilterOptionEnum::Disposition::Override;
1173                     else
1174                         entryStructPointer->script = FilterOptionEnum::Disposition::Apply;
1175
1176                     // Style Sheet.
1177                     if (overrideStruct.styleSheet)
1178                         entryStructPointer->styleSheet = FilterOptionEnum::Disposition::Override;
1179                     else
1180                         entryStructPointer->styleSheet = FilterOptionEnum::Disposition::Apply;
1181
1182                     // Sub Resource.
1183                     if (overrideStruct.subFrame)
1184                         entryStructPointer->subFrame = FilterOptionEnum::Disposition::Override;
1185                     else
1186                         entryStructPointer->subFrame = FilterOptionEnum::Disposition::Apply;
1187
1188                     // XML HTTP Request.
1189                     if (overrideStruct.xmlHttpRequest)
1190                         entryStructPointer->xmlHttpRequest = FilterOptionEnum::Disposition::Override;
1191                     else
1192                         entryStructPointer->xmlHttpRequest = FilterOptionEnum::Disposition::Apply;
1193                 }
1194             }  // Finish processing filter options.
1195
1196
1197             if (filterListString.isEmpty() && !entryStructPointer->hasRequestOptions)  // There are no applied entries and no request options.
1198             {
1199                 // 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.
1200
1201                 // Log the dropping of the entry.
1202                 //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " NOT added from " << filterListFileName << ".";
1203             }
1204             else if (filterListString.startsWith(QLatin1String("@@")))  // Process an allow list entry.
1205             {
1206                 // Remove the initial `@@`.
1207                 filterListString.remove(0, 2);
1208
1209                 // Prepare the filter list string.  TODO.  Initial allow lists must be processed first.
1210                 prepareFilterListString(filterListString, entryStructPointer);
1211
1212                 // Add the entry struct to the main allow list.
1213                 filterListStructPointer->mainAllowListPointer->push_front(entryStructPointer);
1214
1215                 // Log the addition to the filter list.
1216                 //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " added to Main Allow List from " << filterListFileName << ".";
1217             }
1218             else if (filterListString.startsWith(QLatin1String("||")))  // Process an initial domain block list entry.
1219             {
1220                 // Remove the initial `||`.
1221                 filterListString.remove(0, 2);
1222
1223                 // Set the initial match flag.
1224                 entryStructPointer->initialMatch = true;
1225
1226                 // Prepare the filter list string.
1227                 prepareFilterListString(filterListString, entryStructPointer);
1228
1229                 // Add the entry struct to the initial domain block list.
1230                 filterListStructPointer->initialDomainBlockListPointer->push_front(entryStructPointer);
1231
1232                 // Log the addition to the filter list.
1233                 //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " added to Initial Domain Block List from " << filterListFileName << ".";
1234             }
1235             else if (filterListString.contains(QLatin1String("\\")))  // Process a regular expression block list entry.
1236             {
1237                 // Add the regular expression to the applied entry list.
1238                 entryStructPointer->appliedEntryList.append(filterListString);
1239
1240                 // Add the entry struct to the regular expression block list.
1241                 filterListStructPointer->regularExpressionBlockListPointer->push_front(entryStructPointer);
1242             }
1243             else  // Process a main block list entry.
1244             {
1245                 // Prepare the filter list string.
1246                 prepareFilterListString(filterListString, entryStructPointer);
1247
1248                 // Add the entry struct to the main block list.
1249                 filterListStructPointer->mainBlockListPointer->push_front(entryStructPointer);
1250
1251                 // Log the addition to the filter list.
1252                 //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " added to Main Block List from " << filterListFileName << ".";
1253             }
1254         }
1255     }
1256
1257     // Close the filter list file.
1258     filterListFile.close();
1259
1260     // Return the filter list pair.
1261     return filterListStructPointer;
1262 }
1263
1264 void FilterListHelper::populateRequestStruct(RequestStruct *requestStructPointer, const int disposition, const QString &filterListTitle, const int sublistInt, EntryStruct *entryStructPointer) const
1265 {
1266     // Populate the request struct.
1267     requestStructPointer->dispositionInt = disposition;
1268     requestStructPointer->filterListTitle = filterListTitle;
1269     requestStructPointer->sublistInt = sublistInt;
1270     requestStructPointer->entryStruct.appliedEntryList = entryStructPointer->appliedEntryList;
1271     requestStructPointer->entryStruct.originalEntry = entryStructPointer->originalEntry;
1272 }
1273
1274 void FilterListHelper::prepareFilterListString(QString &filterListString, EntryStruct *entryStructPointer) const
1275 {
1276     // Check if this is an initial match.
1277     if (filterListString.startsWith(QLatin1Char('|')))
1278     {
1279         // Strip the initial `|`.
1280         filterListString.remove(0, 1);
1281
1282         // Set the initial match flag.
1283         entryStructPointer->initialMatch = true;
1284     }
1285
1286     // Check if this is a final match.
1287     if (filterListString.endsWith(QLatin1Char('|')))
1288     {
1289         // Strip the final `|`.
1290         filterListString.chop(1);
1291
1292         // Set the final match flag.
1293         entryStructPointer->finalMatch = true;
1294     }
1295
1296     // Remove the initial asterisk if it exists.
1297     if (filterListString.startsWith(QLatin1Char('*')))
1298         filterListString.remove(0, 1);
1299
1300     // Remove the final asterisk if it exists.
1301     if (filterListString.endsWith(QLatin1Char('*')))
1302         filterListString.chop(1);
1303
1304     // Split the filter list string and set it as the applied entry list.
1305     entryStructPointer->appliedEntryList = filterListString.split(QLatin1Char('*'));
1306
1307     // Store the size of the applied entry list.
1308     entryStructPointer->sizeOfAppliedEntryList = entryStructPointer->appliedEntryList.size();
1309
1310     // Determine if this is a single applied entry (including an empty entry).
1311     entryStructPointer->singleAppliedEntry = (entryStructPointer->sizeOfAppliedEntryList == 1);
1312 }