/* * 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 #include /** * Documentation: * * https://adblockplus.org/filter-cheatsheet * https://help.eyeo.com/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 * */ const QLatin1String comment_lastModified("! Last modified: "); const QLatin1String comment_expires("! Expires: "); using namespace AdblockPlus; FilterList::FilterList(QIODevice &from) { if(from.isReadable() && from.isTextModeEnabled()) { while(from.bytesAvailable() > 0) { const auto line = from.readLine(512); if(line[0] == '!') { parseComment(line); } else if(line.contains("##") || line.contains("#@#")) { // ## is element hiding rule // #@# is element hiding exception rule } else { parseRule(line); } } } } void FilterList::parseComment(const QString &line) { if(line.startsWith(comment_lastModified)) { lastModified = QDateTime::fromString(line.mid(comment_lastModified.size()), "dd MMM yyyy HH:mm 'UTC'"); expires = lastModified; } else if(line.startsWith(comment_expires)) { const QRegularExpression time_re("(?:(\\d+) days)|(?:(\\d+) hours)"); const auto match = time_re.match(line); if(match.hasMatch()) { expires = expires.addDays(match.captured(1).toInt()); expires = expires.addSecs(match.captured(2).toInt() * 60 * 60); } } } Rule *FilterList::parseRule(const QByteArray &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); if(!opt.parseAbp(&options)) { return nullptr; } } if(pattern.startsWith("||") && pattern.endsWith("^")) { // domain match pattern = pattern.mid(2, pattern.length() - 3); return new MatcherRule(pattern, opt, MatcherRule::DomainMatch); } else if(pattern.startsWith("|") && pattern.endsWith("|")) { // string equals pattern = pattern.mid(1, pattern.length() - 2); return new MatcherRule(pattern, opt, MatcherRule::UrlEquals); } else if(pattern.startsWith("|")) { // string starts with pattern = pattern.mid(1, pattern.length() - 1); return new MatcherRule(pattern, opt, MatcherRule::UrlStartsWith); } else if(pattern.endsWith("|")) { // string ends with pattern = pattern.mid(0, pattern.length() - 1); return new MatcherRule(pattern, opt, MatcherRule::UrlEndsWith); } else if(pattern.startsWith("/") && pattern.endsWith("/")) { // regular expression pattern = pattern.mid(1, pattern.length() - 2); return new RegexRule(pattern, opt); } else if(!pattern.isEmpty()) { if(pattern.contains('*')) { // wildcard pattern pattern = QRegularExpression::wildcardToRegularExpression(pattern); return new RegexRule(pattern, opt); } else { // contains pattern return new MatcherRule(pattern, opt); } } return nullptr; } bool FilterList::filter(QWebEngineUrlRequestInfo &info) const { return false; }