diff options
Diffstat (limited to 'staging/adblock/filterlist.cpp')
-rw-r--r-- | staging/adblock/filterlist.cpp | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/staging/adblock/filterlist.cpp b/staging/adblock/filterlist.cpp new file mode 100644 index 0000000..be2bd4e --- /dev/null +++ b/staging/adblock/filterlist.cpp @@ -0,0 +1,137 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://library.iserlohn-fortress.net/aqua/smolbote.git + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "filterlist.h" +#include "rule.h" +#include <QRegularExpression> +#include <QTextStream> + +/** + * Documentation: + * + * https://help.eyeo.com/en/adblockplus/how-to-write-filters + * + * https://github.com/gorhill/uBlock/wiki/Introduction-to-basic-filtering-syntax + * https://github.com/gorhill/uBlock/wiki/Static-filter-syntax + * + */ + +using namespace AdblockPlus; + +FilterList::FilterList(QObject *parent) + : QObject(parent) +{ +} + +FilterList::~FilterList() +{ + qDeleteAll(m_rules); +} + +FilterList::ParseResult FilterList::parse(QTextStream &stream) +{ + FilterList::ParseResult result; + + if(stream.readLine().trimmed() != "[Adblock Plus 2.0]") { + result.state = FilterList::InvalidFormat; + return result; + } + + QString line; + while(stream.readLineInto(&line)) { + + if(!line.isEmpty()) { + ++result.lines_total; + + if(line.startsWith('!')) { + ++result.lines_comments; + parseComment(line); + + } else if(line.contains("##") || line.contains("#@#")) { + // ## is element hiding rule + // #@# is element hiding exception rule + if(qgetenv("PRINT_IGNORED") == "1") + qDebug("ignored: >%s<", qUtf8Printable(line)); + ++result.lines_ignored; + + } else { + if(parseRule(line)) + ++result.lines_parsed; + else { + if(qgetenv("PRINT_FAILED") == "1") + qDebug("failed: >%s<", qUtf8Printable(line)); + ++result.lines_failed; + } + } + } + } + + result.state = FilterList::Ok; + return result; +} + +void FilterList::parseComment(QString &line) +{ + m_comments.append(line); +} + +bool FilterList::parseRule(const QString &line) +{ + QString pattern = line; + Options opt; + + if(pattern.startsWith(QLatin1String("@@"))) { + pattern.remove(0, 2); + opt.exception = true; + } + + // parse options + if(pattern.contains('$')) { + const auto list = pattern.split('$'); + pattern = list.at(0); + const auto options = list.at(1).split(','); + + for(const auto &option : options) { + if(!opt.set(option)) + return false; + } + } + + if(pattern.startsWith("||") && pattern.endsWith("^")) { + // domain match + pattern = pattern.mid(2, pattern.length() - 3); + m_rules.append(new MatcherRule(pattern, opt)); + + } else if(pattern.startsWith("|") && pattern.endsWith("|")) { + // string equals + pattern = pattern.mid(1, pattern.length() - 2); + m_rules.append(new MatcherRule(pattern, opt)); + + } else if(pattern.startsWith("||")) { + // string starts with + pattern = pattern.mid(2, pattern.length() - 2); + m_rules.append(new MatcherRule(pattern, opt)); + + } else if(pattern.endsWith("|")) { + // string ends with + pattern = pattern.mid(0, pattern.length() - 1); + m_rules.append(new MatcherRule(pattern, opt)); + + } else if(pattern.startsWith("/") && pattern.endsWith("/")) { + // regular expression + pattern = pattern.mid(1, pattern.length() - 2); + m_rules.append(new RegexRule(pattern, opt)); + + } else { + // wildcard pattern + pattern = QRegularExpression::wildcardToRegularExpression(pattern); + m_rules.append(new RegexRule(pattern, opt)); + } + + return true; +} |