Falkon Develop
Cross-platform Qt-based web browser
adblocksubscription.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com>
4*
5* This program is free software: you can redistribute it and/or modify
6* it under the terms of the GNU General Public License as published by
7* the Free Software Foundation, either version 3 of the License, or
8* (at your option) any later version.
9*
10* This program is distributed in the hope that it will be useful,
11* but WITHOUT ANY WARRANTY; without even the implied warranty of
12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13* GNU General Public License for more details.
14*
15* You should have received a copy of the GNU General Public License
16* along with this program. If not, see <http://www.gnu.org/licenses/>.
17* ============================================================ */
45#include "adblocksubscription.h"
46#include "adblockmanager.h"
47#include "mainapplication.h"
48#include "networkmanager.h"
49#include "datapaths.h"
50#include "qztools.h"
51
52#include <QFile>
53#include <QTimer>
54#include <QNetworkReply>
55#include <QSaveFile>
56
57AdBlockSubscription::AdBlockSubscription(const QString &title, QObject* parent)
58 : QObject(parent)
59 , m_reply(nullptr)
60 , m_title(title)
61 , m_updated(false)
62{
63}
64
66{
67 return m_title;
68}
69
71{
72 return m_filePath;
73}
74
75void AdBlockSubscription::setFilePath(const QString &path)
76{
77 m_filePath = path;
78}
79
81{
82 return m_url;
83}
84
85void AdBlockSubscription::setUrl(const QUrl &url)
86{
87 m_url = url;
88}
89
90void AdBlockSubscription::loadSubscription(const QStringList &disabledRules)
91{
92 QFile file(m_filePath);
93
94 if (!file.exists()) {
95 QTimer::singleShot(0, this, &AdBlockSubscription::updateSubscription);
96 return;
97 }
98
99 if (!file.open(QFile::ReadOnly)) {
100 qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "Unable to open adblock file for reading" << m_filePath;
101 QTimer::singleShot(0, this, &AdBlockSubscription::updateSubscription);
102 return;
103 }
104
105 QTextStream textStream(&file);
106 textStream.setEncoding(QStringConverter::Utf8);
107 // Header is on 3rd line
108 textStream.readLine(1024);
109 textStream.readLine(1024);
110 QString header = textStream.readLine(1024);
111
112 if (!header.startsWith(QLatin1String("[Adblock")) || m_title.isEmpty()) {
113 qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "invalid format of adblock file" << m_filePath;
114 QTimer::singleShot(0, this, &AdBlockSubscription::updateSubscription);
115 return;
116 }
117
118 qDeleteAll(m_rules);
119 m_rules.clear();
120
121 while (!textStream.atEnd()) {
122 const QString line = textStream.readLine().trimmed();
123 if (line.isEmpty()) {
124 continue;
125 }
126 auto *rule = new AdBlockRule(line, this);
127 if (disabledRules.contains(rule->filter())) {
128 rule->setEnabled(false);
129 }
130 m_rules.append(rule);
131 }
132
133 // Initial update
134 if (m_rules.isEmpty() && !m_updated) {
135 QTimer::singleShot(0, this, &AdBlockSubscription::updateSubscription);
136 }
137}
138
140{
141}
142
144{
145 if (m_reply || !m_url.isValid()) {
146 return;
147 }
148
149 m_reply = mApp->networkManager()->get(QNetworkRequest(m_url));
150 connect(m_reply, &QNetworkReply::finished, this, &AdBlockSubscription::subscriptionDownloaded);
151}
152
154{
155 if (m_reply != qobject_cast<QNetworkReply*>(sender())) {
156 return;
157 }
158
159 bool error = false;
160 const QByteArray response = QString::fromUtf8(m_reply->readAll()).toUtf8();
161
162 if (m_reply->error() != QNetworkReply::NoError ||
163 !response.startsWith(QByteArray("[Adblock")) ||
164 !saveDownloadedData(response)
165 ) {
166 error = true;
167 }
168
169 m_reply->deleteLater();
170 m_reply = nullptr;
171
172 if (error) {
173 Q_EMIT subscriptionError(tr("Cannot load subscription!"));
174 return;
175 }
176
177 loadSubscription(AdBlockManager::instance()->disabledRules());
178
179 Q_EMIT subscriptionUpdated();
180 Q_EMIT subscriptionChanged();
181}
182
183bool AdBlockSubscription::saveDownloadedData(const QByteArray &data)
184{
185 QSaveFile file(m_filePath);
186
187 if (!file.open(QFile::WriteOnly)) {
188 qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "Unable to open adblock file for writing:" << m_filePath;
189 return false;
190 }
191
192 // Write subscription header
193 file.write(QSL("Title: %1\nUrl: %2\n").arg(title(), url().toString()).toUtf8());
194 file.write(data);
195 file.commit();
196 return true;
197}
198
200{
201 if (!QzTools::containsIndex(m_rules, offset)) {
202 return nullptr;
203 }
204
205 return m_rules[offset];
206}
207
208QVector<AdBlockRule*> AdBlockSubscription::allRules() const
209{
210 return m_rules;
211}
212
214{
215 if (!QzTools::containsIndex(m_rules, offset)) {
216 return nullptr;
217 }
218
219 AdBlockRule* rule = m_rules[offset];
220 rule->setEnabled(true);
222
223 Q_EMIT subscriptionChanged();
224
225 if (rule->isCssRule())
226 mApp->reloadUserStyleSheet();
227
228 return rule;
229}
230
232{
233 if (!QzTools::containsIndex(m_rules, offset)) {
234 return nullptr;
235 }
236
237 AdBlockRule* rule = m_rules[offset];
238 rule->setEnabled(false);
240
241 Q_EMIT subscriptionChanged();
242
243 if (rule->isCssRule())
244 mApp->reloadUserStyleSheet();
245
246 return rule;
247}
248
250{
251 return false;
252}
253
255{
256 return true;
257}
258
260{
261 Q_UNUSED(rule)
262 return -1;
263}
264
266{
267 Q_UNUSED(offset)
268 return false;
269}
270
272{
273 Q_UNUSED(rule)
274 Q_UNUSED(offset)
275 return nullptr;
276}
277
279{
280 qDeleteAll(m_rules);
281}
282
283// AdBlockCustomList
284
286 : AdBlockSubscription(tr("Custom Rules"), parent)
287{
288 setFilePath(DataPaths::currentProfilePath() + QLatin1String("/adblock/customlist.txt"));
289}
290
291void AdBlockCustomList::loadSubscription(const QStringList &disabledRules)
292{
293 // DuckDuckGo ad whitelist rules
294 // They cannot be removed, but can be disabled.
295 // Please consider not disabling them. Thanks!
296
297 const QString ddg1 = QSL("@@||duckduckgo.com^$document");
298 const QString ddg2 = QSL("duckduckgo.com#@#.has-ad");
299
300 const QString rules = QzTools::readAllFileContents(filePath());
301
302 QFile file(filePath());
303 if (!file.exists()) {
305 }
306
307 if (file.open(QFile::WriteOnly | QFile::Append)) {
308 QTextStream stream(&file);
309 stream.setEncoding(QStringConverter::Utf8);
310
311 if (!rules.contains(ddg1 + QL1S("\n")))
312 stream << ddg1 << Qt::endl;
313
314 if (!rules.contains(QL1S("\n") + ddg2))
315 stream << ddg2 << Qt::endl;
316 }
317 file.close();
318
320}
321
323{
324 QFile file(filePath());
325
326 if (!file.open(QFile::ReadWrite | QFile::Truncate)) {
327 qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "Unable to open adblock file for writing:" << filePath();
328 return;
329 }
330
331 QTextStream textStream(&file);
332 textStream.setEncoding(QStringConverter::Utf8);
333 textStream << "Title: " << title() << Qt::endl;
334 textStream << "Url: " << url().toString() << Qt::endl;
335 textStream << "[Adblock Plus 1.1.1]" << Qt::endl;
336
337 for (const AdBlockRule* rule : std::as_const(m_rules)) {
338 textStream << rule->filter() << Qt::endl;
339 }
340
341 file.close();
342}
343
345{
346 return true;
347}
348
350{
351 return false;
352}
353
354bool AdBlockCustomList::containsFilter(const QString &filter) const
355{
356 for (const AdBlockRule* rule : std::as_const(m_rules)) {
357 if (rule->filter() == filter) {
358 return true;
359 }
360 }
361
362 return false;
363}
364
365bool AdBlockCustomList::removeFilter(const QString &filter)
366{
367 for (int i = 0; i < m_rules.count(); ++i) {
368 const AdBlockRule* rule = m_rules.at(i);
369
370 if (rule->filter() == filter) {
371 return removeRule(i);
372 }
373 }
374
375 return false;
376}
377
379{
380 m_rules.append(rule);
381
382 Q_EMIT subscriptionChanged();
383
384 if (rule->isCssRule())
385 mApp->reloadUserStyleSheet();
386
387 return m_rules.count() - 1;
388}
389
391{
392 if (!QzTools::containsIndex(m_rules, offset)) {
393 return false;
394 }
395
396 AdBlockRule* rule = m_rules.at(offset);
397 const QString filter = rule->filter();
398
399 m_rules.remove(offset);
400
401 Q_EMIT subscriptionChanged();
402
403 if (rule->isCssRule())
404 mApp->reloadUserStyleSheet();
405
407
408 delete rule;
409 return true;
410}
411
413{
414 if (!QzTools::containsIndex(m_rules, offset)) {
415 return nullptr;
416 }
417
418 AdBlockRule* oldRule = m_rules.at(offset);
419 m_rules[offset] = rule;
420
421 Q_EMIT subscriptionChanged();
422
423 if (rule->isCssRule() || oldRule->isCssRule())
424 mApp->reloadUserStyleSheet();
425
426 delete oldRule;
427 return m_rules[offset];
428}
int addRule(AdBlockRule *rule) override
void loadSubscription(const QStringList &disabledRules) override
bool removeRule(int offset) override
bool removeFilter(const QString &filter)
bool canBeRemoved() const override
void saveSubscription() override
AdBlockCustomList(QObject *parent=nullptr)
const AdBlockRule * replaceRule(AdBlockRule *rule, int offset) override
bool containsFilter(const QString &filter) const
bool canEditRules() const override
void removeDisabledRule(const QString &filter)
static AdBlockManager * instance()
void addDisabledRule(const QString &filter)
QString filter() const
void setEnabled(bool enabled)
bool isCssRule() const
virtual const AdBlockRule * replaceRule(AdBlockRule *rule, int offset)
virtual bool canBeRemoved() const
virtual bool canEditRules() const
virtual void saveSubscription()
AdBlockSubscription(const QString &title, QObject *parent=nullptr)
const AdBlockRule * rule(int offset) const
virtual int addRule(AdBlockRule *rule)
void subscriptionError(const QString &message)
void setFilePath(const QString &path)
virtual void loadSubscription(const QStringList &disabledRules)
QVector< AdBlockRule * > m_rules
const AdBlockRule * disableRule(int offset)
virtual bool removeRule(int offset)
virtual bool saveDownloadedData(const QByteArray &data)
const AdBlockRule * enableRule(int offset)
QVector< AdBlockRule * > allRules() const
void setUrl(const QUrl &url)
static QString currentProfilePath()
Definition: datapaths.cpp:95
static bool containsIndex(const T &container, int index)
Definition: qztools.h:95
static QString readAllFileContents(const QString &filename)
Definition: qztools.cpp:98
#define mApp
i
Definition: i18n.py:23
#define QL1S(x)
Definition: qzcommon.h:44
#define QSL(x)
Definition: qzcommon.h:40