diff options
author | Andrea Diamantini <adjam7@gmail.com> | 2012-07-30 19:02:15 +0200 |
---|---|---|
committer | Andrea Diamantini <adjam7@gmail.com> | 2012-12-10 02:48:04 +0100 |
commit | 1949d41ac6fbd92f248a995a25aa4c91aced2ae8 (patch) | |
tree | 41ac0217ac4d8d32b15bb30c18ab22ce6730ee89 /src/adblock | |
parent | Session Manager (diff) | |
download | rekonq-1949d41ac6fbd92f248a995a25aa4c91aced2ae8.tar.xz |
Restored Adblock moving hiding logic to the one used in kwebkitpart
This will let everyone to save time reinventing the wheel and let us
remove webpage from adblockmanager code, but just manage it via (Q)NAM.
Diffstat (limited to 'src/adblock')
-rw-r--r-- | src/adblock/adblockhostmatcher.cpp | 56 | ||||
-rw-r--r-- | src/adblock/adblockhostmatcher.h | 54 | ||||
-rw-r--r-- | src/adblock/adblockmanager.cpp | 403 | ||||
-rw-r--r-- | src/adblock/adblockmanager.h | 210 | ||||
-rw-r--r-- | src/adblock/adblockrule.cpp | 67 | ||||
-rw-r--r-- | src/adblock/adblockrule.h | 74 | ||||
-rw-r--r-- | src/adblock/adblockrulefallbackimpl.cpp | 180 | ||||
-rw-r--r-- | src/adblock/adblockrulefallbackimpl.h | 56 | ||||
-rw-r--r-- | src/adblock/adblockruleimpl.h | 44 | ||||
-rw-r--r-- | src/adblock/adblockrulenullimpl.cpp | 139 | ||||
-rw-r--r-- | src/adblock/adblockrulenullimpl.h | 51 | ||||
-rw-r--r-- | src/adblock/adblockruletextmatchimpl.cpp | 94 | ||||
-rw-r--r-- | src/adblock/adblockruletextmatchimpl.h | 52 | ||||
-rw-r--r-- | src/adblock/adblockwidget.cpp | 242 | ||||
-rw-r--r-- | src/adblock/adblockwidget.h | 73 | ||||
-rw-r--r-- | src/adblock/blocked_elements.ui | 116 | ||||
-rw-r--r-- | src/adblock/blockedelementswidget.cpp | 116 | ||||
-rw-r--r-- | src/adblock/blockedelementswidget.h | 71 | ||||
-rw-r--r-- | src/adblock/settings_adblock.ui | 178 | ||||
-rw-r--r-- | src/adblock/tests/RULES | 19 | ||||
-rw-r--r-- | src/adblock/tests/divhidingtest.html | 15 |
21 files changed, 2310 insertions, 0 deletions
diff --git a/src/adblock/adblockhostmatcher.cpp b/src/adblock/adblockhostmatcher.cpp new file mode 100644 index 00000000..021fe12d --- /dev/null +++ b/src/adblock/adblockhostmatcher.cpp @@ -0,0 +1,56 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2011 by Benjamin Poulain <ikipou at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + +// Self Includes +#include "adblockhostmatcher.h" + +// Rekonq Includes +#include "rekonq_defines.h" + +bool AdBlockHostMatcher::tryAddFilter(const QString &filter) +{ + if (filter.startsWith(QL1S("||"))) + { + + QString domain = filter.mid(2); + + if (!domain.endsWith(QL1C('^'))) + return false; + + if (domain.contains(QL1C('$'))) + return false; + + domain = domain.left(domain.size() - 1); + + if (domain.contains(QL1C('/')) || domain.contains(QL1C('*')) || domain.contains(QL1C('^'))) + return false; + + domain = domain.toLower(); + m_hostList.insert(domain); + m_hostList.insert(QL1S("www.") + domain); + return true; + } + return false; +} diff --git a/src/adblock/adblockhostmatcher.h b/src/adblock/adblockhostmatcher.h new file mode 100644 index 00000000..bdad883c --- /dev/null +++ b/src/adblock/adblockhostmatcher.h @@ -0,0 +1,54 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2011 by Benjamin Poulain <ikipou at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + +#ifndef ADBLOCKHOSTMATCHER_H +#define ADBLOCKHOSTMATCHER_H + +#include <QSet> +#include <QString> + +class AdBlockHostMatcher +{ +public: + // Try to add an adblock filter to this host matcher. + // If the filter is not an hostname, the filter is not added + // and the method return false; + bool tryAddFilter(const QString &filter); + + bool match(const QString &host) const + { + return m_hostList.contains(host.toLower()); + } + + void clear() + { + m_hostList.clear(); + } + +private: + QSet<QString> m_hostList; +}; + +#endif // ADBLOCKHOSTMATCHER_H diff --git a/src/adblock/adblockmanager.cpp b/src/adblock/adblockmanager.cpp new file mode 100644 index 00000000..feee243e --- /dev/null +++ b/src/adblock/adblockmanager.cpp @@ -0,0 +1,403 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +// Self Includes +#include "adblockmanager.h" +#include "adblockmanager.moc" + +// Auto Includes +#include "rekonq.h" + +// Local Includes +#include "adblockwidget.h" +#include "blockedelementswidget.h" + +#include "webpage.h" + +// KDE Includes +#include <KIO/FileCopyJob> +#include <KStandardDirs> + +// Qt Includes +#include <QUrl> +#include <QWebElement> +#include <QNetworkReply> +#include <QNetworkRequest> + + +QWeakPointer<AdBlockManager> AdBlockManager::s_adBlockManager; + + +AdBlockManager *AdBlockManager::self() +{ + if (s_adBlockManager.isNull()) + { + s_adBlockManager = new AdBlockManager(qApp); + } + return s_adBlockManager.data(); +} + + +// ---------------------------------------------------------------------------------------------- + + +AdBlockManager::AdBlockManager(QObject *parent) + : QObject(parent) + , _isAdblockEnabled(false) + , _isHideAdsEnabled(false) +{ + loadSettings(); +} + + +AdBlockManager::~AdBlockManager() +{ + _whiteList.clear(); + _blackList.clear(); + _hideList.clear(); +} + + +bool AdBlockManager::isEnabled() +{ + return _isAdblockEnabled; +} + + +bool AdBlockManager::isHidingElements() +{ + return _isHideAdsEnabled; +} + + +void AdBlockManager::loadSettings() +{ + // first, check this... + QString adblockFilePath = KStandardDirs::locateLocal("appdata" , QL1S("adblockrc")); + if (!QFile::exists(adblockFilePath)) + { + QString generalAdblockFilePath = KStandardDirs::locate("appdata" , QL1S("adblockrc")); + QFile adblockFile(generalAdblockFilePath); + bool copied = adblockFile.copy(adblockFilePath); + if (!copied) + { + kDebug() << "oh oh... Problems copying default adblock file"; + return; + } + } + _adblockConfig = KSharedConfig::openConfig("adblockrc", KConfig::SimpleConfig, "appdata"); + // ---------------- + + _hostWhiteList.clear(); + _hostBlackList.clear(); + + _whiteList.clear(); + _blackList.clear(); + _hideList.clear(); + + KConfigGroup settingsGroup(_adblockConfig, "Settings"); + _isAdblockEnabled = settingsGroup.readEntry("adBlockEnabled", false); + + // no need to load filters if adblock is not enabled :) + if (!_isAdblockEnabled) + return; + + // just to be sure.. + _isHideAdsEnabled = settingsGroup.readEntry("hideAdsEnabled", false); + + // ---------------------------------------------------------- + + QDateTime today = QDateTime::currentDateTime(); + QDateTime lastUpdate = QDateTime::fromString(settingsGroup.readEntry("lastUpdate", QString())); + int days = settingsGroup.readEntry("updateInterval", 7); + + bool allSubscriptionsNeedUpdate = (today > lastUpdate.addDays(days)); + if (allSubscriptionsNeedUpdate) + { + settingsGroup.writeEntry("lastUpdate", today.toString()); + } + + // (Eventually) update and load automatic rules + KConfigGroup filtersGroup(_adblockConfig, "FiltersList"); + for (int i = 0; i < 60; i++) + { + QString n = QString::number(i + 1); + if (!filtersGroup.hasKey("FilterEnabled-" + n)) + continue; + + bool isFilterEnabled = filtersGroup.readEntry("FilterEnabled-" + n, false); + if (!isFilterEnabled) + continue; + + bool fileExists = subscriptionFileExists(i); + if (allSubscriptionsNeedUpdate || !fileExists) + { + kDebug() << "FILE SHOULDN'T EXIST. updating subscription"; + updateSubscription(i); + } + else + { + QString rulesFilePath = KStandardDirs::locateLocal("appdata" , QL1S("adblockrules_") + n); + loadRules(rulesFilePath); + } + } + + // load local rules + QString localRulesFilePath = KStandardDirs::locateLocal("appdata" , QL1S("adblockrules_local")); + loadRules(localRulesFilePath); +} + + +void AdBlockManager::loadRules(const QString &rulesFilePath) +{ + QFile ruleFile(rulesFilePath); + if (!ruleFile.open(QFile::ReadOnly | QFile::Text)) + { + kDebug() << "Unable to open rule file" << rulesFilePath; + return; + } + + QTextStream in(&ruleFile); + while (!in.atEnd()) + { + QString stringRule = in.readLine(); + loadRuleString(stringRule); + } +} + + +void AdBlockManager::loadRuleString(const QString &stringRule) +{ + // ! rules are comments + if (stringRule.startsWith('!')) + return; + + // [ rules are ABP info + if (stringRule.startsWith('[')) + return; + + // empty rules are just dangerous.. + // (an empty rule in whitelist allows all, in blacklist blocks all..) + if (stringRule.isEmpty()) + return; + + // white rules + if (stringRule.startsWith(QL1S("@@"))) + { + const QString filter = stringRule.mid(2); + if (_hostWhiteList.tryAddFilter(filter)) + return; + + AdBlockRule rule(filter); + _whiteList << rule; + return; + } + + // hide (CSS) rules + if (stringRule.startsWith(QL1S("##"))) + { + _hideList << stringRule.mid(2); + return; + } + + // TODO implement domain-specific hiding + if (stringRule.contains(QL1S("##"))) + return; + + if (_hostBlackList.tryAddFilter(stringRule)) + return; + + AdBlockRule rule(stringRule); + _blackList << rule; +} + + +bool AdBlockManager::blockRequest(const QNetworkRequest &request) +{ + if (!_isAdblockEnabled) + return false; + + // we (ad)block just http traffic + if (request.url().scheme() != QL1S("http")) + return false; + + QString urlString = request.url().toString(); + // We compute a lowercase version of the URL so each rule does not + // have to do it. + const QString urlStringLowerCase = urlString.toLower(); + const QString host = request.url().host(); + + // check white rules before :) + if (_hostWhiteList.match(host)) + { + kDebug() << "ADBLOCK: WHITE RULE (@@) Matched by string: " << urlString; + return false; + } + + Q_FOREACH(const AdBlockRule & filter, _whiteList) + { + if (filter.match(request, urlString, urlStringLowerCase)) + { + kDebug() << "ADBLOCK: WHITE RULE (@@) Matched by string: " << urlString; + return false; + } + } + + // then check the black ones :( + if (_hostBlackList.match(host)) + { + kDebug() << "ADBLOCK: BLACK RULE Matched by string: " << urlString; + return true; + } + + Q_FOREACH(const AdBlockRule & filter, _blackList) + { + if (filter.match(request, urlString, urlStringLowerCase)) + { + kDebug() << "ADBLOCK: BLACK RULE Matched by string: " << urlString; + return true; + } + } + // no match + return false; +} + + +void AdBlockManager::updateSubscription(int i) +{ + KConfigGroup filtersGroup(_adblockConfig, "FiltersList"); + QString n = QString::number(i + 1); + + QString fUrl = filtersGroup.readEntry("FilterURL-" + n, QString()); + KUrl subUrl = KUrl(fUrl); + + QString rulesFilePath = KStandardDirs::locateLocal("appdata" , QL1S("adblockrules_") + n); + KUrl destUrl = KUrl(rulesFilePath); + + KIO::FileCopyJob* job = KIO::file_copy(subUrl , destUrl, -1, KIO::HideProgressInfo); + job->metaData().insert("ssl_no_client_cert", "TRUE"); + job->metaData().insert("ssl_no_ui", "TRUE"); + job->metaData().insert("UseCache", "false"); + job->metaData().insert("cookies", "none"); + job->metaData().insert("no-auth", "true"); + + connect(job, SIGNAL(finished(KJob *)), this, SLOT(slotFinished(KJob *))); +} + + +void AdBlockManager::slotFinished(KJob *job) +{ + if (job->error()) + return; + + KIO::FileCopyJob *fJob = qobject_cast<KIO::FileCopyJob *>(job); + KUrl url = fJob->destUrl(); + url.setProtocol(QString()); // this is needed to load local url well :( + loadRules(url.url()); +} + + +bool AdBlockManager::subscriptionFileExists(int i) +{ + QString n = QString::number(i + 1); + + QString rulesFilePath = KStandardDirs::locateLocal("appdata" , QL1S("adblockrules_") + n); + return QFile::exists(rulesFilePath); +} + + +void AdBlockManager::showSettings() +{ + QPointer<KDialog> dialog = new KDialog(); + dialog->setCaption(i18nc("@title:window", "Ad Block Settings")); + dialog->setButtons(KDialog::Ok | KDialog::Cancel); + + AdBlockWidget widget(_adblockConfig); + dialog->setMainWidget(&widget); + connect(dialog, SIGNAL(okClicked()), &widget, SLOT(save())); + connect(dialog, SIGNAL(okClicked()), this, SLOT(loadSettings())); + dialog->exec(); + + dialog->deleteLater(); +} + + +void AdBlockManager::addCustomRule(const QString &stringRule, bool reloadPage) +{ + // save rule in local filters + QString localRulesFilePath = KStandardDirs::locateLocal("appdata" , QL1S("adblockrules_local")); + + QFile ruleFile(localRulesFilePath); + if (!ruleFile.open(QFile::WriteOnly | QFile::Append)) + { + kDebug() << "Unable to open rule file" << localRulesFilePath; + return; + } + + QTextStream out(&ruleFile); + out << stringRule << '\n'; + + ruleFile.close(); + + // load it + loadRuleString(stringRule); + + // eventually reload page + if (reloadPage) + emit reloadCurrentPage(); +} + + +void AdBlockManager::showBlockedItemDialog() +{ + QPointer<KDialog> dialog = new KDialog(); + dialog->setCaption(i18nc("@title:window", "Blocked elements")); + dialog->setButtons(KDialog::Ok); + + BlockedElementsWidget widget(this); + widget.setBlockedElements(_blockedElements); + widget.setHidedElements(_hidedElements); + + dialog->setMainWidget(&widget); + dialog->exec(); + + Q_FOREACH(const QString & r, widget.rulesToAdd()) + { + addCustomRule(r); + } + + if (widget.pageNeedsReload()) + emit reloadCurrentPage(); + + dialog->deleteLater(); +} + + +void AdBlockManager::clearElementsLists() +{ + _blockedElements.clear(); + _hidedElements = 0; +} diff --git a/src/adblock/adblockmanager.h b/src/adblock/adblockmanager.h new file mode 100644 index 00000000..1946d4ad --- /dev/null +++ b/src/adblock/adblockmanager.h @@ -0,0 +1,210 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + + +#ifndef ADBLOCK_MANAGER_H +#define ADBLOCK_MANAGER_H + + +// NOTE: AdBlockPlus Filters (fast) summary +// +// ### Basic Filter rules +// +// RULE = http://example.com/ads/* +// this should block every link containing all things from that link +// +// ### Exception rules (@@) +// +// RULE = @@advice* +// +// this will save every site, also that matched by other rules, cointaining words +// that starts with "advice". Wildcards && regular expression allowed here. +// +// ### Beginning/end matching rules (||) +// +// RULE=||http://badsite.com +// +// will stop all links starting with http://badsite.com +// +// RULE=*swf|| +// +// will stop all links to direct flash contents +// +// ### Comments (!) +// +// RULE=!azz.. +// +// Every rule starting with a ! is commented out and should not be checked +// +// ### Filter Options +// +// There are 3 kind of filter options: +// +// --- ### TYPE OPTIONS +// +// You can also specify a number of options to modify the behavior of a filter. +// You list these options separated with commas after a dollar sign ($) at the end of the filter +// +// RULE=*/ads/*$element,match-case +// +// where $element can be one of the following: +// $script external scripts loaded via HTML script tag +// $image regular images, typically loaded via HTML img tag +// $background background images, often specified via CSS +// $stylesheet external CSS stylesheet files +// $object content handled by browser plugins, e.g. Flash or Java +// $xbl XBL bindings (typically loaded by -moz-binding CSS property) Firefox 3 or higher required +// $ping link pings Firefox 3 or higher required +// $xmlhttprequest requests started by the XMLHttpRequest object Firefox 3 or higher required +// $object-subrequest requests started plugins like Flash Firefox 3 or higher required +// $dtd DTD files loaded by XML documents Firefox 3 or higher required +// $subdocument embedded pages, usually included via HTML frames +// $document the page itself (only exception rules can be applied to the page) +// $other types of requests not covered in the list above +// +// +// --- ### INVERSE TYPE OPTIONS +// +// Inverse type options are allowed through the ~ sign, for example: +// +// RULE=*/ads/*~$script,match-case +// +// +// --- ### THIRD-PARTY OPTIONS +// +// If "third-party" option is specified, filter is applied just to requests coming from a different +// origin than the currently viewed page. +// In the same way, the "~third-party" option restricts the filter to the requests coming from the +// same origin as the currently viewed page. +// +// +// ### Regular expressions +// +// They usually allow to check for (a lot of) sites, using just one rule, but be careful: +// BASIC FILTERS ARE PROCESSED FASTER THAN REGULAR EXPRESSIONS +// (That's true at least in ABP! In rekonq, I don't know...) +// +// +// ### ELEMENT HIDING (##) +// +// This is quite different from usual adblock (but, for me, more powerful!). Sometimes you will find advertisements +// that can’t be blocked because they are embedded as text in the web page itself. +// All you can do there is HIDE the element :) +// +// RULE=##div.advise +// +// The previous rule will hide every div whose class is named "advise". Usual CSS selectors apply here :) +// +// END NOTE ---------------------------------------------------------------------------------------------------------- + + +// Rekonq Includes +#include "rekonq_defines.h" + +// Local Includes +#include "adblockhostmatcher.h" +#include "adblockrule.h" + +// KDE Includes +#include <KIO/Job> +#include <KSharedConfig> + +// Qt Includes +#include <QObject> +#include <QStringList> +#include <QByteArray> + +// Forward Includes +class QNetworkRequest; +class WebPage; + +// Definitions +typedef QList<AdBlockRule> AdBlockRuleList; + + +class REKONQ_TESTS_EXPORT AdBlockManager : public QObject +{ + Q_OBJECT + +public: + /** + * Entry point. + * Access to AdBlockManager class by using + * AdBlockManager::self()->thePublicMethodYouNeed() + */ + static AdBlockManager *self(); + + ~AdBlockManager(); + + bool isEnabled(); + bool isHidingElements(); + + bool blockRequest(const QNetworkRequest &request); + + void addCustomRule(const QString &, bool reloadPage = true); + void clearElementsLists(); + +private: + AdBlockManager(QObject *parent = 0); + + void updateSubscription(int); + bool subscriptionFileExists(int); + + // load a file rule, given a path + void loadRules(const QString &rulesFilePath); + + // load a single rule + void loadRuleString(const QString &stringRule); + +private Q_SLOTS: + void loadSettings(); + void showSettings(); + void showBlockedItemDialog(); + + void slotFinished(KJob *); + +Q_SIGNALS: + void reloadCurrentPage(); + +private: + bool _isAdblockEnabled; + bool _isHideAdsEnabled; + + AdBlockHostMatcher _hostBlackList; + AdBlockHostMatcher _hostWhiteList; + AdBlockRuleList _blackList; + AdBlockRuleList _whiteList; + QStringList _hideList; + + QStringList _blockedElements; + int _hidedElements; + + KSharedConfig::Ptr _adblockConfig; + + static QWeakPointer<AdBlockManager> s_adBlockManager; +}; + +#endif diff --git a/src/adblock/adblockrule.cpp b/src/adblock/adblockrule.cpp new file mode 100644 index 00000000..d65c340d --- /dev/null +++ b/src/adblock/adblockrule.cpp @@ -0,0 +1,67 @@ +/* ============================================================ + * + * This file is a part of the rekonq project + * + * Copyright (c) 2009 by Zsombor Gegesy <gzsombor@gmail.com> + * Copyright (c) 2009 by Benjamin C. Meyer <ben@meyerhome.net> + * Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * ============================================================ */ + + +// Self Includes +#include "adblockrule.h" + +// Local Includes +#include "adblockrulefallbackimpl.h" +#include "adblockrulenullimpl.h" +#include "adblockruletextmatchimpl.h" + + +AdBlockRule::AdBlockRule(const QString &filter) +{ + switch (AdBlockRule::ruleType(filter)) + { + case TextRule: + m_implementation = QSharedPointer<AdBlockRuleImpl>(new AdBlockRuleTextMatchImpl(filter)); + break; + + case FallbackRule: + m_implementation = QSharedPointer<AdBlockRuleImpl>(new AdBlockRuleFallbackImpl(filter)); + break; + + case NullRule: + default: + m_implementation = QSharedPointer<AdBlockRuleImpl>(new AdBlockRuleNullImpl(filter)); + break; + } +} + + +RuleTypes AdBlockRule::ruleType(const QString &filter) +{ + if (AdBlockRuleTextMatchImpl::isTextMatchFilter(filter)) + return TextRule; + + if (AdBlockRuleNullImpl::isNullFilter(filter)) + return NullRule; + + return FallbackRule; +} diff --git a/src/adblock/adblockrule.h b/src/adblock/adblockrule.h new file mode 100644 index 00000000..15dea344 --- /dev/null +++ b/src/adblock/adblockrule.h @@ -0,0 +1,74 @@ +/* + * This file is a part of the rekonq project + * + * Copyright (c) 2009 by Benjamin C. Meyer <ben@meyerhome.net> + * Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * ============================================================ */ + + +#ifndef ADBLOCKRULE_H +#define ADBLOCKRULE_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +#include "adblockruleimpl.h" + +#include <QSharedPointer> + +// Forward Includes +class QNetworkRequest; +class QString; + + +enum RuleTypes +{ + TextRule, + FallbackRule, + NullRule +}; + + +class AdBlockRule +{ +public: + AdBlockRule(const QString &filter); + + bool match(const QNetworkRequest &request, const QString &encodedUrl, const QString &encodedUrlLowerCase) const + { + Q_ASSERT(encodedUrl.toLower() == encodedUrlLowerCase); + bool b = m_implementation->match(request, encodedUrl, encodedUrlLowerCase); + if (b) + { + kDebug() << m_implementation->ruleType() << ": rule string = " << m_implementation->ruleString(); + } + return b; + } + + static RuleTypes ruleType(const QString &filter); + +private: + QSharedPointer<AdBlockRuleImpl> m_implementation; +}; + + +#endif // ADBLOCKRULE_H diff --git a/src/adblock/adblockrulefallbackimpl.cpp b/src/adblock/adblockrulefallbackimpl.cpp new file mode 100644 index 00000000..b547beb0 --- /dev/null +++ b/src/adblock/adblockrulefallbackimpl.cpp @@ -0,0 +1,180 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2011 by Benjamin Poulain <ikipou at gmail dot com> +* Copyright (C) 2011-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +// Self Includes +#include "adblockrulefallbackimpl.h" + +// Rekonq Includes +#include "rekonq_defines.h" + +// Qt Includes +#include <QWebFrame> +#include <QNetworkReply> +#include <QStringList> + + + +static inline bool isRegExpFilter(const QString &filter) +{ + return filter.startsWith(QL1C('/')) && filter.endsWith(QL1C('/')); +} + + +AdBlockRuleFallbackImpl::AdBlockRuleFallbackImpl(const QString &filter) + : AdBlockRuleImpl(filter) + , m_thirdPartyOption(false) +{ + m_regExp.setCaseSensitivity(Qt::CaseInsensitive); + m_regExp.setPatternSyntax(QRegExp::RegExp2); + + QString parsedLine = filter; + + const int optionsNumber = parsedLine.lastIndexOf(QL1C('$')); + if (optionsNumber >= 0 && !isRegExpFilter(parsedLine)) + { + const QStringList options(parsedLine.mid(optionsNumber + 1).split(QL1C(','))); + parsedLine = parsedLine.left(optionsNumber); + + if (options.contains(QL1S("match-case"))) + m_regExp.setCaseSensitivity(Qt::CaseSensitive); + + if (options.contains(QL1S("third-party"))) + m_thirdPartyOption = true; + + Q_FOREACH(const QString & option, options) + { + // Domain restricted filter + const QString domainKeyword(QL1S("domain=")); + if (option.startsWith(domainKeyword)) + { + QStringList domainList = option.mid(domainKeyword.length()).split(QL1C('|')); + Q_FOREACH(const QString & domain, domainList) + { + if (domain.startsWith(QL1C('~'))) + m_whiteDomains.insert(domain.toLower()); + else + m_blackDomains.insert(domain.toLower()); + } + } + } + } + + if (isRegExpFilter(parsedLine)) + parsedLine = parsedLine.mid(1, parsedLine.length() - 2); + else + parsedLine = convertPatternToRegExp(parsedLine); + + m_regExp.setPattern(parsedLine); +} + + +bool AdBlockRuleFallbackImpl::match(const QNetworkRequest &request, const QString &encodedUrl, const QString &) const +{ + if (m_thirdPartyOption) + { + const QString referer = request.rawHeader("referer"); + const QString host = request.url().host(); + + if (referer.contains(host)) // is NOT third party + return false; + } + + const bool regexpMatch = m_regExp.indexIn(encodedUrl) != -1; + + if (regexpMatch && (!m_whiteDomains.isEmpty() || !m_blackDomains.isEmpty())) + { + Q_ASSERT(qobject_cast<QWebFrame*>(request.originatingObject())); + const QWebFrame *const origin = static_cast<QWebFrame * const>(request.originatingObject()); + + const QString originDomain = origin->url().host(); + + if (!m_whiteDomains.isEmpty()) + { + // In this context, white domains means we block anything but what is in the list. + if (m_whiteDomains.contains(originDomain)) + return false; + return true; + } + else if (m_blackDomains.contains(originDomain)) + { + return true; + } + return false; + } + return regexpMatch; +} + + +QString AdBlockRuleFallbackImpl::convertPatternToRegExp(const QString &wildcardPattern) +{ + QString pattern = wildcardPattern; + + // remove multiple wildcards + pattern.replace(QRegExp(QL1S("\\*+")), QL1S("*")); + + // remove anchors following separator placeholder + pattern.replace(QRegExp(QL1S("\\^\\|$")), QL1S("^")); + + // remove leading wildcards + pattern.replace(QRegExp(QL1S("^(\\*)")), QL1S("")); + + // remove trailing wildcards + pattern.replace(QRegExp(QL1S("(\\*)$")), QL1S("")); + + // escape special symbols + pattern.replace(QRegExp(QL1S("(\\W)")), QL1S("\\\\1")); + + // process extended anchor at expression start + pattern.replace(QRegExp(QL1S("^\\\\\\|\\\\\\|")), QL1S("^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?")); + + // process separator placeholders + pattern.replace(QRegExp(QL1S("\\\\\\^")), QL1S("(?:[^\\w\\d\\-.%]|$)")); + + // process anchor at expression start + pattern.replace(QRegExp(QL1S("^\\\\\\|")), QL1S("^")); + + // process anchor at expression end + pattern.replace(QRegExp(QL1S("\\\\\\|$")), QL1S("$")); + + // replace wildcards by .* + pattern.replace(QRegExp(QL1S("\\\\\\*")), QL1S(".*")); + + // Finally, return... + return pattern; +} + + +QString AdBlockRuleFallbackImpl::ruleString() const +{ + return m_regExp.pattern(); +} + + +QString AdBlockRuleFallbackImpl::ruleType() const +{ + return QL1S("AdBlockRuleFallbackImpl"); +} diff --git a/src/adblock/adblockrulefallbackimpl.h b/src/adblock/adblockrulefallbackimpl.h new file mode 100644 index 00000000..4b5716f8 --- /dev/null +++ b/src/adblock/adblockrulefallbackimpl.h @@ -0,0 +1,56 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2011 by Benjamin Poulain <ikipou at gmail dot com> +* Copyright (C) 2011-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + +#ifndef ADBLOCKRULEFALLBACKIMPL_H +#define ADBLOCKRULEFALLBACKIMPL_H + +#include "adblockruleimpl.h" + +// Qt Includes +#include <QRegExp> +#include <QString> +#include <QSet> + +class AdBlockRuleFallbackImpl : public AdBlockRuleImpl +{ +public: + AdBlockRuleFallbackImpl(const QString &filter); + bool match(const QNetworkRequest &request, const QString &encodedUrl, const QString &encodedUrlLowerCase) const; + + QString ruleString() const; + QString ruleType() const; + +private: + QString convertPatternToRegExp(const QString &wildcardPattern); + + QRegExp m_regExp; + QSet<QString> m_whiteDomains; + QSet<QString> m_blackDomains; + + bool m_thirdPartyOption; +}; + +#endif // ADBLOCKRULEFALLBACKIMPL_H diff --git a/src/adblock/adblockruleimpl.h b/src/adblock/adblockruleimpl.h new file mode 100644 index 00000000..f1d428d5 --- /dev/null +++ b/src/adblock/adblockruleimpl.h @@ -0,0 +1,44 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2011 by Benjamin Poulain <ikipou at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + +#ifndef ADBLOCKRULEIMPL_H +#define ADBLOCKRULEIMPL_H + +class QString; +class QNetworkRequest; + +class AdBlockRuleImpl +{ +public: + AdBlockRuleImpl(const QString &) {} + virtual ~AdBlockRuleImpl() {} + virtual bool match(const QNetworkRequest &request, const QString &encodedUrl, const QString &encodedUrlLowerCase) const = 0; + + // This are added just for debugging purposes + virtual QString ruleString() const = 0; + virtual QString ruleType() const = 0; +}; + +#endif // ADBLOCKRULEIMPL_H diff --git a/src/adblock/adblockrulenullimpl.cpp b/src/adblock/adblockrulenullimpl.cpp new file mode 100644 index 00000000..e16b56db --- /dev/null +++ b/src/adblock/adblockrulenullimpl.cpp @@ -0,0 +1,139 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2011-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +// Self Includes +#include "adblockrulenullimpl.h" + +// Rekonq Includes +#include "rekonq_defines.h" + +// Qt Includes +#include <QStringList> + + +AdBlockRuleNullImpl::AdBlockRuleNullImpl(const QString &filter) + : AdBlockRuleImpl(filter) +{ +} + + +bool AdBlockRuleNullImpl::match(const QNetworkRequest &, const QString &, const QString &) const +{ + return false; +} + + +bool AdBlockRuleNullImpl::isNullFilter(const QString &filter) +{ + QString parsedLine = filter; + + const int optionsNumber = parsedLine.lastIndexOf(QL1C('$')); + if (optionsNumber == 0) + return false; + + const QStringList options(parsedLine.mid(optionsNumber + 1).split(QL1C(','))); + + Q_FOREACH(const QString & option, options) + { + // NOTE: + // I moved the check from option == QL1S to option.endsWith() + // to check option && ~option. Hope it will NOT be a problem... + + // third_party: managed inside adblockrulefallbackimpl + if (option.endsWith(QL1S("third-party"))) + return false; + + // script + if (option.endsWith(QL1S("script"))) + return true; + + // image + if (option.endsWith(QL1S("image"))) + return true; + + // background + if (option.endsWith(QL1S("background"))) + return true; + + // stylesheet + if (option.endsWith(QL1S("stylesheet"))) + return true; + + // object + if (option.endsWith(QL1S("object"))) + return true; + + // xbl + if (option.endsWith(QL1S("xbl"))) + return true; + + // ping + if (option.endsWith(QL1S("ping"))) + return true; + + // xmlhttprequest + if (option.endsWith(QL1S("xmlhttprequest"))) + return true; + + // object_subrequest + if (option.endsWith(QL1S("object-subrequest"))) + return true; + + // dtd + if (option.endsWith(QL1S("dtd"))) + return true; + + // subdocument + if (option.endsWith(QL1S("subdocument"))) + return true; + + // document + if (option.endsWith(QL1S("document"))) + return true; + + // other + if (option.endsWith(QL1S("other"))) + return true; + + // collapse + if (option.endsWith(QL1S("collapse"))) + return true; + } + + return false; +} + + +QString AdBlockRuleNullImpl::ruleString() const +{ + return QString(); +} + + +QString AdBlockRuleNullImpl::ruleType() const +{ + return QL1S("AdBlockRuleNullImpl"); +} diff --git a/src/adblock/adblockrulenullimpl.h b/src/adblock/adblockrulenullimpl.h new file mode 100644 index 00000000..a8f69652 --- /dev/null +++ b/src/adblock/adblockrulenullimpl.h @@ -0,0 +1,51 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2011-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +#ifndef ADBLOCKRULE_NULL_IMPL_H +#define ADBLOCKRULE_NULL_IMPL_H + + +// Local Includes +#include "adblockruleimpl.h" + +// Qt Includes +#include <QString> + + +class AdBlockRuleNullImpl : public AdBlockRuleImpl +{ + +public: + AdBlockRuleNullImpl(const QString &filter); + bool match(const QNetworkRequest &, const QString &, const QString &) const; + + static bool isNullFilter(const QString &filter); + + QString ruleString() const; + QString ruleType() const; +}; + +#endif // ADBLOCKRULE_NULL_IMPL_H diff --git a/src/adblock/adblockruletextmatchimpl.cpp b/src/adblock/adblockruletextmatchimpl.cpp new file mode 100644 index 00000000..fd901e9c --- /dev/null +++ b/src/adblock/adblockruletextmatchimpl.cpp @@ -0,0 +1,94 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2011 by Benjamin Poulain <ikipou at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +// Self Includes +#include "adblockruletextmatchimpl.h" + +// Rekonq Includes +#include "rekonq_defines.h" + +// Qt Includes +#include <QNetworkRequest> + + +AdBlockRuleTextMatchImpl::AdBlockRuleTextMatchImpl(const QString &filter) + : AdBlockRuleImpl(filter) +{ + Q_ASSERT(AdBlockRuleTextMatchImpl::isTextMatchFilter(filter)); + + m_textToMatch = filter.toLower(); + m_textToMatch.remove(QL1C('*')); +} + + +bool AdBlockRuleTextMatchImpl::match(const QNetworkRequest &request, const QString &encodedUrl, const QString &encodedUrlLowerCase) const +{ + Q_UNUSED(request); + Q_UNUSED(encodedUrl); + // Case sensitive compare is faster, but would be incorrect with encodedUrl since + // we do want case insensitive. + // What we do is work on a lowercase version of m_textToMatch, and compare to the lowercase + // version of encodedUrl. + return encodedUrlLowerCase.contains(m_textToMatch, Qt::CaseSensitive); +} + + +bool AdBlockRuleTextMatchImpl::isTextMatchFilter(const QString &filter) +{ + // We don't deal with options just yet + if (filter.contains(QL1C('$'))) + return false; + + // We don't deal with element matching + if (filter.contains(QL1S("##"))) + return false; + + // We don't deal with the begin-end matching + if (filter.startsWith(QL1C('|')) || filter.endsWith(QL1C('|'))) + return false; + + // We only handle * at the beginning or the end + int starPosition = filter.indexOf(QL1C('*')); + while (starPosition >= 0) + { + if (starPosition != 0 && starPosition != (filter.length() - 1)) + return false; + starPosition = filter.indexOf(QL1C('*'), starPosition + 1); + } + return true; +} + + +QString AdBlockRuleTextMatchImpl::ruleString() const +{ + return m_textToMatch; +} + + +QString AdBlockRuleTextMatchImpl::ruleType() const +{ + return QL1S("AdBlockRuleTextMatchImpl"); +} diff --git a/src/adblock/adblockruletextmatchimpl.h b/src/adblock/adblockruletextmatchimpl.h new file mode 100644 index 00000000..8600543d --- /dev/null +++ b/src/adblock/adblockruletextmatchimpl.h @@ -0,0 +1,52 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2011 by Benjamin Poulain <ikipou at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +#ifndef ADBLOCKRULETEXTMATCHIMPL_H +#define ADBLOCKRULETEXTMATCHIMPL_H + +#include "adblockruleimpl.h" + +// Qt Includes +#include <QString> + + +// Simple rule to find a string in the URL +class AdBlockRuleTextMatchImpl : public AdBlockRuleImpl +{ +public: + AdBlockRuleTextMatchImpl(const QString &filter); + bool match(const QNetworkRequest &request, const QString &encodedUrl, const QString &encodedUrlLowerCase) const; + + static bool isTextMatchFilter(const QString &filter); + + QString ruleString() const; + QString ruleType() const; + +private: + QString m_textToMatch; +}; + +#endif // ADBLOCKRULETEXTMATCHIMPL_H diff --git a/src/adblock/adblockwidget.cpp b/src/adblock/adblockwidget.cpp new file mode 100644 index 00000000..5ada506e --- /dev/null +++ b/src/adblock/adblockwidget.cpp @@ -0,0 +1,242 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +// Self Includes +#include "adblockwidget.h" +#include "adblockwidget.moc" + +// Auto Includes +#include "rekonq.h" + +// KDE Includes +#include <KSharedConfig> +#include <KStandardDirs> +#include <KIcon> + +// Qt Includes +#include <QString> +#include <QWhatsThis> +#include <QListWidgetItem> + + +AdBlockWidget::AdBlockWidget(KSharedConfig::Ptr config, QWidget *parent) + : QWidget(parent) + , _changed(false) + , _adblockConfig(config) +{ + setupUi(this); + + hintLabel->setText(i18n("<qt>Filter expression (e.g. <tt>http://www.example.com/ad/*</tt>, <a href=\"filterhelp\">more information</a>):")); + connect(hintLabel, SIGNAL(linkActivated(QString)), this, SLOT(slotInfoLinkActivated(QString))); + + manualFiltersListWidget->setSortingEnabled(true); + manualFiltersListWidget->setSelectionMode(QAbstractItemView::SingleSelection); + + searchLine->setListWidget(manualFiltersListWidget); + + insertButton->setIcon(KIcon("list-add")); + connect(insertButton, SIGNAL(clicked()), this, SLOT(insertRule())); + + removeButton->setIcon(KIcon("list-remove")); + connect(removeButton, SIGNAL(clicked()), this, SLOT(removeRule())); + + load(); + + spinBox->setSuffix(ki18np(" day", " days")); + + // emit changed signal + connect(insertButton, SIGNAL(clicked()), this, SLOT(hasChanged())); + connect(removeButton, SIGNAL(clicked()), this, SLOT(hasChanged())); + connect(checkEnableAdblock, SIGNAL(stateChanged(int)), this, SLOT(hasChanged())); + connect(checkHideAds, SIGNAL(stateChanged(int)), this, SLOT(hasChanged())); + connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(hasChanged())); + + connect(automaticFiltersListWidget, SIGNAL(itemChanged(QListWidgetItem *)), this, SLOT(hasChanged())); +} + + +void AdBlockWidget::slotInfoLinkActivated(const QString &url) +{ + Q_UNUSED(url) + + QString hintHelpString = i18n("<qt><p>Enter an expression to filter. Filters can be defined as either:" + "<ul><li>a shell-style wildcard, e.g. <tt>http://www.example.com/ads*</tt>, " + "the wildcards <tt>*?[]</tt> may be used</li>" + "<li>a full regular expression by surrounding the string with '<tt>/</tt>', " + "e.g. <tt>/\\/(ad|banner)\\./</tt></li></ul>" + "<p>Any filter string can be preceded by '<tt>@@</tt>' to whitelist (allow) any matching URL, " + "which takes priority over any blacklist (blocking) filter."); + + QWhatsThis::showText(QCursor::pos(), hintHelpString); +} + + +void AdBlockWidget::insertRule() +{ + QString rule = addFilterLineEdit->text(); + if (rule.isEmpty()) + return; + + manualFiltersListWidget->addItem(rule); + addFilterLineEdit->clear(); +} + + +void AdBlockWidget::removeRule() +{ + manualFiltersListWidget->takeItem(manualFiltersListWidget->currentRow()); +} + + +void AdBlockWidget::load() +{ + // General settings + KConfigGroup settingsGroup(_adblockConfig, "Settings"); + + const bool isAdBlockEnabled = settingsGroup.readEntry("adBlockEnabled", false); + checkEnableAdblock->setChecked(isAdBlockEnabled); + + // update enabled status + checkHideAds->setEnabled(isAdBlockEnabled); + tabWidget->setEnabled(isAdBlockEnabled); + + const bool areImageFiltered = settingsGroup.readEntry("hideAdsEnabled", false); + checkHideAds->setChecked(areImageFiltered); + + const int days = settingsGroup.readEntry("updateInterval", 7); + spinBox->setValue(days); + + // ------------------------------------------------------------------------------ + + // automatic filters + KConfigGroup autoFiltersGroup(_adblockConfig, "FiltersList"); + + int i = 1; + QString n = QString::number(i); + + while (autoFiltersGroup.hasKey("FilterName-" + n)) + { + bool filterEnabled = autoFiltersGroup.readEntry("FilterEnabled-" + n, false); + QString filterName = autoFiltersGroup.readEntry("FilterName-" + n, QString()); + + QListWidgetItem *subItem = new QListWidgetItem(automaticFiltersListWidget); + subItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable); + if (filterEnabled) + subItem->setCheckState(Qt::Checked); + else + subItem->setCheckState(Qt::Unchecked); + + subItem->setText(filterName); + + i++; + n = QString::number(i); + } + + // ------------------------------------------------------------------------------ + + // local filters + QString localRulesFilePath = KStandardDirs::locateLocal("appdata" , QL1S("adblockrules_local")); + + QFile ruleFile(localRulesFilePath); + if (!ruleFile.open(QFile::ReadOnly | QFile::Text)) + { + kDebug() << "Unable to open rule file" << localRulesFilePath; + return; + } + + QTextStream in(&ruleFile); + while (!in.atEnd()) + { + QString stringRule = in.readLine(); + QListWidgetItem *subItem = new QListWidgetItem(manualFiltersListWidget); + subItem->setText(stringRule); + } +} + + +void AdBlockWidget::save() +{ + if (!_changed) + return; + + // General settings + KConfigGroup settingsGroup(_adblockConfig, "Settings"); + + settingsGroup.writeEntry("adBlockEnabled", checkEnableAdblock->isChecked()); + settingsGroup.writeEntry("hideAdsEnabled", checkHideAds->isChecked()); + settingsGroup.writeEntry("updateInterval", spinBox->value()); + + // automatic filters + KConfigGroup autoFiltersGroup(_adblockConfig, "FiltersList"); + for (int i = 0; i < automaticFiltersListWidget->count(); i++) + { + QListWidgetItem *subItem = automaticFiltersListWidget->item(i); + bool active = true; + if (subItem->checkState() == Qt::Unchecked) + active = false; + + QString n = QString::number(i + 1); + autoFiltersGroup.writeEntry("FilterEnabled-" + n, active); + } + + // local filters + QString localRulesFilePath = KStandardDirs::locateLocal("appdata" , QL1S("adblockrules_local")); + + QFile ruleFile(localRulesFilePath); + if (!ruleFile.open(QFile::WriteOnly | QFile::Text)) + { + kDebug() << "Unable to open rule file" << localRulesFilePath; + return; + } + + QTextStream out(&ruleFile); + for (int i = 0; i < manualFiltersListWidget->count(); i++) + { + QListWidgetItem *subItem = manualFiltersListWidget->item(i); + QString stringRule = subItem->text(); + out << stringRule << '\n'; + } + + // ------------------------------------------------------------------------------- + _changed = false; + emit changed(false); +} + + +void AdBlockWidget::hasChanged() +{ + // update enabled status + checkHideAds->setEnabled(checkEnableAdblock->isChecked()); + tabWidget->setEnabled(checkEnableAdblock->isChecked()); + _changed = true; + emit changed(true); +} + + +bool AdBlockWidget::changed() +{ + return _changed; +} diff --git a/src/adblock/adblockwidget.h b/src/adblock/adblockwidget.h new file mode 100644 index 00000000..37f29f93 --- /dev/null +++ b/src/adblock/adblockwidget.h @@ -0,0 +1,73 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2010-2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +#ifndef ADBLOCK_WIDGET_H +#define ADBLOCK_WIDGET_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// Ui Includes +#include "ui_settings_adblock.h" + +// KDE Includes +#include <KSharedConfig> + +// Qt Includes +#include <QWidget> + + +class AdBlockWidget : public QWidget, private Ui::adblock +{ + Q_OBJECT + +public: + explicit AdBlockWidget(KSharedConfig::Ptr config, QWidget *parent = 0); + + bool changed(); + +Q_SIGNALS: + void changed(bool); + +public Q_SLOTS: + void save(); + +private Q_SLOTS: + void hasChanged(); + + void slotInfoLinkActivated(const QString &); + void insertRule(); + void removeRule(); + +private: + void load(); + + bool _changed; + KSharedConfig::Ptr _adblockConfig; +}; + +#endif // ADBLOCK_WIDGET_H diff --git a/src/adblock/blocked_elements.ui b/src/adblock/blocked_elements.ui new file mode 100644 index 00000000..204bf81f --- /dev/null +++ b/src/adblock/blocked_elements.ui @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>BlockedElements</class> + <widget class="QWidget" name="BlockedElements"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>527</width> + <height>407</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="BlockedLabel"> + <property name="text"> + <string><html><head/><body><p><span style=" font-weight:600;">Blocked elements</span></p></body></html></string> + </property> + </widget> + </item> + <item> + <widget class="QFrame" name="frame"> + <property name="palette"> + <palette> + <active> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="autoFillBackground"> + <bool>true</bool> + </property> + <property name="frameShape"> + <enum>QFrame::StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="HidedLabel"> + <property name="text"> + <string><b>Hidden elements</b></string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>TextLabel</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/adblock/blockedelementswidget.cpp b/src/adblock/blockedelementswidget.cpp new file mode 100644 index 00000000..141dd203 --- /dev/null +++ b/src/adblock/blockedelementswidget.cpp @@ -0,0 +1,116 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +// Self Includes +#include "blockedelementswidget.h" +#include "blockedelementswidget.moc" + +// Local Includes +#include "adblockmanager.h" + +// Qt Includes +#include <QHBoxLayout> +#include <QVBoxLayout> +#include <QPushButton> + + +BlockedElementsWidget::BlockedElementsWidget(QObject *manager, QWidget *parent) + : QWidget(parent) + , _manager(manager) + , _reloadPage(false) +{ + setupUi(this); +} + + +void BlockedElementsWidget::setBlockedElements(const QStringList &list) +{ + QVBoxLayout *frameLayout = new QVBoxLayout(frame); + + Q_FOREACH(const QString & block, list) + { + QString truncatedUrl = block; + const int maxTextSize = 73; + if (truncatedUrl.length() > maxTextSize) + { + const int truncateSize = 70; + truncatedUrl.truncate(truncateSize); + truncatedUrl += QL1S("..."); + } + QWidget *w = new QWidget(this); + QHBoxLayout *l = new QHBoxLayout(w); + l->addWidget(new QLabel(truncatedUrl, this)); + + QPushButton *button = new QPushButton(KIcon("dialog-ok-apply"), i18n("Unblock"), this); + button->setProperty("URLTOUNBLOCK", block); + button->setFixedWidth(100); + connect(button, SIGNAL(clicked()), this, SLOT(unblockElement())); + l->addWidget(button); + + w->setMinimumWidth(500); + frameLayout->addWidget(w); + } +} + + +void BlockedElementsWidget::setHidedElements(int n) +{ + AdBlockManager *m = qobject_cast<AdBlockManager *>(_manager); + if (m->isHidingElements()) + label->setText(i18np("There is %1 hidden element in this page.", "There are %1 hidden elements in this page.", QString::number(n))); + else + label->setText(i18n("Hiding elements is disabled.")); +} + + +void BlockedElementsWidget::unblockElement() +{ + QPushButton *buttonClicked = qobject_cast<QPushButton *>(sender()); + if (!buttonClicked) + return; + + QString urlString = QL1S("@@") + buttonClicked->property("URLTOUNBLOCK").toString(); + kDebug() << "urlString: " << urlString; + + QString newText = i18n("Unblocked"); + QString buttonText = buttonClicked->text().remove('&'); + if (buttonText == newText) + { + buttonClicked->setText(i18n("Unblock")); + buttonClicked->setIcon(KIcon("dialog-ok-apply")); + + _rulesToAdd.removeOne(urlString); + } + else + { + buttonClicked->setText(newText); + buttonClicked->setIcon(KIcon("dialog-ok")); + + _rulesToAdd << urlString; + } + + _reloadPage = true; +} diff --git a/src/adblock/blockedelementswidget.h b/src/adblock/blockedelementswidget.h new file mode 100644 index 00000000..d26ee849 --- /dev/null +++ b/src/adblock/blockedelementswidget.h @@ -0,0 +1,71 @@ +/* ============================================================ +* +* This file is a part of the rekonq project +* +* Copyright (C) 2012 by Andrea Diamantini <adjam7 at gmail dot com> +* +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* ============================================================ */ + + +#ifndef BLOCKED_ELEMENTS_WIDGET_H +#define BLOCKED_ELEMENTS_WIDGET_H + + +// Rekonq Includes +#include "rekonq_defines.h" + +// Ui Includes +#include "ui_blocked_elements.h" + +// Qt Includes +#include <QWidget> + + +class BlockedElementsWidget : public QWidget, private Ui::BlockedElements +{ + Q_OBJECT + +public: + explicit BlockedElementsWidget(QObject *manager, QWidget *parent = 0); + + void setBlockedElements(const QStringList &); + void setHidedElements(int); + + bool pageNeedsReload() + { + return _reloadPage; + }; + + QStringList rulesToAdd() + { + return _rulesToAdd; + }; + +private Q_SLOTS: + void unblockElement(); + +private: + QObject *_manager; + + bool _reloadPage; + QStringList _rulesToAdd; +}; + +#endif // BLOCKED_ELEMENTS_WIDGET_H diff --git a/src/adblock/settings_adblock.ui b/src/adblock/settings_adblock.ui new file mode 100644 index 00000000..e4f06339 --- /dev/null +++ b/src/adblock/settings_adblock.ui @@ -0,0 +1,178 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>adblock</class> + <widget class="QWidget" name="adblock"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>601</width> + <height>507</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="checkEnableAdblock"> + <property name="text"> + <string>&Enable Ad Block</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="checkHideAds"> + <property name="text"> + <string>&Hide filtered elements</string> + </property> + </widget> + </item> + <item> + <widget class="KTabWidget" name="tabWidget"> + <property name="toolTip"> + <string/> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab_3"> + <attribute name="title"> + <string>Automatic Filters</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QListWidget" name="automaticFiltersListWidget"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Update enabled automatic filters every:</string> + </property> + </widget> + </item> + <item> + <widget class="KIntSpinBox" name="spinBox"> + <property name="minimumSize"> + <size> + <width>120</width> + <height>0</height> + </size> + </property> + <property name="suffix"> + <string/> + </property> + <property name="value"> + <number>7</number> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Manual Filters</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Search:</string> + </property> + </widget> + </item> + <item> + <widget class="KListWidgetSearchLine" name="searchLine"/> + </item> + </layout> + </item> + <item> + <widget class="KListWidget" name="manualFiltersListWidget"/> + </item> + <item> + <widget class="QLabel" name="hintLabel"> + <property name="text"> + <string comment="KDE::DoNotExtract">TextLabel</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="KLineEdit" name="addFilterLineEdit"/> + </item> + <item> + <widget class="QToolButton" name="insertButton"> + <property name="toolTip"> + <string>Add filter expression</string> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="removeButton"> + <property name="toolTip"> + <string>Remove filter expression</string> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>KListWidgetSearchLine</class> + <extends>KLineEdit</extends> + <header>klistwidgetsearchline.h</header> + </customwidget> + <customwidget> + <class>KLineEdit</class> + <extends>QLineEdit</extends> + <header>klineedit.h</header> + </customwidget> + <customwidget> + <class>KIntSpinBox</class> + <extends>QSpinBox</extends> + <header>knuminput.h</header> + </customwidget> + <customwidget> + <class>KListWidget</class> + <extends>QListWidget</extends> + <header>klistwidget.h</header> + </customwidget> + <customwidget> + <class>KTabWidget</class> + <extends>QTabWidget</extends> + <header>ktabwidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/src/adblock/tests/RULES b/src/adblock/tests/RULES new file mode 100644 index 00000000..8dfdf8fa --- /dev/null +++ b/src/adblock/tests/RULES @@ -0,0 +1,19 @@ +! cancel ALL your rules in the adblock, +! load these and test rekonq against +! the proposed sites +! Good Luck! +! +! This rule will block EVERY site containing adv +adv +! +! This one will wildcard EVERY site containing the word "advice" +@@advice +! +! Block every google site :) +||http://google* +! +! Block every pdf file! +*pdf|| +! +! Block every div of the class "advise" +##div.advise
\ No newline at end of file diff --git a/src/adblock/tests/divhidingtest.html b/src/adblock/tests/divhidingtest.html new file mode 100644 index 00000000..a38e9ea1 --- /dev/null +++ b/src/adblock/tests/divhidingtest.html @@ -0,0 +1,15 @@ +<html> + +<head> +</head> + +<body> + +<div class="advise"> +<h1>Oh oh.. an advise!!</h1> +</div> +<h2>no advises here :)</h2> +At least, I hope so... +</body> + +</html>
\ No newline at end of file |