File indexing completed on 2024-04-21 04:42:37

0001 /*
0002     SPDX-FileCopyrightText: 2009 Dawit Alemayehu <adawit@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "networkaccessmanager.h"
0008 #include "settings/webkitsettings.h"
0009 
0010 #include "kwebkitpart_debug.h"
0011 #include <KLocalizedString>
0012 #include <KProtocolInfo>
0013 #include <KRun>
0014 
0015 #include <QTimer>
0016 #include <QWidget>
0017 #include <QNetworkReply>
0018 #include <QWebFrame>
0019 #include <QWebElementCollection>
0020 #include <QMimeType>
0021 #include <QMimeDatabase>
0022 
0023 
0024 #define QL1S(x) QLatin1String(x)
0025 #define HIDABLE_ELEMENTS   QL1S("audio,img,embed,object,iframe,frame,video")
0026 
0027 /* Null network reply */
0028 class NullNetworkReply : public QNetworkReply
0029 {
0030 public:
0031     explicit NullNetworkReply(const QNetworkRequest &req, QObject* parent = nullptr)
0032         :QNetworkReply(parent)
0033     {
0034         setRequest(req);
0035         setUrl(req.url());
0036         setHeader(QNetworkRequest::ContentLengthHeader, 0);
0037         setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
0038         setError(QNetworkReply::ContentAccessDenied, i18n("Blocked by ad filter"));
0039         setAttribute(QNetworkRequest::User, QNetworkReply::ContentAccessDenied);
0040         QTimer::singleShot(0, this, SIGNAL(finished()));
0041     }
0042 
0043     void abort() override {}
0044     qint64 bytesAvailable() const override { return 0; }
0045 
0046 protected:
0047     qint64 readData(char*, qint64) override {return -1;}
0048 };
0049 
0050 namespace KDEPrivate {
0051 
0052 MyNetworkAccessManager::MyNetworkAccessManager(QObject *parent)
0053                        : KIO::AccessManager(parent)
0054 {
0055 }
0056 
0057 static bool isMixedContent(const QUrl& baseUrl, const QUrl& reqUrl)
0058 {
0059     const QString baseProtocol (baseUrl.scheme());
0060     const bool hasSecureScheme = (baseProtocol.compare(QL1S("https")) == 0 ||
0061                                   baseProtocol.compare(QL1S("webdavs")) == 0);
0062     return (hasSecureScheme && baseProtocol != reqUrl.scheme());
0063 }
0064 
0065 static bool blockRequest(QNetworkAccessManager::Operation op, const QUrl& requestUrl)
0066 {
0067    if (op != QNetworkAccessManager::GetOperation)
0068        return false;
0069 
0070    if (!WebKitSettings::self()->isAdFilterEnabled())
0071        return false;
0072 
0073    if (!WebKitSettings::self()->isAdFiltered(requestUrl.toString()))
0074        return false;
0075 
0076    qCDebug(KWEBKITPART_LOG) << "*** REQUEST BLOCKED: URL" << requestUrl << "RULE" << WebKitSettings::self()->adFilteredBy(requestUrl.toString());
0077    return true;
0078 }
0079 
0080 
0081 QNetworkReply *MyNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData)
0082 {
0083     QWebFrame* frame = qobject_cast<QWebFrame*>(req.originatingObject());
0084 
0085     if (!blockRequest(op, req.url())) {
0086         if (KProtocolInfo::isHelperProtocol(req.url())) {
0087             (void) new KRun(req.url(), qobject_cast<QWidget*>(req.originatingObject()));
0088             return new NullNetworkReply(req, this);
0089         }
0090         QNetworkReply* reply = KIO::AccessManager::createRequest(op, req, outgoingData);
0091         if (frame && isMixedContent(frame->baseUrl(), req.url())) {
0092             connect(reply, SIGNAL(metaDataChanged()), this, SLOT(slotMetaDataChanged()));
0093         }
0094         return reply;
0095     }
0096 
0097     if (frame) {
0098         if (!m_blockedRequests.contains(frame))
0099             connect(frame, SIGNAL(loadFinished(bool)), this, SLOT(slotFinished(bool)));
0100         m_blockedRequests.insert(frame, req.url());
0101     }
0102 
0103     return new NullNetworkReply(req, this);
0104 }
0105 
0106 static void hideBlockedElements(const QUrl& url, QWebElementCollection& collection)
0107 {
0108     for (QWebElementCollection::iterator it = collection.begin(); it != collection.end(); ++it) {
0109         const QUrl baseUrl ((*it).webFrame()->baseUrl());
0110         QString src = (*it).attribute(QL1S("src"));
0111         if (src.isEmpty())
0112             src = (*it).evaluateJavaScript(QL1S("this.src")).toString();
0113         if (src.isEmpty())
0114             continue;
0115         const QUrl resolvedUrl(baseUrl.resolved(QUrl(src)));
0116         if (url == resolvedUrl) {
0117             //qCDebug(KWEBKITPART_LOG) << "*** HIDING ELEMENT: " << (*it).tagName() << resolvedUrl;
0118             (*it).removeFromDocument();
0119         }
0120     }
0121 }
0122 
0123 void MyNetworkAccessManager::slotFinished(bool ok)
0124 {
0125     if (!ok)
0126         return;
0127 
0128     if(!WebKitSettings::self()->isAdFilterEnabled())
0129         return;
0130 
0131     if(!WebKitSettings::self()->isHideAdsEnabled())
0132         return;
0133 
0134     QWebFrame* frame = qobject_cast<QWebFrame*>(sender());
0135     if (!frame)
0136         return;
0137 
0138     QList<QUrl> urls = m_blockedRequests.values(frame);
0139     if (urls.isEmpty())
0140         return;
0141 
0142    QWebElementCollection collection = frame->findAllElements(HIDABLE_ELEMENTS);
0143    if (frame->parentFrame())
0144         collection += frame->parentFrame()->findAllElements(HIDABLE_ELEMENTS);
0145 
0146     Q_FOREACH(const QUrl& url, urls)
0147         hideBlockedElements(url, collection);
0148 }
0149 
0150 static bool isActiveContent(const QString& contentType)
0151 {
0152     QMimeDatabase db;
0153     const QMimeType mime = db.mimeTypeForName(contentType);
0154     return (mime.isValid() && mime.inherits(QLatin1String("application/javascript")));
0155 }
0156 
0157 void MyNetworkAccessManager::slotMetaDataChanged()
0158 {
0159     QNetworkReply* reply = qobject_cast<QNetworkReply*> (sender());
0160     if (reply) {
0161         const bool activeContent = isActiveContent(reply->header(QNetworkRequest::ContentTypeHeader).toString());
0162         if ((activeContent && !WebKitSettings::self()->alowActiveMixedContent()) ||
0163             (!activeContent && !WebKitSettings::self()->allowMixedContentDisplay())) {
0164             reply->abort();
0165             emit QMetaObject::invokeMethod(reply, "finished", Qt::AutoConnection);
0166         }
0167     }
0168 }
0169 }