/* * 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://neueland.iserlohn-fortress.net/smolbote.hg * * SPDX-License-Identifier: GPL-3.0 */ #include "urlinterceptor.h" #include #include #include #include #include #include UrlRequestInterceptor::UrlRequestInterceptor(const std::unique_ptr &config, QObject *parent) : QWebEngineUrlRequestInterceptor(parent) { QDir hostsD(config->value("filter.path").value()); const QStringList hostFiles = hostsD.entryList(QDir::Files); for(const QString &file : hostFiles) { const QString absPath = hostsD.absoluteFilePath(file); auto r = parse(absPath); #ifdef QT_DEBUG qDebug("Parsed %i rules from %s", r.count(), qUtf8Printable(absPath)); #endif rules.unite(r); } const auto header = config->value>("filter.header"); if(header) { for(const std::string &h : header.value()) { std::vector s; boost::split(s, h, boost::is_any_of(":=")); auto pair = std::make_pair(s.at(0), s.at(1)); m_headers.emplace_back(pair); } } QFile rules(config->value("filter.json-path").value()); if(rules.open(QIODevice::ReadOnly | QIODevice::Text)) { auto doc = QJsonDocument::fromJson(rules.readAll()).object(); Q_ASSERT(doc.value("domains").isArray()); for(QJsonValue d : doc.value("domains").toArray()) { domain.addDomain(d.toString()); } Q_ASSERT(doc.value("rules").isArray()); for(QJsonValue rule : doc.value("rules").toArray()) { auto p = std::make_unique(rule.toObject()); parseJson(p, rule.toObject()); domain.addRule(p); } rules.close(); } } // test DNT on https://browserleaks.com/donottrack void UrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) { for(const Header &header : m_headers) { info.setHttpHeader(QByteArray::fromStdString(header.first), QByteArray::fromStdString(header.second)); } if(rules.contains(info.requestUrl().host())) { info.block(rules.value(info.requestUrl().host()).isBlocking); return; } if(domain.hasMatch(info.requestUrl().host())) { domain.process(info); } #ifdef QT_DEBUG // qDebug("request>>>"); // qDebug("firstParty url=%s", qUtf8Printable(info.firstPartyUrl().toString())); // qDebug("firstParty host=%s", qUtf8Printable(info.firstPartyUrl().host())); // qDebug("request url=%s", qUtf8Printable(info.requestUrl().toString())); // qDebug("request host=%s", qUtf8Printable(info.requestUrl().host())); // qDebug("<<<"); #endif } QHash parse(const QString &filename) { QHash rules; QFile hostfile(filename); if(hostfile.open(QIODevice::ReadOnly | QIODevice::Text)) { // with a QTextStream we can read lines without getting linebreaks at the end QTextStream hostfile_stream(&hostfile); while(!hostfile_stream.atEnd()) { // read line and remove any whitespace at the end const QString &line = hostfile_stream.readLine().trimmed(); // skip comments and empty lines if(line.isEmpty() || line.startsWith('#')) continue; // everything else should be a rule // format is // 0.0.0.0 hostname const QStringList &parts = line.split(' '); const QString &redirect = parts.at(0); for(auto i = parts.constBegin() + 1; i != parts.constEnd(); ++i) { if(!rules.contains(*i)) { UrlRequestInterceptor::HostRule rule{}; rule.isBlocking = (redirect == "0.0.0.0"); rules.insert(*i, rule); } } // for(const QString &host : parts.mid(1)) { // if(!rules.contains(host)) { // UrlRequestInterceptor::HostRule rule{}; // rule.isBlocking = redirect == "0.0.0.0"; // rules.insert(host, rule); // } // } } hostfile.close(); } return rules; }