diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/browser.cpp | 6 | ||||
-rw-r--r-- | src/browser.h | 4 | ||||
-rw-r--r-- | src/webengine/filter.cpp | 116 | ||||
-rw-r--r-- | src/webengine/filter.h | 45 | ||||
-rw-r--r-- | src/webengine/urlinterceptor.cpp | 127 | ||||
-rw-r--r-- | src/webengine/urlinterceptor.h | 18 |
7 files changed, 190 insertions, 128 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 45a4c06..e8f2794 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,6 +38,8 @@ set(srclist subwindow/tabwidget.h # webengine + webengine/filter.cpp + webengine/filter.h webengine/urlinterceptor.cpp webengine/urlinterceptor.h webengine/webpage.cpp diff --git a/src/browser.cpp b/src/browser.cpp index 7bf4adf..7295b56 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -27,6 +27,7 @@ #include <version.h> #include <web/profilemanager.h> #include <web/webprofile.h> +#include "webengine/filter.h" Browser::Browser(int &argc, char *argv[], bool allowSecondary) : SingleApplication(argc, argv, allowSecondary, SingleApplication::User | SingleApplication::SecondaryNotification | SingleApplication::ExcludeAppVersion) @@ -58,7 +59,8 @@ QPair<QString, WebProfile *> Browser::loadProfile(const QString &id) { WebProfile *profile = m_profileManager->loadProfile(id); connect(profile, &WebProfile::downloadRequested, m_downloads.get(), &DownloadsWidget::addDownload); - profile->setRequestInterceptor(m_urlFilter.get()); + auto *interceptor = new UrlRequestInterceptor(m_urlFilter.get(), profile, profile); + profile->setRequestInterceptor(interceptor); return QPair<QString, WebProfile *>(m_profileManager->id(profile), profile); } @@ -107,7 +109,7 @@ void Browser::setup() // downloads m_downloads = std::make_unique<DownloadsWidget>(m_config->value<QString>("downloads.path").value()); // url request filter - m_urlFilter = std::make_unique<UrlRequestInterceptor>(m_config); + m_urlFilter = std::make_unique<Filter>(m_config); // cookie request filter // load profiles diff --git a/src/browser.h b/src/browser.h index b4352a7..6731f59 100644 --- a/src/browser.h +++ b/src/browser.h @@ -20,7 +20,7 @@ class Configuration; class BookmarksWidget; class DownloadsWidget; -class UrlRequestInterceptor; +class Filter; class MainWindow; class WebProfile; class Browser : public SingleApplication, public BrowserInterface @@ -62,7 +62,7 @@ private: std::shared_ptr<BookmarksWidget> m_bookmarks; std::unique_ptr<DownloadsWidget> m_downloads; ProfileManager *m_profileManager; - std::unique_ptr<UrlRequestInterceptor> m_urlFilter; + std::unique_ptr<Filter> m_urlFilter; QVector<MainWindow *> m_windows; QVector<Plugin> m_plugins; diff --git a/src/webengine/filter.cpp b/src/webengine/filter.cpp new file mode 100644 index 0000000..b250843 --- /dev/null +++ b/src/webengine/filter.cpp @@ -0,0 +1,116 @@ +/* + * 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/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "filter.h" +#include "urlinterceptor.h" +#include <QDir> +#include <QJsonArray> +#include <QJsonDocument> +#include <QTextStream> +#include <configuration/configuration.h> +#include "util.h" + + +QHash<QString, Filter::HostRule> parseHostlist(const QString &filename) +{ + QHash<QString, Filter::HostRule> rules; + + if(QFile hostfile(filename); 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 <redirect> <host> + // 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)) { + Filter::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; +} +/* +inline std::vector<FilterRule> parseAdBlockList(const QString &filename) +{ + std::vector<FilterRule> rules; + QFile list(filename); + + if(list.open(QIODevice::ReadOnly | QIODevice::Text), true) { + QTextStream l(&list); + QString line; + while(l.readLineInto(&line)) { + AdBlockRule rule(line); + if(rule.isEnabled()) { + rules.emplace_back(std::move(rule)); + } + } + list.close(); + } + + return rules; +}*/ + +Filter::Filter::Filter(const std::unique_ptr<Configuration> &config, QObject* parent) + : QObject(parent) +{ + // parse headers + if(const auto headers = config->value<QStringList>("filter.header"); headers) { + for(const QString &header : headers.value()) { + const auto list = header.split(QLatin1Literal(":")); + if(list.length() == 2) + m_headers.insert(list.at(0).toLatin1(), list.at(1).toLatin1()); + } + } + + const QStringList hostfiles = Util::files(config->value<QString>("filter.path").value()); + for(const QString &hostfile : hostfiles) { + m_hostlist.unite(parseHostlist(hostfile)); + } + + /* + auto filtersPath = config->value<QString>("filter.adblock"); + if(filtersPath) + filters = parseAdBlockList(filtersPath.value()); + */ +} + +std::optional<Filter::HostRule> Filter::hostlistRule(const QString& url) const +{ + if(!m_hostlist.contains(url)) + return std::nullopt; + + return std::optional<Filter::HostRule>(m_hostlist.value(url)); +} diff --git a/src/webengine/filter.h b/src/webengine/filter.h new file mode 100644 index 0000000..3eac5ee --- /dev/null +++ b/src/webengine/filter.h @@ -0,0 +1,45 @@ +/* + * 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/gitea/aqua/smolbote + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#ifndef SMOLBOTE_FILTER_H +#define SMOLBOTE_FILTER_H + +#include "urlfilter/filterrule.h" +#include <QByteArray> +#include <QVector> +#include <optional> + +class Configuration; +class Filter : public QObject +{ + Q_OBJECT +public: + struct HostRule { + bool isBlocking; + }; + + explicit Filter(const std::unique_ptr<Configuration> &config, QObject *parent = nullptr); + ~Filter() override = default; + + const QHash<QString, HostRule> hostlist() const + { + return qAsConst(m_hostlist); + } + std::optional<HostRule> hostlistRule(const QString &url) const; + + const QMap<QByteArray, QByteArray> headers() const + { + return qAsConst(m_headers); + } + +private: + QHash<QString, HostRule> m_hostlist; + QMap<QByteArray, QByteArray> m_headers; +}; + +#endif // SMOLBOTE_FILTER_H diff --git a/src/webengine/urlinterceptor.cpp b/src/webengine/urlinterceptor.cpp index db4aea9..cf9b85f 100644 --- a/src/webengine/urlinterceptor.cpp +++ b/src/webengine/urlinterceptor.cpp @@ -14,127 +14,32 @@ #include <QTextStream> #include <boost/algorithm/string.hpp> #include <configuration/configuration.h> +#include "filter.h" +#include <web/webprofile.h> -inline std::vector<FilterRule> parseAdBlockList(const QString &filename) -{ - std::vector<FilterRule> rules; - QFile list(filename); - - if(list.open(QIODevice::ReadOnly | QIODevice::Text), true) { - QTextStream l(&list); - QString line; - while(l.readLineInto(&line)) { - AdBlockRule rule(line); - if(rule.isEnabled()) { - rules.emplace_back(std::move(rule)); - } - } - list.close(); - } - - return rules; -} +// test DNT on https://browserleaks.com/donottrack -UrlRequestInterceptor::UrlRequestInterceptor(const std::unique_ptr<Configuration> &config, QObject *parent) +UrlRequestInterceptor::UrlRequestInterceptor(Filter* filter, WebProfile* profile, QObject* parent) : QWebEngineUrlRequestInterceptor(parent) { - QDir hostsD(config->value<QString>("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<std::vector<std::string>>("filter.header"); - if(header) { - for(const std::string &h : header.value()) { - std::vector<std::string> 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); - } - } - - auto filtersPath = config->value<QString>("filter.adblock"); - if(filtersPath) - filters = parseAdBlockList(filtersPath.value()); + Q_CHECK_PTR(filter); + m_filter = filter; + Q_CHECK_PTR(profile); + m_profile = profile; } -// 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)); + auto hostlistCheck = m_filter->hostlistRule(info.requestUrl().host()); + if(hostlistCheck) { + info.block(hostlistCheck.value().isBlocking); } - if(rules.contains(info.requestUrl().host())) { - info.block(rules.value(info.requestUrl().host()).isBlocking); - return; + // set headers + for(auto i = m_filter->headers().constBegin(); i != m_filter->headers().constEnd(); ++i) { + info.setHttpHeader(i.key(), i.value()); } - - const uint domainHash = qHash(info.firstPartyUrl().host()); - const QWebEngineUrlRequestInfo::ResourceType type = info.resourceType(); - const QUrl requestUrl = info.requestUrl(); - for(const FilterRule &rule : filters) { - if(rule.matchesDomain(domainHash) && rule.matchesType(type) && rule.matchesUrl(requestUrl)) { - info.block(rule.isBlocking()); -#ifdef QT_DEBUG - qDebug("--> blocked %s", qUtf8Printable(info.requestUrl().toString())); -#endif - break; - } + for(auto i = m_profile->headers().constBegin(); i != m_profile->headers().constEnd(); ++i) { + info.setHttpHeader(i.key(), i.value()); } } - -QHash<QString, UrlRequestInterceptor::HostRule> parse(const QString &filename) -{ - QHash<QString, UrlRequestInterceptor::HostRule> 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 <redirect> <host> - // 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; -} diff --git a/src/webengine/urlinterceptor.h b/src/webengine/urlinterceptor.h index 575e0c9..420a161 100644 --- a/src/webengine/urlinterceptor.h +++ b/src/webengine/urlinterceptor.h @@ -15,29 +15,21 @@ #include <QWebEngineUrlRequestInterceptor> #include <memory> -typedef std::pair<std::string, std::string> Header; - +class Filter; +class WebProfile; class Configuration; class UrlRequestInterceptor : public QWebEngineUrlRequestInterceptor { Q_OBJECT public: - struct HostRule { - bool isBlocking; - }; - - explicit UrlRequestInterceptor(const std::unique_ptr<Configuration> &config, QObject *parent = nullptr); + explicit UrlRequestInterceptor(Filter *filter, WebProfile *profile, QObject *parent = nullptr); ~UrlRequestInterceptor() override = default; void interceptRequest(QWebEngineUrlRequestInfo &info) override; private: - QHash<QString, HostRule> rules; - std::vector<FilterRule> filters; - std::vector<Header> m_headers; + Filter *m_filter; + WebProfile *m_profile; }; -QHash<QString, UrlRequestInterceptor::HostRule> parse(const QString &filename); -inline std::vector<FilterRule> parseAdBlockList(const QString &filename); - #endif // SMOLBOTE_URLREQUESTINTERCEPTOR_H |