]> gitweb.stoutner.com Git - PrivacyBrowserPC.git/blob - src/helpers/FilterListHelper.cpp
Filter options 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 // Qt toolkit headers.
25 #include <QDebug>
26 #include <QFile>
27 #include <QTextStream>
28
29 // KDE Framework headers.
30 #include <KLocalizedString>
31
32 // Construct the class.
33 FilterListHelper::FilterListHelper()
34 {
35     // Populate the translated disposition strings.  Translated entries cannot be public static const.
36     DEFAULT_STRING = i18nc("Default disposition", "Default - Allowed");
37     ALLOWED_STRING = i18nc("Allowed disposition", "Allowed");
38     BLOCKED_STRING = i18nc("Blocked disposition", "Blocked");
39
40     // Populate the translated filter option disposition strings.  Translated entries cannot be public static const.
41     FILTER_OPTION_NULL = QString();
42     FILTER_OPTION_APPLY = i18nc("Apply filter option", "Apply");
43     FILTER_OPTION_OVERRIDE = i18nc("Override filter option", "Override");
44
45     // Populate the translated navigation type strings.  Translated entries cannot be public static const.
46     NAVIGATION_TYPE_LINK = i18nc("Navigation type link", "Link");
47     NAVIGATION_TYPE_TYPED = i18nc("Navigation type typed", "Typed");
48     NAVIGATION_TYPE_FORM_SUBMITTED = i18nc("Navigation type form submitted", "Form Submitted");
49     NAVIGATION_TYPE_BACK_FORWARD = i18nc("Navigation type back/forward", "Back/Forward");
50     NAVIGATION_TYPE_RELOAD = i18nc("Navigation type reload", "Reload");
51     NAVIGATION_TYPE_REDIRECT = i18nc("Navigation type redirect", "Redirect");
52     NAVIGATION_TYPE_OTHER = i18nc("Navigation type other", "Other");
53
54     // Populate the translated resource type strings.  Translated entries cannot be public static const.
55     RESOURCE_TYPE_MAIN_FRAME = i18nc("Resource type main frame", "Main Frame");
56     RESOURCE_TYPE_SUB_FRAME = i18nc("Resource type sub frame", "Sub Frame");
57     RESOURCE_TYPE_STYLESHEET = i18nc("Resource type stylesheet", "Stylesheet");
58     RESOURCE_TYPE_SCRIPT = i18nc("Resource type script", "Script");
59     RESOURCE_TYPE_IMAGE = i18nc("Resource type image", "Image");
60     RESOURCE_TYPE_FONT_RESOURCE = i18nc("Resource type font", "Font");
61     RESOURCE_TYPE_SUB_RESOURCE = i18nc("Resource type sub resource", "Sub Resource");
62     RESOURCE_TYPE_OBJECT = i18nc("Resource type object", "Object");
63     RESOURCE_TYPE_MEDIA = i18nc("Resource type media", "Media");
64     RESOURCE_TYPE_WORKER = i18nc("Resource type worker", "Worker");
65     RESOURCE_TYPE_SHARED_WORKER = i18nc("Resource type shared worker", "Shared Worker");
66     RESOURCE_TYPE_PREFETCH = i18nc("Resource type prefetch", "Prefetch");
67     RESOURCE_TYPE_FAVICON = i18nc("Resource type favicon", "Favicon");
68     RESOURCE_TYPE_XHR = i18nc("Resource type XML HTTP request", "XML HTTP Request");
69     RESOURCE_TYPE_PING = i18nc("Resource type HTTP ping", "HTTP Ping");
70     RESOURCE_TYPE_SERVICE_WORKER = i18nc("Resource type service worker", "Service Worker");
71     RESOURCE_TYPE_CSP_REPORT = i18nc("Resource type content security policy report", "Content Security Policy Report");
72     RESOURCE_TYPE_PLUGIN_RESOURCE = i18nc("Resource type plugin request", "Plugin Request");
73     RESOURCE_TYPE_NAVIGATION_PRELOAD_MAIN_FRAME = i18nc("Resource type preload main frame", "Preload Main Frame");
74     RESOURCE_TYPE_NAVIGATION_PRELOAD_SUB_FRAME = i18nc("Resource type preload sub frame", "Preload Sub Frame");
75     RESOURCE_TYPE_UNKNOWN = i18nc("Resource type unknown", "Unknown");
76
77     // Populate the translated sublist strings.  Translated entries cannot be public static const.
78     MAIN_ALLOWLIST_STRING = i18nc("Main allowlist sublist", "Main Allow List");
79     MAIN_BLOCKLIST_STRING = i18nc("Main blocklist sublist", "Main Block List");
80     INITIAL_DOMAIN_BLOCKLIST_STRING = i18nc("Initial domain blocklist string", "Initial Domain Block List");
81
82     // Populate the filter lists.
83     ultraListStructPointer = populateFilterList(QLatin1String(":/filterlists/ultralist.txt"));
84     ultraPrivacyStructPointer = populateFilterList(QLatin1String(":/filterlists/ultraprivacy.txt"));
85     easyListStructPointer = populateFilterList(QLatin1String(":/filterlists/easylist.txt"));
86     easyPrivacyStructPointer = populateFilterList(QLatin1String(":/filterlists/easyprivacy.txt"));
87     fanboyAnnoyanceStructPointer = populateFilterList(QLatin1String(":/filterlists/fanboy-annoyance.txt"));
88 }
89
90 bool FilterListHelper::checkFilterLists(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer) const
91 {
92     // Initiate a continue checking tracker.  If the tracker changes to false, all process of the request will be stopped.
93     bool continueChecking = true;
94
95     // Create a URL struct.
96     UrlStruct urlStruct;
97
98     // Get the URLs.
99     QUrl firstPartyUrl = urlRequestInfo.firstPartyUrl();
100     QUrl requestUrl = urlRequestInfo.requestUrl();
101
102     // Get the hosts.
103     QString firstPartyHost = firstPartyUrl.host();
104     QString requestHost = requestUrl.host();
105
106     // Determine if this is a third-party request.
107     urlStruct.isThirdPartyRequest = (firstPartyHost != requestHost);
108
109     // Get the request URL string.
110     urlStruct.urlString = requestUrl.toString();
111
112     // Create a URL string with separators.
113     urlStruct.urlStringWithSeparators = urlStruct.urlString;
114
115     // Replace the separators characters with `^`.
116     urlStruct.urlStringWithSeparators.replace(QLatin1Char(':'), QLatin1Char('^'));
117     urlStruct.urlStringWithSeparators.replace(QLatin1Char('/'), QLatin1Char('^'));
118     urlStruct.urlStringWithSeparators.replace(QLatin1Char('?'), QLatin1Char('^'));
119     urlStruct.urlStringWithSeparators.replace(QLatin1Char('='), QLatin1Char('^'));
120     urlStruct.urlStringWithSeparators.replace(QLatin1Char('&'), QLatin1Char('^'));
121
122     // Add a `^` to the end of the string.
123     urlStruct.urlStringWithSeparators.append(QLatin1Char('^'));
124
125     // Create truncated URL strings and initially populate it with the original URL strings.
126     urlStruct.truncatedUrlString = urlStruct.urlString;
127     urlStruct.truncatedUrlStringWithSeparators = urlStruct.urlStringWithSeparators;
128
129     // Get the index of the beginning of the fully qualified domain name.
130     int fqdnIndex = urlStruct.truncatedUrlString.indexOf(QLatin1String("://")) + 3;
131
132     // Truncate the URL to the beginning of the fully qualified domain name.
133     urlStruct.truncatedUrlString.remove(0, fqdnIndex);
134     urlStruct.truncatedUrlStringWithSeparators.remove(0, fqdnIndex);
135
136     // Check UltraList.
137     continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, ultraListStructPointer);
138
139     // Check UltraPrivacy.
140     if (continueChecking)
141         continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, ultraPrivacyStructPointer);
142
143     // Check EasyList.
144     if (continueChecking)
145         continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, easyListStructPointer);
146
147     // Check EasyPrivacy.
148     if (continueChecking)
149         continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, easyPrivacyStructPointer);
150
151     if (continueChecking)
152         continueChecking = checkIndividualList(urlRequestInfo, urlStruct, requestStructPointer, fanboyAnnoyanceStructPointer);
153
154     // Return the continue checking status.
155     return continueChecking;
156 }
157
158 bool FilterListHelper::blockRequest(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, const QString &filterListTitle, const int sublistInt,
159                                     EntryStruct *entryStructPointer) const
160 {
161     // Block the request.
162     urlRequestInfo.block(true);
163
164     // Populate the request struct.
165     populateRequestStruct(requestStructPointer, BLOCKED, filterListTitle, sublistInt, entryStructPointer);
166
167     // Log the block.
168     //qDebug().noquote().nospace() << "Blocked request:  " << urlRequestInfo.firstPartyUrl() << ",  Filter list entry:  " << entryStructPointer->appliedEntry;
169
170     // Returning `false` stops all processing of the request.
171     return false;
172 }
173
174 bool FilterListHelper::checkIndividualList(QWebEngineUrlRequestInfo &urlRequestInfo, UrlStruct &urlStruct, RequestStruct *requestStructPointer, FilterListStruct *filterListStructPointer) const
175 {
176     // Check the main allow list.
177     for (auto filterListEntry = filterListStructPointer->mainAllowList.begin(); filterListEntry != filterListStructPointer->mainAllowList.end(); ++filterListEntry)
178     {
179         // Get the entry struct.
180         EntryStruct *entryStructPointer = *filterListEntry;
181
182         // TODO.  Temporarily ignore empty applied entries.
183         if (!entryStructPointer->appliedEntry.isEmpty())
184         {
185             // Check if the URL string contains the applied entry.
186             if (urlStruct.urlString.contains(entryStructPointer->appliedEntry) || urlStruct.urlStringWithSeparators.contains(entryStructPointer->appliedEntry))
187             {
188                 // Allow the request.
189                 urlRequestInfo.block(false);
190
191                 // Populate the request struct.
192                 populateRequestStruct(requestStructPointer, ALLOWED, filterListStructPointer->title, MAIN_ALLOWLIST, entryStructPointer);
193
194                 // Log the allow.
195                 //qDebug().noquote().nospace() << "Allowed request:  " << urlStruct.urlString << ", Filter list entry:  " << entryStructPointer->appliedEntry;
196
197                 // Returning `false` stops all processing of the request.
198                 return false;
199             }
200         }
201     }
202
203     // Check the main block list.
204     for (auto filterListEntry = filterListStructPointer->mainBlockList.begin(); filterListEntry != filterListStructPointer->mainBlockList.end(); ++filterListEntry)
205     {
206         // Get the entry struct.
207         EntryStruct *entryStructPointer = *filterListEntry;
208
209         // TODO.  Temporarily ignore empty applied entries.
210         if (!entryStructPointer->appliedEntry.isEmpty())
211         {
212             // Check if the URL string contains the applied entry.
213             if (urlStruct.urlString.contains(entryStructPointer->appliedEntry) || urlStruct.urlStringWithSeparators.contains(entryStructPointer->appliedEntry))
214             {
215                 // Check the third-party status.
216                 bool continueChecking = checkThirdParty(urlRequestInfo, requestStructPointer, urlStruct.isThirdPartyRequest, filterListStructPointer->title, MAIN_BLOCKLIST, entryStructPointer);
217
218                 // Stop processing the filter lists if continue checking is `false`.  Returning false halts all processing at upper levels.
219                 if (continueChecking == false)
220                     return false;
221             }
222         }
223     }
224
225     // Check the initial domain block list.
226     for (auto filterListEntry = filterListStructPointer->initialDomainBlockList.begin(); filterListEntry != filterListStructPointer->initialDomainBlockList.end(); ++filterListEntry)
227     {
228         // Get the entry struct.
229         EntryStruct *entryStructPointer = *filterListEntry;
230
231         // Check if the truncated URL string begins with the applied entry.
232         if (urlStruct.truncatedUrlString.startsWith(entryStructPointer->appliedEntry) || urlStruct.truncatedUrlStringWithSeparators.startsWith(entryStructPointer->appliedEntry))
233         {
234             // Check the third-party status.
235             bool continueChecking = checkThirdParty(urlRequestInfo, requestStructPointer, urlStruct.isThirdPartyRequest, filterListStructPointer->title, INITIAL_DOMAIN_BLOCKLIST, entryStructPointer);
236
237             // Stop processing the filter lists if continue checking is `false`.  Returning false halts all processing at upper levels.
238             if (continueChecking == false)
239                 return false;
240         }
241     }
242
243     // Return `true` to continue processing the URL request.
244     return true;
245 }
246
247 bool FilterListHelper::checkThirdParty(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, const bool isThirdPartyRequest, const QString &filterListTitle,
248                                        const int sublistInt, EntryStruct *entryStructPointer) const
249 {
250     // Check third-party status.
251     if (entryStructPointer->thirdParty == FilterOptionEnum::Disposition::Null)  // Ignore third-party status.
252     {
253         // Check if filter options are applied.
254         if (entryStructPointer->hasFilterOptions)  // Filter options are applied.
255         {
256             // Process the filter options.
257             return processFilterOptions(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
258         }
259         else  // Filter options are not applied.
260         {
261             // Block the request.
262             return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
263         }
264     }
265     else if ((entryStructPointer->thirdParty == FilterOptionEnum::Disposition::Apply) && isThirdPartyRequest)  // Block third-party request.
266     {
267         // Check if filter options are applied.
268         if (entryStructPointer->hasFilterOptions)  // Filter options are applied.
269         {
270             // Process the filter options.
271             return processFilterOptions(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
272         }
273         else  // Filter options are not applied.
274         {
275             // Block the request.
276             return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
277         }
278     }
279     else if ((entryStructPointer->thirdParty == FilterOptionEnum::Disposition::Override) && !isThirdPartyRequest)  // Block first-party requests.
280     {
281         // Check if filter options are applied.
282         if (entryStructPointer->hasFilterOptions)  // Filter options are applied.
283         {
284             // Process the filter options.
285             return processFilterOptions(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
286         }
287         else  // Filter options are not applied.
288         {
289             // Block the request.
290             return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
291         }
292     }
293
294     // Returning `true` continues to process the filter lists.  Returning `false` halts all processing of the filter lists.
295     return true;
296 }
297
298 QString FilterListHelper::getDispositionString(int dispositionInt) const
299 {
300     // Return the translated disposition string.
301     switch (dispositionInt)
302     {
303         case ALLOWED: return ALLOWED_STRING;
304         case BLOCKED: return BLOCKED_STRING;
305         default: return DEFAULT_STRING;
306     }
307 }
308
309 QString FilterListHelper::getFilterOptionDispositionString(const FilterOptionEnum::Disposition filterOptionDisposition) const
310 {
311     // Return the translated filter option disposition string.
312     switch (filterOptionDisposition)
313     {
314         case FilterOptionEnum::Disposition::Apply: return FILTER_OPTION_APPLY;
315         case FilterOptionEnum::Disposition::Override: return FILTER_OPTION_OVERRIDE;
316         default: return FILTER_OPTION_NULL;
317     }
318 }
319
320 QString FilterListHelper::getNavigationTypeString(int navigationTypeInt) const
321 {
322     // Return the translated navigation type string.
323     switch (navigationTypeInt)
324     {
325         case QWebEngineUrlRequestInfo::NavigationTypeLink: return NAVIGATION_TYPE_LINK;
326         case QWebEngineUrlRequestInfo::NavigationTypeTyped: return NAVIGATION_TYPE_TYPED;
327         case QWebEngineUrlRequestInfo::NavigationTypeFormSubmitted: return NAVIGATION_TYPE_FORM_SUBMITTED;
328         case QWebEngineUrlRequestInfo::NavigationTypeBackForward: return NAVIGATION_TYPE_BACK_FORWARD;
329         case QWebEngineUrlRequestInfo::NavigationTypeReload: return NAVIGATION_TYPE_RELOAD;
330         case QWebEngineUrlRequestInfo::NavigationTypeRedirect: return NAVIGATION_TYPE_REDIRECT;
331         default: return NAVIGATION_TYPE_OTHER;
332     }
333 }
334
335 QString FilterListHelper::getResourceTypeString(int resourceTypeInt) const
336 {
337     // Return the translated resource type string.
338     switch (resourceTypeInt)
339     {
340         case QWebEngineUrlRequestInfo::ResourceTypeMainFrame: return RESOURCE_TYPE_MAIN_FRAME;
341         case QWebEngineUrlRequestInfo::ResourceTypeSubFrame: return RESOURCE_TYPE_SUB_FRAME;
342         case QWebEngineUrlRequestInfo::ResourceTypeStylesheet: return RESOURCE_TYPE_STYLESHEET;
343         case QWebEngineUrlRequestInfo::ResourceTypeScript: return RESOURCE_TYPE_SCRIPT;
344         case QWebEngineUrlRequestInfo::ResourceTypeImage: return RESOURCE_TYPE_IMAGE;
345         case QWebEngineUrlRequestInfo::ResourceTypeFontResource: return RESOURCE_TYPE_FONT_RESOURCE;
346         case QWebEngineUrlRequestInfo::ResourceTypeSubResource: return RESOURCE_TYPE_SUB_RESOURCE;
347         case QWebEngineUrlRequestInfo::ResourceTypeObject: return RESOURCE_TYPE_OBJECT;
348         case QWebEngineUrlRequestInfo::ResourceTypeMedia: return RESOURCE_TYPE_MEDIA;
349         case QWebEngineUrlRequestInfo::ResourceTypeWorker: return RESOURCE_TYPE_WORKER;
350         case QWebEngineUrlRequestInfo::ResourceTypeSharedWorker: return RESOURCE_TYPE_SHARED_WORKER;
351         case QWebEngineUrlRequestInfo::ResourceTypePrefetch: return RESOURCE_TYPE_PREFETCH;
352         case QWebEngineUrlRequestInfo::ResourceTypeFavicon: return RESOURCE_TYPE_FAVICON;
353         case QWebEngineUrlRequestInfo::ResourceTypeXhr: return RESOURCE_TYPE_XHR;
354         case QWebEngineUrlRequestInfo::ResourceTypePing: return RESOURCE_TYPE_PING;
355         case QWebEngineUrlRequestInfo::ResourceTypeServiceWorker: return RESOURCE_TYPE_SERVICE_WORKER;
356         case QWebEngineUrlRequestInfo::ResourceTypeCspReport: return RESOURCE_TYPE_CSP_REPORT;
357         case QWebEngineUrlRequestInfo::ResourceTypePluginResource: return RESOURCE_TYPE_PLUGIN_RESOURCE;
358         case QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadMainFrame: return RESOURCE_TYPE_NAVIGATION_PRELOAD_MAIN_FRAME;
359         case QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadSubFrame: return RESOURCE_TYPE_NAVIGATION_PRELOAD_SUB_FRAME;
360         default: return RESOURCE_TYPE_UNKNOWN;
361     }
362 }
363
364 QString FilterListHelper::getSublistName(int sublistInt) const
365 {
366     // Return the name of the requested sublist.
367     switch (sublistInt)
368     {
369         case MAIN_ALLOWLIST: return MAIN_ALLOWLIST_STRING;
370         case MAIN_BLOCKLIST: return MAIN_BLOCKLIST_STRING;
371         case INITIAL_DOMAIN_BLOCKLIST: return INITIAL_DOMAIN_BLOCKLIST_STRING;
372         default: return QString();  // The default return should never be reached.
373     }
374 }
375
376 FilterListStruct* FilterListHelper::populateFilterList(const QString &filterListFileName) const
377 {
378     // Get the filter list file.
379     QFile filterListFile(filterListFileName);
380
381     // Open the filter list file.
382     filterListFile.open(QIODevice::ReadOnly);
383
384     // Create a filter list text stream.
385     QTextStream filterListTextStream(&filterListFile);
386
387     // Create a filter list struct.
388     FilterListStruct *filterListStructPointer = new FilterListStruct;
389
390     // Populate the filter list file name.
391     filterListStructPointer->filePath = filterListFileName;
392
393     // Create a filter list string.
394     QString filterListString;
395
396     // Process each line of the filter list.
397     while (filterListTextStream.readLineInto(&filterListString)) {
398         // Create an entry struct.
399         EntryStruct *entryStructPointer = new EntryStruct;
400
401         // Store the original entry.
402         entryStructPointer->originalEntry = filterListString;
403
404         // Process the entry.
405         if (filterListString.isEmpty())  // Ignore empty lines.
406         {
407             // Do nothing.
408
409             // Log the dropping of the line.
410             //qDebug().noquote().nospace() << filterListString << " NOT added from " << filterListFileName << " (empty line).";
411         }
412         else if (filterListString.startsWith(QLatin1Char('[')))  // The line starts with `[`, which is the file format.
413         {
414             // Do nothing.
415
416             // Log the dropping of the line.
417             //qDebug().noquote().nospace() << filterListString << " NOT added from " << filterListFileName << " (file format).";
418         }
419         else if (filterListString.contains(QLatin1String("##")) ||
420                  filterListString.contains(QLatin1String("#?#")) ||
421                  filterListString.contains(QLatin1String("#@#")) ||
422                  filterListString.contains(QLatin1String("#$#")))  // The line contains unimplemented content filtering.
423         {
424             // Do nothing.
425
426             // Log the dropping of the line.
427             //qDebug().noquote().nospace() << filterListString << " NOT added from " << filterListFileName << " (content filtering).";
428         }
429         else if (filterListString.startsWith(QLatin1Char('!')))  // The line starts with `!`, which are comments.
430         {
431             if (filterListString.startsWith(QLatin1String("! Title: ")))  // The line contains the title.
432             {
433                 // Add the title to the filter list struct.
434                 filterListStructPointer->title = filterListString.remove(0, 9);
435
436                 // Log the addition of the filter list title.
437                 //qDebug().noquote().nospace() << "Filter list title:  " << filterListString << " added from " << filterListFileName;
438             }
439             else if (filterListString.startsWith(QLatin1String("! Version: ")))  // The line contains the version.
440             {
441                 // Add the version to the filter list struct.
442                 filterListStructPointer->version = filterListString.remove(0, 11);
443
444                 // Log the addition of the filter list version.
445                 //qDebug().noquote().nospace() << "Filter list version:  " << filterListString << " added from " << filterListFileName;
446             }
447
448             // Else do nothing.
449
450             // Log the dropping of the line.
451             //qDebug().noquote().nospace() << originalFilterListString << " NOT added from " << filterListFileName;
452         }
453         else  // Process the filter options.
454         {
455             // Split any filter options from the end of the string.
456             QStringList splitEntryStringList = filterListString.split(QLatin1Char('$'));
457
458             // Store the entry without the filter options as the filter list string.
459             filterListString = splitEntryStringList[0];
460
461             // Create a popup only filter option tracker.
462             bool popupOnlyFilterOption = false;
463
464             // Process the filter options if they exist.
465             if (splitEntryStringList.size() > 1)
466             {
467                 // Store the filter options.
468                 entryStructPointer->filterOptions = splitEntryStringList[1];
469
470                 // Split the filter options.
471                 QStringList filterOptionsList = splitEntryStringList[1].split(QLatin1Char(','));
472
473                 // Check if the entry has a single popup filter option as Qt WebEngine doesn't know how to process them.
474                 if ((filterOptionsList.size() == 1) && (filterOptionsList[0] == QLatin1String("popup")))  // This entry has a single popup filter option.
475                 {
476                     // Set the popup only filter option flag.
477                     popupOnlyFilterOption = true;
478                 }
479                 else  // This entry has filter options besides popup.
480                 {
481                     // Initialize an override struct.
482                     OverrideStruct overrideStruct;
483
484                     // Populate the filter options entries.
485                     foreach (QString filterOption, filterOptionsList)
486                     {
487                         // Populate the third-party options.
488                         if (filterOption == QLatin1String("third-party")) entryStructPointer->thirdParty = FilterOptionEnum::Disposition::Apply;
489                         if (filterOption == QLatin1String("~third-party")) entryStructPointer->thirdParty = FilterOptionEnum::Disposition::Override;
490
491                         // Populate the filter options.
492                         if (filterOption == QLatin1String("document"))
493                         {
494                             // Populate the main frame option.
495                             entryStructPointer->mainFrame = FilterOptionEnum::Disposition::Apply;
496
497                             // Set the filter options flag.
498                             entryStructPointer->hasFilterOptions = true;
499                         }
500
501                         if (filterOption == QLatin1String("font"))
502                         {
503                             // Populate the font option.
504                             entryStructPointer->font = FilterOptionEnum::Disposition::Apply;
505
506                             // Set the filter options flag.
507                             entryStructPointer->hasFilterOptions = true;
508                         }
509
510                         if (filterOption == QLatin1String("image"))
511                         {
512                             // Populate the image option.
513                             entryStructPointer->image = FilterOptionEnum::Disposition::Apply;
514
515                             // Set the filter options flag.
516                             entryStructPointer->hasFilterOptions = true;
517                         }
518
519                         if (filterOption == QLatin1String("media"))
520                         {
521                             // Populate the media option.
522                             entryStructPointer->media = FilterOptionEnum::Disposition::Apply;
523
524                             // Set the filter options flag.
525                             entryStructPointer->hasFilterOptions = true;
526                         }
527
528                         if (filterOption == QLatin1String("object"))
529                         {
530                             // Populate the object option.
531                             entryStructPointer->object = FilterOptionEnum::Disposition::Apply;
532
533                             // Set the filter options flag.
534                             entryStructPointer->hasFilterOptions = true;
535                         }
536
537                         if (filterOption == QLatin1String("other"))
538                         {
539                             // Populate the other option.
540                             entryStructPointer->other = FilterOptionEnum::Disposition::Apply;
541
542                             // Set the filter options flag.
543                             entryStructPointer->hasFilterOptions = true;
544                         }
545
546                         if (filterOption == QLatin1String("ping"))
547                         {
548                             // Populate the ping option.
549                             entryStructPointer->ping = FilterOptionEnum::Disposition::Apply;
550
551                             // Set the filter options flag.
552                             entryStructPointer->hasFilterOptions = true;
553                         }
554
555                         if (filterOption == QLatin1String("script"))
556                         {
557                             // Populate the script option.
558                             entryStructPointer->script = FilterOptionEnum::Disposition::Apply;
559
560                             // Set the filter options flag.
561                             entryStructPointer->hasFilterOptions = true;
562                         }
563
564                         if (filterOption == QLatin1String("stylesheet"))
565                         {
566                             // Populate the script option.
567                             entryStructPointer->styleSheet = FilterOptionEnum::Disposition::Apply;
568
569                             // Set the filter options flag.
570                             entryStructPointer->hasFilterOptions = true;
571                         }
572
573                         if (filterOption == QLatin1String("subdocument"))
574                         {
575                             // Populate the sub resource option.
576                             entryStructPointer->subFrame = FilterOptionEnum::Disposition::Apply;
577
578                             // Set the filter options flag.
579                             entryStructPointer->hasFilterOptions = true;
580                         }
581
582                         if (filterOption == QLatin1String("xmlhttprequest"))
583                         {
584                             //Populate the XML HTTP request option.
585                             entryStructPointer->xmlHttpRequest = FilterOptionEnum::Disposition::Apply;
586
587                             // Set the filter options flag.
588                             entryStructPointer->hasFilterOptions = true;
589                         }
590
591                         // Populate the override struct.
592                         if (filterOption == QLatin1String("~document"))
593                         {
594                             // Populate the override struct.
595                             overrideStruct.hasOverride = true;
596                             overrideStruct.mainFrame = true;
597                         }
598
599                         if (filterOption == QLatin1String("~font"))
600                         {
601                             // Populate the override struct.
602                             overrideStruct.hasOverride = true;
603                             overrideStruct.font = true;
604                         }
605
606                         if (filterOption == QLatin1String("~image"))
607                         {
608                             // Populate the override struct.
609                             overrideStruct.hasOverride = true;
610                             overrideStruct.image = true;
611                         }
612
613                         if (filterOption == QLatin1String("~media"))
614                         {
615                             // Populate the override struct.
616                             overrideStruct.hasOverride = true;
617                             overrideStruct.media = true;
618                         }
619
620                         if (filterOption == QLatin1String("~object"))
621                         {
622                             // Populate the override struct.
623                             overrideStruct.hasOverride = true;
624                             overrideStruct.object = true;
625                         }
626
627                         if (filterOption == QLatin1String("~other"))
628                         {
629                             // Populate the override struct.
630                             overrideStruct.hasOverride = true;
631                             overrideStruct.other = true;
632                         }
633
634                         if (filterOption == QLatin1String("~ping"))
635                         {
636                             // Populate the override struct.
637                             overrideStruct.hasOverride = true;
638                             overrideStruct.ping = true;
639                         }
640
641                         if (filterOption == QLatin1String("~script"))
642                         {
643                             // Populate the override struct.
644                             overrideStruct.hasOverride = true;
645                             overrideStruct.script = true;
646                         }
647
648                         if (filterOption == QLatin1String("~stylesheet"))
649                         {
650                             // Populate the override struct.
651                             overrideStruct.hasOverride = true;
652                             overrideStruct.styleSheet = true;
653                         }
654
655                         if (filterOption == QLatin1String("~subdocument"))
656                         {
657                             // Populate the override struct.
658                             overrideStruct.hasOverride = true;
659                             overrideStruct.subFrame = true;
660                         }
661
662                         if (filterOption == QLatin1String("~xmlhttprequest"))
663                         {
664                             // Populate the override struct.
665                             overrideStruct.hasOverride = true;
666                             overrideStruct.xmlHttpRequest = true;
667                         }
668                     }
669
670                     // Apply the overrides.
671                     if (overrideStruct.hasOverride)
672                     {
673                         // Font.
674                         if (overrideStruct.font)
675                             entryStructPointer->font = FilterOptionEnum::Disposition::Override;
676                         else
677                             entryStructPointer->font = FilterOptionEnum::Disposition::Apply;
678
679                         // Image.
680                         if (overrideStruct.image)
681                             entryStructPointer->image = FilterOptionEnum::Disposition::Override;
682                         else
683                             entryStructPointer->image = FilterOptionEnum::Disposition::Apply;
684
685                         // Main Frame (document).
686                         if (overrideStruct.mainFrame)
687                             entryStructPointer->mainFrame = FilterOptionEnum::Disposition::Override;
688                         else
689                             entryStructPointer->mainFrame = FilterOptionEnum::Disposition::Apply;
690
691                         // Media.
692                         if (overrideStruct.media)
693                             entryStructPointer->media = FilterOptionEnum::Disposition::Override;
694                         else
695                             entryStructPointer->media = FilterOptionEnum::Disposition::Apply;
696
697                         // Object.
698                         if (overrideStruct.object)
699                             entryStructPointer->object = FilterOptionEnum::Disposition::Override;
700                         else
701                             entryStructPointer->object = FilterOptionEnum::Disposition::Apply;
702
703                         // Other.
704                         if (overrideStruct.other)
705                             entryStructPointer->other = FilterOptionEnum::Disposition::Override;
706                         else
707                             entryStructPointer->other = FilterOptionEnum::Disposition::Apply;
708
709                         // Ping.
710                         if (overrideStruct.ping)
711                             entryStructPointer->ping = FilterOptionEnum::Disposition::Override;
712                         else
713                             entryStructPointer->ping = FilterOptionEnum::Disposition::Apply;
714
715                         //  Script.
716                         if (overrideStruct.script)
717                             entryStructPointer->script = FilterOptionEnum::Disposition::Override;
718                         else
719                             entryStructPointer->script = FilterOptionEnum::Disposition::Apply;
720
721                         // Style Sheet.
722                         if (overrideStruct.styleSheet)
723                             entryStructPointer->styleSheet = FilterOptionEnum::Disposition::Override;
724                         else
725                             entryStructPointer->styleSheet = FilterOptionEnum::Disposition::Apply;
726
727                         // Sub Resource.
728                         if (overrideStruct.subFrame)
729                             entryStructPointer->subFrame = FilterOptionEnum::Disposition::Override;
730                         else
731                             entryStructPointer->subFrame = FilterOptionEnum::Disposition::Apply;
732
733                         // XML HTTP Request.
734                         if (overrideStruct.xmlHttpRequest)
735                             entryStructPointer->xmlHttpRequest = FilterOptionEnum::Disposition::Override;
736                         else
737                             entryStructPointer->xmlHttpRequest = FilterOptionEnum::Disposition::Apply;
738                     }
739                 }
740             }
741
742             // Drop entries that only have a single popup filter option as Qt WebEngine doesn't know how to process them.
743             if (popupOnlyFilterOption)  // This entry has a single popup filter option.
744             {
745                 // Do nothing.
746
747                 // Log the dropping of the line.
748                 //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " NOT added from " << filterListFileName << " (single popup filter option).";
749             }
750             else if (filterListString.startsWith(QLatin1String("@@")))  // Process an allow list entry.
751             {
752                 // Remove the initial `@@`.
753                 filterListString.remove(0, 2);
754
755                 // Remove any initial and trailing asterisks.
756                 removeInitialAndTrailingAsterisks(filterListString);
757
758                 // Add the applied entry to the struct.
759                 entryStructPointer->appliedEntry = filterListString;
760
761                 // Add the filter list entry struct to the main allow list.
762                 filterListStructPointer->mainAllowList.push_front(entryStructPointer);
763
764                 // Log the addition to the filter list.
765                 //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " added to Main Allow List from " << filterListFileName << ".";
766             }
767             else if (filterListString.startsWith(QLatin1String("||")))  // Process an initial domain allow list entry.
768             {
769                 // Remove the initial `||`.
770                 filterListString.remove(0, 2);
771
772                 // Add the applied entry to the struct.
773                 entryStructPointer->appliedEntry = filterListString;
774
775                 // Add the filter list entry struct to the initial domain block list.
776                 filterListStructPointer->initialDomainBlockList.push_front(entryStructPointer);
777
778                 // Log the addition to the filter list.
779                 //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " added to Initial Domain Block List from " << filterListFileName << ".";
780             }
781             else  // Process a block list entry.
782             {
783                 // Remove any initial and trailing asterisks.
784                 removeInitialAndTrailingAsterisks(filterListString);
785
786                 // Add the applied entry to the struct.
787                 entryStructPointer->appliedEntry = filterListString;
788
789                 // Add the filter list entry struct to the main block list.
790                 filterListStructPointer->mainBlockList.push_front(entryStructPointer);
791
792                 // Log the addition to the filter list.
793                 //qDebug().noquote().nospace() << entryStructPointer->originalEntry << " added to Main Block List from " << filterListFileName << ".";
794             }
795         }
796     }
797
798     // Close the filter list file.
799     filterListFile.close();
800
801     // Return the filter list pair.
802     return filterListStructPointer;
803 }
804
805 void FilterListHelper::populateRequestStruct(RequestStruct *requestStructPointer, const int disposition, const QString &filterListTitle, const int sublistInt, EntryStruct *entryStructPointer) const
806 {
807     // Populate the request struct.
808     requestStructPointer->dispositionInt = disposition;
809     requestStructPointer->filterListTitle = filterListTitle;
810     requestStructPointer->sublistInt = sublistInt;
811     requestStructPointer->entryStruct.appliedEntry = entryStructPointer->appliedEntry;
812     requestStructPointer->entryStruct.originalEntry = entryStructPointer->originalEntry;
813 }
814
815 bool FilterListHelper::processFilterOptions(QWebEngineUrlRequestInfo &urlRequestInfo, RequestStruct *requestStructPointer, const QString &filterListTitle, const int sublistInt,
816                                             EntryStruct *entryStructPointer) const
817 {
818     // Block font requests.
819     if ((entryStructPointer->font == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeFontResource))
820     {
821         // Block the request.
822         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
823     }
824
825     // Block image requests.
826     if ((entryStructPointer->image == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeImage))
827     {
828         // Block the request.
829         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
830     }
831
832     // Block main frame requests.
833     if ((entryStructPointer->mainFrame == FilterOptionEnum::Disposition::Apply) && ((requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeMainFrame) ||
834                                                                                     (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadMainFrame)))
835     {
836         // Block the request.
837         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
838     }
839
840     // Block media requests.
841     if ((entryStructPointer->media == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeMedia))
842     {
843         // Block the request.
844         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
845     }
846
847     // Block object requests.
848     if ((entryStructPointer->object == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeObject))
849     {
850         // Block the request.
851         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
852     }
853
854     // Block other requests.
855     if ((entryStructPointer->other == FilterOptionEnum::Disposition::Apply) && ((requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeSubResource) ||
856                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeWorker) ||
857                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeSharedWorker) ||
858                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypePrefetch) ||
859                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeFavicon) ||
860                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeServiceWorker) ||
861                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeCspReport) ||
862                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypePluginResource) ||
863                                                                                 (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeUnknown)))
864     {
865         // Block the request.
866         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
867     }
868
869     // Block ping requests
870     if ((entryStructPointer->ping == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypePing))
871     {
872         // Block the request.
873         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
874     }
875
876     // Block script requests.
877     if ((entryStructPointer->script == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeScript))
878     {
879         // Block the request.
880         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
881     }
882
883     // Block style sheet requests.
884     if ((entryStructPointer->styleSheet == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeStylesheet))
885     {
886         // Block the request.
887         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
888     }
889
890     // Block sub resource requests.
891     if ((entryStructPointer->subFrame == FilterOptionEnum::Disposition::Apply) && ((requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeSubFrame) ||
892                                                                                    (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadSubFrame)))
893     {
894         // Block the request.
895         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
896     }
897
898     // Block XML HTTP requests.
899     if ((entryStructPointer->xmlHttpRequest == FilterOptionEnum::Disposition::Apply) && (requestStructPointer->resourceTypeInt == QWebEngineUrlRequestInfo::ResourceTypeXhr))
900     {
901         // Block the request.
902         return blockRequest(urlRequestInfo, requestStructPointer, filterListTitle, sublistInt, entryStructPointer);
903     }
904
905     // Returning true continues processing the filter list.
906     return true;
907 }
908
909 void FilterListHelper::removeInitialAndTrailingAsterisks(QString &filterListEntry) const
910 {
911     // Remove the initial asterisk if it exists.
912     if (filterListEntry.startsWith(QLatin1Char('*')))
913         filterListEntry.remove(0, 1);
914
915     // Remove the final asterisk if it exists.
916     if (filterListEntry.endsWith(QLatin1Char('*')))
917         filterListEntry.chop(1);
918 }