/* * 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 #include /** * 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; }