File indexing completed on 2024-05-12 04:57:50
0001 /* ============================================================ 0002 * Falkon - Qt web browser 0003 * Copyright (C) 2010-2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 /** 0019 * Copyright (c) 2009, Benjamin C. Meyer <ben@meyerhome.net> 0020 * 0021 * Redistribution and use in source and binary forms, with or without 0022 * modification, are permitted provided that the following conditions 0023 * are met: 0024 * 1. Redistributions of source code must retain the above copyright 0025 * notice, this list of conditions and the following disclaimer. 0026 * 2. Redistributions in binary form must reproduce the above copyright 0027 * notice, this list of conditions and the following disclaimer in the 0028 * documentation and/or other materials provided with the distribution. 0029 * 3. Neither the name of the Benjamin Meyer nor the names of its contributors 0030 * may be used to endorse or promote products derived from this software 0031 * without specific prior written permission. 0032 * 0033 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 0034 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 0035 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 0036 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 0037 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 0038 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 0039 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 0040 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 0041 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 0042 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 0043 * SUCH DAMAGE. 0044 */ 0045 #include "adblocksubscription.h" 0046 #include "adblockmanager.h" 0047 #include "mainapplication.h" 0048 #include "networkmanager.h" 0049 #include "datapaths.h" 0050 #include "qztools.h" 0051 0052 #include <QFile> 0053 #include <QTimer> 0054 #include <QNetworkReply> 0055 #include <QSaveFile> 0056 0057 AdBlockSubscription::AdBlockSubscription(const QString &title, QObject* parent) 0058 : QObject(parent) 0059 , m_reply(nullptr) 0060 , m_title(title) 0061 , m_updated(false) 0062 { 0063 } 0064 0065 QString AdBlockSubscription::title() const 0066 { 0067 return m_title; 0068 } 0069 0070 QString AdBlockSubscription::filePath() const 0071 { 0072 return m_filePath; 0073 } 0074 0075 void AdBlockSubscription::setFilePath(const QString &path) 0076 { 0077 m_filePath = path; 0078 } 0079 0080 QUrl AdBlockSubscription::url() const 0081 { 0082 return m_url; 0083 } 0084 0085 void AdBlockSubscription::setUrl(const QUrl &url) 0086 { 0087 m_url = url; 0088 } 0089 0090 void AdBlockSubscription::loadSubscription(const QStringList &disabledRules) 0091 { 0092 QFile file(m_filePath); 0093 0094 if (!file.exists()) { 0095 QTimer::singleShot(0, this, &AdBlockSubscription::updateSubscription); 0096 return; 0097 } 0098 0099 if (!file.open(QFile::ReadOnly)) { 0100 qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "Unable to open adblock file for reading" << m_filePath; 0101 QTimer::singleShot(0, this, &AdBlockSubscription::updateSubscription); 0102 return; 0103 } 0104 0105 QTextStream textStream(&file); 0106 textStream.setEncoding(QStringConverter::Utf8); 0107 // Header is on 3rd line 0108 textStream.readLine(1024); 0109 textStream.readLine(1024); 0110 QString header = textStream.readLine(1024); 0111 0112 if (!header.startsWith(QLatin1String("[Adblock")) || m_title.isEmpty()) { 0113 qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "invalid format of adblock file" << m_filePath; 0114 QTimer::singleShot(0, this, &AdBlockSubscription::updateSubscription); 0115 return; 0116 } 0117 0118 qDeleteAll(m_rules); 0119 m_rules.clear(); 0120 0121 while (!textStream.atEnd()) { 0122 const QString line = textStream.readLine().trimmed(); 0123 if (line.isEmpty()) { 0124 continue; 0125 } 0126 auto *rule = new AdBlockRule(line, this); 0127 if (disabledRules.contains(rule->filter())) { 0128 rule->setEnabled(false); 0129 } 0130 m_rules.append(rule); 0131 } 0132 0133 // Initial update 0134 if (m_rules.isEmpty() && !m_updated) { 0135 QTimer::singleShot(0, this, &AdBlockSubscription::updateSubscription); 0136 } 0137 } 0138 0139 void AdBlockSubscription::saveSubscription() 0140 { 0141 } 0142 0143 void AdBlockSubscription::updateSubscription() 0144 { 0145 if (m_reply || !m_url.isValid()) { 0146 return; 0147 } 0148 0149 m_reply = mApp->networkManager()->get(QNetworkRequest(m_url)); 0150 connect(m_reply, &QNetworkReply::finished, this, &AdBlockSubscription::subscriptionDownloaded); 0151 } 0152 0153 void AdBlockSubscription::subscriptionDownloaded() 0154 { 0155 if (m_reply != qobject_cast<QNetworkReply*>(sender())) { 0156 return; 0157 } 0158 0159 bool error = false; 0160 const QByteArray response = QString::fromUtf8(m_reply->readAll()).toUtf8(); 0161 0162 if (m_reply->error() != QNetworkReply::NoError || 0163 !response.startsWith(QByteArray("[Adblock")) || 0164 !saveDownloadedData(response) 0165 ) { 0166 error = true; 0167 } 0168 0169 m_reply->deleteLater(); 0170 m_reply = nullptr; 0171 0172 if (error) { 0173 Q_EMIT subscriptionError(tr("Cannot load subscription!")); 0174 return; 0175 } 0176 0177 loadSubscription(AdBlockManager::instance()->disabledRules()); 0178 0179 Q_EMIT subscriptionUpdated(); 0180 Q_EMIT subscriptionChanged(); 0181 } 0182 0183 bool AdBlockSubscription::saveDownloadedData(const QByteArray &data) 0184 { 0185 QSaveFile file(m_filePath); 0186 0187 if (!file.open(QFile::WriteOnly)) { 0188 qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "Unable to open adblock file for writing:" << m_filePath; 0189 return false; 0190 } 0191 0192 // Write subscription header 0193 file.write(QSL("Title: %1\nUrl: %2\n").arg(title(), url().toString()).toUtf8()); 0194 file.write(data); 0195 file.commit(); 0196 return true; 0197 } 0198 0199 const AdBlockRule* AdBlockSubscription::rule(int offset) const 0200 { 0201 if (!QzTools::containsIndex(m_rules, offset)) { 0202 return nullptr; 0203 } 0204 0205 return m_rules[offset]; 0206 } 0207 0208 QVector<AdBlockRule*> AdBlockSubscription::allRules() const 0209 { 0210 return m_rules; 0211 } 0212 0213 const AdBlockRule* AdBlockSubscription::enableRule(int offset) 0214 { 0215 if (!QzTools::containsIndex(m_rules, offset)) { 0216 return nullptr; 0217 } 0218 0219 AdBlockRule* rule = m_rules[offset]; 0220 rule->setEnabled(true); 0221 AdBlockManager::instance()->removeDisabledRule(rule->filter()); 0222 0223 Q_EMIT subscriptionChanged(); 0224 0225 if (rule->isCssRule()) 0226 mApp->reloadUserStyleSheet(); 0227 0228 return rule; 0229 } 0230 0231 const AdBlockRule* AdBlockSubscription::disableRule(int offset) 0232 { 0233 if (!QzTools::containsIndex(m_rules, offset)) { 0234 return nullptr; 0235 } 0236 0237 AdBlockRule* rule = m_rules[offset]; 0238 rule->setEnabled(false); 0239 AdBlockManager::instance()->addDisabledRule(rule->filter()); 0240 0241 Q_EMIT subscriptionChanged(); 0242 0243 if (rule->isCssRule()) 0244 mApp->reloadUserStyleSheet(); 0245 0246 return rule; 0247 } 0248 0249 bool AdBlockSubscription::canEditRules() const 0250 { 0251 return false; 0252 } 0253 0254 bool AdBlockSubscription::canBeRemoved() const 0255 { 0256 return true; 0257 } 0258 0259 int AdBlockSubscription::addRule(AdBlockRule* rule) 0260 { 0261 Q_UNUSED(rule) 0262 return -1; 0263 } 0264 0265 bool AdBlockSubscription::removeRule(int offset) 0266 { 0267 Q_UNUSED(offset) 0268 return false; 0269 } 0270 0271 const AdBlockRule* AdBlockSubscription::replaceRule(AdBlockRule* rule, int offset) 0272 { 0273 Q_UNUSED(rule) 0274 Q_UNUSED(offset) 0275 return nullptr; 0276 } 0277 0278 AdBlockSubscription::~AdBlockSubscription() 0279 { 0280 qDeleteAll(m_rules); 0281 } 0282 0283 // AdBlockCustomList 0284 0285 AdBlockCustomList::AdBlockCustomList(QObject* parent) 0286 : AdBlockSubscription(tr("Custom Rules"), parent) 0287 { 0288 setFilePath(DataPaths::currentProfilePath() + QLatin1String("/adblock/customlist.txt")); 0289 } 0290 0291 void AdBlockCustomList::loadSubscription(const QStringList &disabledRules) 0292 { 0293 // DuckDuckGo ad whitelist rules 0294 // They cannot be removed, but can be disabled. 0295 // Please consider not disabling them. Thanks! 0296 0297 const QString ddg1 = QSL("@@||duckduckgo.com^$document"); 0298 const QString ddg2 = QSL("duckduckgo.com#@#.has-ad"); 0299 0300 const QString rules = QzTools::readAllFileContents(filePath()); 0301 0302 QFile file(filePath()); 0303 if (!file.exists()) { 0304 saveSubscription(); 0305 } 0306 0307 if (file.open(QFile::WriteOnly | QFile::Append)) { 0308 QTextStream stream(&file); 0309 stream.setEncoding(QStringConverter::Utf8); 0310 0311 if (!rules.contains(ddg1 + QL1S("\n"))) 0312 stream << ddg1 << Qt::endl; 0313 0314 if (!rules.contains(QL1S("\n") + ddg2)) 0315 stream << ddg2 << Qt::endl; 0316 } 0317 file.close(); 0318 0319 AdBlockSubscription::loadSubscription(disabledRules); 0320 } 0321 0322 void AdBlockCustomList::saveSubscription() 0323 { 0324 QFile file(filePath()); 0325 0326 if (!file.open(QFile::ReadWrite | QFile::Truncate)) { 0327 qWarning() << "AdBlockSubscription::" << __FUNCTION__ << "Unable to open adblock file for writing:" << filePath(); 0328 return; 0329 } 0330 0331 QTextStream textStream(&file); 0332 textStream.setEncoding(QStringConverter::Utf8); 0333 textStream << "Title: " << title() << Qt::endl; 0334 textStream << "Url: " << url().toString() << Qt::endl; 0335 textStream << "[Adblock Plus 1.1.1]" << Qt::endl; 0336 0337 for (const AdBlockRule* rule : std::as_const(m_rules)) { 0338 textStream << rule->filter() << Qt::endl; 0339 } 0340 0341 file.close(); 0342 } 0343 0344 bool AdBlockCustomList::canEditRules() const 0345 { 0346 return true; 0347 } 0348 0349 bool AdBlockCustomList::canBeRemoved() const 0350 { 0351 return false; 0352 } 0353 0354 bool AdBlockCustomList::containsFilter(const QString &filter) const 0355 { 0356 for (const AdBlockRule* rule : std::as_const(m_rules)) { 0357 if (rule->filter() == filter) { 0358 return true; 0359 } 0360 } 0361 0362 return false; 0363 } 0364 0365 bool AdBlockCustomList::removeFilter(const QString &filter) 0366 { 0367 for (int i = 0; i < m_rules.count(); ++i) { 0368 const AdBlockRule* rule = m_rules.at(i); 0369 0370 if (rule->filter() == filter) { 0371 return removeRule(i); 0372 } 0373 } 0374 0375 return false; 0376 } 0377 0378 int AdBlockCustomList::addRule(AdBlockRule* rule) 0379 { 0380 m_rules.append(rule); 0381 0382 Q_EMIT subscriptionChanged(); 0383 0384 if (rule->isCssRule()) 0385 mApp->reloadUserStyleSheet(); 0386 0387 return m_rules.count() - 1; 0388 } 0389 0390 bool AdBlockCustomList::removeRule(int offset) 0391 { 0392 if (!QzTools::containsIndex(m_rules, offset)) { 0393 return false; 0394 } 0395 0396 AdBlockRule* rule = m_rules.at(offset); 0397 const QString filter = rule->filter(); 0398 0399 m_rules.remove(offset); 0400 0401 Q_EMIT subscriptionChanged(); 0402 0403 if (rule->isCssRule()) 0404 mApp->reloadUserStyleSheet(); 0405 0406 AdBlockManager::instance()->removeDisabledRule(filter); 0407 0408 delete rule; 0409 return true; 0410 } 0411 0412 const AdBlockRule* AdBlockCustomList::replaceRule(AdBlockRule* rule, int offset) 0413 { 0414 if (!QzTools::containsIndex(m_rules, offset)) { 0415 return nullptr; 0416 } 0417 0418 AdBlockRule* oldRule = m_rules.at(offset); 0419 m_rules[offset] = rule; 0420 0421 Q_EMIT subscriptionChanged(); 0422 0423 if (rule->isCssRule() || oldRule->isCssRule()) 0424 mApp->reloadUserStyleSheet(); 0425 0426 delete oldRule; 0427 return m_rules[offset]; 0428 }