aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAqua-sama <aqua@iserlohn-fortress.net>2018-10-07 13:20:54 +0200
committerAqua-sama <aqua@iserlohn-fortress.net>2018-10-07 18:19:19 +0200
commit2a5ea0269a1f9511c51d661a6c7d7bdc7d0176fa (patch)
tree7649cf4c1c20bbe8c801d642148992eea314d3ec
parentAdd hint on enabling plugins to makepkg (diff)
downloadsmolbote-2a5ea0269a1f9511c51d661a6c7d7bdc7d0176fa.tar.xz
Expand HTTP header settings #4
- add doc/Usage/Filter.asciidoc to explain the usage of the filter headers - add HTTP headers to Profile (section "headers") - Use request interceptor to apply filter headers, then profile headers - add insert/delete actions to ProfileEditor
-rw-r--r--doc/Usage/Filter.asciidoc12
-rw-r--r--lib/configuration/configuration.h7
-rw-r--r--lib/web/profilemanager.cpp13
-rw-r--r--lib/web/webprofile.cpp14
-rw-r--r--lib/web/webprofile.h9
-rw-r--r--plugins/ProfileEditor/CMakeLists.txt3
-rw-r--r--plugins/ProfileEditor/forms/newhttpheaderdialog.cpp32
-rw-r--r--plugins/ProfileEditor/forms/newhttpheaderdialog.h33
-rw-r--r--plugins/ProfileEditor/forms/newhttpheaderdialog.ui84
-rw-r--r--plugins/ProfileEditor/forms/profileview.cpp46
-rw-r--r--plugins/ProfileEditor/forms/profileview.h3
-rw-r--r--plugins/ProfileEditor/forms/profileview.ui68
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/browser.cpp6
-rw-r--r--src/browser.h4
-rw-r--r--src/webengine/filter.cpp116
-rw-r--r--src/webengine/filter.h45
-rw-r--r--src/webengine/urlinterceptor.cpp127
-rw-r--r--src/webengine/urlinterceptor.h18
19 files changed, 512 insertions, 130 deletions
diff --git a/doc/Usage/Filter.asciidoc b/doc/Usage/Filter.asciidoc
new file mode 100644
index 0000000..d23d57a
--- /dev/null
+++ b/doc/Usage/Filter.asciidoc
@@ -0,0 +1,12 @@
+== Url Request Filter
+smolbote has a singular URL request filter that is installed onto all profiles.
+Any setting applied to it will be applied to all profiles.
+
+=== filter.header
+ A list of header-value pairs, separated by a colon (':').
+
+You can specify multiple headers by using --filter.header multiple times:
+[source, sh]
+----
+poi --filter.header "Dnt:1" --filter.header "Accept:text/html"
+----
diff --git a/lib/configuration/configuration.h b/lib/configuration/configuration.h
index d4770ae..1524536 100644
--- a/lib/configuration/configuration.h
+++ b/lib/configuration/configuration.h
@@ -56,6 +56,13 @@ public:
return std::optional<QString>(QString::fromStdString(this->value<std::string>(path).value()));
//return std::optional<QString>(vm[path].as<const char*>());
+ } else if constexpr(std::is_same_v<T, QStringList>) {
+ QStringList r;
+ for(const std::string &item : this->value<std::vector<std::string>>(path).value()) {
+ r.append(QString::fromStdString(item));
+ }
+ return std::optional<QStringList>(r);
+
} else if constexpr(std::is_same_v<T, std::string>) {
if(vm[path].value().type() == typeid(int)) {
diff --git a/lib/web/profilemanager.cpp b/lib/web/profilemanager.cpp
index 74ddc75..17435e8 100644
--- a/lib/web/profilemanager.cpp
+++ b/lib/web/profilemanager.cpp
@@ -73,6 +73,19 @@ WebProfile *ProfileManager::loadProfile(const QString &path)
this->m_profiles.at(id)->settings.setValue("attributes/" + QString::number(attr), value);
});
+ // headers
+ ptr->settings.beginGroup("headers");
+ for(const QString &key : ptr->settings.childKeys()) {
+ ptr->profile->setHttpHeader(key.toLatin1(), ptr->settings.value(key).toString().toLatin1());
+ }
+ ptr->settings.endGroup();
+ connect(ptr->profile, &WebProfile::headerChanged, ptr->profile, [this, id](const QString &name, const QString &value) {
+ this->m_profiles.at(id)->settings.setValue("headers/" + name, value);
+ });
+ connect(ptr->profile, &WebProfile::headerRemoved, ptr->profile, [this, id](const QString &name) {
+ this->m_profiles.at(id)->settings.remove("headers/" + name);
+ });
+
m_profiles[id] = std::move(ptr);
return m_profiles.at(id)->profile;
}
diff --git a/lib/web/webprofile.cpp b/lib/web/webprofile.cpp
index bd84d38..16c12b7 100644
--- a/lib/web/webprofile.cpp
+++ b/lib/web/webprofile.cpp
@@ -135,6 +135,20 @@ void WebProfile::setHttpUserAgent(const QString &userAgent)
emit propertyChanged("httpUserAgent", userAgent);
}
+void WebProfile::setHttpHeader(const QString& name, const QString& value)
+{
+ m_headers[name.toLatin1()] = value.toLatin1();
+ emit headerChanged(name, value);
+}
+
+void WebProfile::removeHttpHeader(const QString& name)
+{
+ if(m_headers.contains(name.toLatin1())) {
+ m_headers.remove(name.toLatin1());
+ emit headerRemoved(name);
+ }
+}
+
void WebProfile::setSpellCheckEnabled(bool enable)
{
QWebEngineProfile::setSpellCheckEnabled(enable);
diff --git a/lib/web/webprofile.h b/lib/web/webprofile.h
index b58fad7..269989b 100644
--- a/lib/web/webprofile.h
+++ b/lib/web/webprofile.h
@@ -65,6 +65,10 @@ public:
{
return qAsConst(m_cookies);
}
+ const QMap<QByteArray, QByteArray> headers() const
+ {
+ return qAsConst(m_headers);
+ }
// search url
QString search() const;
@@ -86,6 +90,8 @@ public:
void setHttpCacheMaximumSize(int maxSize);
void setHttpCacheType(int type);
void setHttpUserAgent(const QString &userAgent);
+ void setHttpHeader(const QString &name, const QString &value);
+ void removeHttpHeader(const QString &name);
void setSpellCheckEnabled(bool enable);
@@ -97,6 +103,8 @@ signals:
void propertyChanged(const QString &name, const QVariant &value);
void attributeChanged(const QWebEngineSettings::WebAttribute attribute, const bool value);
+ void headerChanged(const QString &name, const QString &value);
+ void headerRemoved(const QString &name);
private:
static WebProfile *profile;
@@ -107,6 +115,7 @@ private:
QUrl m_newtab = QUrl("about:blank");
QVector<QNetworkCookie> m_cookies;
+ QMap<QByteArray, QByteArray> m_headers;
};
#endif // SMOLBOTE_WEBENGINEPROFILE_H
diff --git a/plugins/ProfileEditor/CMakeLists.txt b/plugins/ProfileEditor/CMakeLists.txt
index d22d3d6..7c9a774 100644
--- a/plugins/ProfileEditor/CMakeLists.txt
+++ b/plugins/ProfileEditor/CMakeLists.txt
@@ -20,6 +20,9 @@ add_library(ProfileEditorPlugin SHARED
forms/newprofiledialog.cpp
forms/newprofiledialog.h
forms/newprofiledialog.ui
+ forms/newhttpheaderdialog.cpp
+ forms/newhttpheaderdialog.h
+ forms/newhttpheaderdialog.ui
)
target_include_directories(ProfileEditorPlugin
diff --git a/plugins/ProfileEditor/forms/newhttpheaderdialog.cpp b/plugins/ProfileEditor/forms/newhttpheaderdialog.cpp
new file mode 100644
index 0000000..3978c4e
--- /dev/null
+++ b/plugins/ProfileEditor/forms/newhttpheaderdialog.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "newhttpheaderdialog.h"
+#include "ui_newhttpheaderdialog.h"
+
+NewHttpHeaderDialog::NewHttpHeaderDialog(QWidget *parent) :
+ QDialog(parent),
+ ui(new Ui::NewHttpHeaderDialog)
+{
+ ui->setupUi(this);
+}
+
+NewHttpHeaderDialog::~NewHttpHeaderDialog()
+{
+ delete ui;
+}
+
+QString NewHttpHeaderDialog::header() const
+{
+ return ui->header->text();
+}
+
+QString NewHttpHeaderDialog::value() const
+{
+ return ui->value->text();
+}
diff --git a/plugins/ProfileEditor/forms/newhttpheaderdialog.h b/plugins/ProfileEditor/forms/newhttpheaderdialog.h
new file mode 100644
index 0000000..53a2a80
--- /dev/null
+++ b/plugins/ProfileEditor/forms/newhttpheaderdialog.h
@@ -0,0 +1,33 @@
+/*
+ * 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 NEWHTTPHEADERDIALOG_H
+#define NEWHTTPHEADERDIALOG_H
+
+#include <QDialog>
+
+namespace Ui {
+class NewHttpHeaderDialog;
+}
+
+class NewHttpHeaderDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit NewHttpHeaderDialog(QWidget *parent = nullptr);
+ ~NewHttpHeaderDialog();
+
+ QString header() const;
+ QString value() const;
+
+private:
+ Ui::NewHttpHeaderDialog *ui;
+};
+
+#endif // NEWHTTPHEADERDIALOG_H
diff --git a/plugins/ProfileEditor/forms/newhttpheaderdialog.ui b/plugins/ProfileEditor/forms/newhttpheaderdialog.ui
new file mode 100644
index 0000000..a457ba6
--- /dev/null
+++ b/plugins/ProfileEditor/forms/newhttpheaderdialog.ui
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>NewHttpHeaderDialog</class>
+ <widget class="QDialog" name="NewHttpHeaderDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>320</width>
+ <height>108</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="header_label">
+ <property name="text">
+ <string>Header</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="header"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="value_label">
+ <property name="text">
+ <string>Value</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="value"/>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>NewHttpHeaderDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>NewHttpHeaderDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/plugins/ProfileEditor/forms/profileview.cpp b/plugins/ProfileEditor/forms/profileview.cpp
index ccb4ae5..4a89af6 100644
--- a/plugins/ProfileEditor/forms/profileview.cpp
+++ b/plugins/ProfileEditor/forms/profileview.cpp
@@ -13,6 +13,7 @@
#include <QWebEngineSettings>
#include <QWebEngineCookieStore>
#include <QDateTime>
+#include "newhttpheaderdialog.h"
inline void connectSetting(QCheckBox *checkBox, WebProfile *profile, QWebEngineSettings::WebAttribute attr)
{
@@ -86,6 +87,28 @@ ProfileView::ProfileView(WebProfile *profile, QWidget *parent)
ui->storagePath_lineEdit->setText(m_profile->persistentStoragePath());
ui->cachePath_lineEdit->setText(m_profile->cachePath());
+ // headers tab
+ for(auto i = m_profile->headers().constBegin(); i != m_profile->headers().constEnd(); ++i) {
+ //ui->httpHeaders->addItem();
+ headerChanged(i.key(), i.value());
+ }
+ connect(m_profile, &WebProfile::headerChanged, this, &ProfileView::headerChanged);
+ connect(m_profile, &WebProfile::headerRemoved, this, &ProfileView::headerRemoved);
+ connect(ui->headers_insert, &QPushButton::clicked, m_profile, [this]() {
+ auto *dlg = new NewHttpHeaderDialog(this);
+ if(dlg->exec() == QDialog::Accepted) {
+ m_profile->setHttpHeader(dlg->header(), dlg->value());
+ }
+ delete dlg;
+ });
+ connect(ui->headers_delete, &QPushButton::clicked, m_profile, [this]() {
+ for(auto &list : ui->httpHeaders->selectedRanges()) {
+ for(int i = list.bottomRow(); i >= list.topRow(); --i) {
+ m_profile->removeHttpHeader(ui->httpHeaders->item(i, 0)->text());
+ }
+ }
+ });
+
// settings tab
connectSetting(ui->autoloadImages, m_profile, QWebEngineSettings::AutoLoadImages);
connectSetting(ui->autoloadIcons, m_profile, QWebEngineSettings::AutoLoadIconsForPage);
@@ -157,6 +180,29 @@ void ProfileView::loadCookies(QWebEngineCookieStore *store)
connect(ui->cookies_deleteAll, &QPushButton::clicked, store, &QWebEngineCookieStore::deleteAllCookies);
}
+void ProfileView::headerChanged(const QString &name, const QString &value)
+{
+ const auto items = ui->httpHeaders->findItems(name, Qt::MatchExactly);
+ if(!items.isEmpty()) {
+ QTableWidgetItem *valueItem = ui->httpHeaders->item(items.constFirst()->row(), 1);
+ valueItem->setText(value);
+ } else {
+ // new header
+ const int index = ui->httpHeaders->rowCount();
+ ui->httpHeaders->setRowCount(index + 1);
+ ui->httpHeaders->setItem(index, 0, new QTableWidgetItem(name));
+ ui->httpHeaders->setItem(index, 1, new QTableWidgetItem(value));
+ }
+}
+
+void ProfileView::headerRemoved(const QString& name)
+{
+ const auto items = ui->httpHeaders->findItems(name, Qt::MatchExactly);
+ if(!items.isEmpty()) {
+ ui->httpHeaders->removeRow(items.constFirst()->row());
+ }
+}
+
void ProfileView::cookieAdded(const QNetworkCookie &cookie)
{
auto index = ui->cookies->rowCount();
diff --git a/plugins/ProfileEditor/forms/profileview.h b/plugins/ProfileEditor/forms/profileview.h
index 5ff472b..d05b0f1 100644
--- a/plugins/ProfileEditor/forms/profileview.h
+++ b/plugins/ProfileEditor/forms/profileview.h
@@ -33,6 +33,9 @@ public:
private slots:
void loadCookies(QWebEngineCookieStore *store);
+ void headerChanged(const QString &name, const QString &value);
+ void headerRemoved(const QString &name);
+
void cookieAdded(const QNetworkCookie &cookie);
void cookieRemoved(const QNetworkCookie &cookie);
diff --git a/plugins/ProfileEditor/forms/profileview.ui b/plugins/ProfileEditor/forms/profileview.ui
index 74b6d5d..630e53d 100644
--- a/plugins/ProfileEditor/forms/profileview.ui
+++ b/plugins/ProfileEditor/forms/profileview.ui
@@ -234,6 +234,70 @@
</item>
</layout>
</widget>
+ <widget class="QWidget" name="httpHeadersTab">
+ <attribute name="title">
+ <string>Headers</string>
+ </attribute>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QTableWidget" name="httpHeaders">
+ <attribute name="horizontalHeaderDefaultSectionSize">
+ <number>200</number>
+ </attribute>
+ <attribute name="horizontalHeaderMinimumSectionSize">
+ <number>100</number>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <column>
+ <property name="text">
+ <string>Name</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Value</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QPushButton" name="headers_insert">
+ <property name="text">
+ <string>Insert</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="headers_delete">
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
<widget class="QWidget" name="settingsTab">
<attribute name="title">
<string>Settings</string>
@@ -249,8 +313,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>276</width>
- <height>855</height>
+ <width>584</width>
+ <height>797</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
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