diff --git a/article_maker.cc b/article_maker.cc index e18b63fb..5df14417 100644 --- a/article_maker.cc +++ b/article_maker.cc @@ -733,19 +733,19 @@ void ArticleRequest::bodyFinished() { update(); qDebug() << "send dicts(stemmed):" << word << ":" << dictIds; - emit GlobalBroadcaster::instance()->emitDictIds(ActiveDictIds{word, dictIds}); + emit GlobalBroadcaster::instance()->dictionaryChanges(ActiveDictIds{word, dictIds}); dictIds.clear(); } else { finish(); qDebug() << "send dicts(finished):" << word << ":" << dictIds; - emit GlobalBroadcaster::instance()->emitDictIds(ActiveDictIds{word, dictIds}); + emit GlobalBroadcaster::instance()->dictionaryChanges(ActiveDictIds{word, dictIds}); dictIds.clear(); } } else if (wasUpdated) { update(); qDebug() << "send dicts(updated):" << word << ":" << dictIds; - emit GlobalBroadcaster::instance()->emitDictIds(ActiveDictIds{word, dictIds}); + emit GlobalBroadcaster::instance()->dictionaryChanges(ActiveDictIds{word, dictIds}); dictIds.clear(); } } diff --git a/article_netmgr.cc b/article_netmgr.cc index b39a3eeb..9a3edc6b 100644 --- a/article_netmgr.cc +++ b/article_netmgr.cc @@ -29,7 +29,7 @@ using std::string; connect( baseReply, SIGNAL( errorOccurred( QNetworkReply::NetworkError) ), this, SLOT( applyError( QNetworkReply::NetworkError ) ) ); - connect( baseReply, SIGNAL( readyRead() ), this, SLOT( readDataFromBase() ) ); + connect( baseReply, SIGNAL( readyRead() ), this, SIGNAL( readyRead() ) ); // Redirect QNetworkReply signals @@ -68,7 +68,8 @@ using std::string; QList< QByteArray > rawHeaders = baseReply->rawHeaderList(); for( QList< QByteArray >::iterator it = rawHeaders.begin(); it != rawHeaders.end(); ++it ) { - if( it->toLower() != "x-frame-options" ) + auto headerName = it->toLower(); + if( headerName != "x-frame-options" && headerName != "content-security-policy") setRawHeader( *it, baseReply->rawHeader( *it ) ); } @@ -121,7 +122,7 @@ using std::string; qint64 AllowFrameReply::bytesAvailable() const { - return buffer.size() + QNetworkReply::bytesAvailable(); + return baseReply->bytesAvailable(); } void AllowFrameReply::applyError( QNetworkReply::NetworkError code ) @@ -130,20 +131,22 @@ using std::string; emit errorOccurred( code ); } - void AllowFrameReply::readDataFromBase() - { - QByteArray data; - data.resize( baseReply->bytesAvailable() ); - baseReply->read( data.data(), data.size() ); - buffer += data; - emit readyRead(); - } +// void AllowFrameReply::readDataFromBase() +// { +//// QByteArray data; +//// data.resize( baseReply->bytesAvailable() ); +//// baseReply->read( data.data(), data.size() ); +//// buffer += data; +// emit readyRead(); +// } qint64 AllowFrameReply::readData( char * data, qint64 maxSize ) { - qint64 size = qMin( maxSize, qint64( buffer.size() ) ); - memcpy( data, buffer.data(), size ); - buffer.remove( 0, size ); + auto bytesAvailable= baseReply->bytesAvailable(); + qint64 size = qMin( maxSize, bytesAvailable ); + baseReply->read( data, size ); +// memcpy( data, buffer.data(), size ); +// buffer.remove( 0, size ); return size; } @@ -151,6 +154,7 @@ QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op, QNetworkRequest const & req, QIODevice * outgoingData ) { + QUrl url; if ( op == GetOperation ) { if ( req.url().scheme() == "qrcx" ) @@ -168,7 +172,7 @@ QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op, return QNetworkAccessManager::createRequest( op, newReq, outgoingData ); } - QUrl url=req.url(); + url=req.url(); QMimeType mineType=db.mimeTypeForUrl (url); QString contentType=mineType.name(); @@ -178,7 +182,6 @@ QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op, Utils::Url::addQueryItem(url,"word",path.mid(1)); url.setPath(""); Utils::Url::addQueryItem(url,"group","1"); - } } @@ -193,7 +196,7 @@ QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op, //can not match dictionary in the above code,means the url must be external links. //if not external url,can be blocked from here. no need to continue execute the following code. //such as bres://upload.wikimedia.... etc . - if (!Utils::isExternalLink(req.url())) { + if (!Utils::isExternalLink(url)) { gdWarning( "Blocking element \"%s\" as built-in link ", req.url().toEncoded().data() ); return new BlockedNetworkReply( this ); } @@ -205,17 +208,15 @@ QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op, { QByteArray referer = req.rawHeader( "Referer" ); - //GD_DPRINTF( "Referer: %s\n", referer.data() ); - QUrl refererUrl = QUrl::fromEncoded( referer ); //GD_DPRINTF( "Considering %s vs %s\n", getHostBase( req.url() ).toUtf8().data(), // getHostBase( refererUrl ).toUtf8().data() ); - if ( !req.url().host().endsWith( refererUrl.host() ) && - getHostBase( req.url() ) != getHostBase( refererUrl ) && !req.url().scheme().startsWith("data") ) + if ( !url.host().endsWith( refererUrl.host() ) && + getHostBase( url ) != getHostBase( refererUrl ) && !url.scheme().startsWith("data") ) { - gdWarning( "Blocking element \"%s\" due to not same domain", req.url().toEncoded().data() ); + gdWarning( "Blocking element \"%s\" due to not same domain", url.toEncoded().data() ); return new BlockedNetworkReply( this ); } @@ -241,15 +242,17 @@ QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op, } // spoof User-Agent - QNetworkRequest newReq(req); - if ( hideGoldenDictHeader && req.url().scheme().startsWith("http", Qt::CaseInsensitive)) + QNetworkRequest newReq; + newReq.setUrl(url); + newReq.setAttribute( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy ); + if ( hideGoldenDictHeader && url.scheme().startsWith("http", Qt::CaseInsensitive)) { newReq.setRawHeader("User-Agent", req.rawHeader("User-Agent").replace(qApp->applicationName().toUtf8(), "")); } QNetworkReply * reply = QNetworkAccessManager::createRequest( op, newReq, outgoingData ); - if( req.url().scheme() == "https") + if( url.scheme() == "https") { #ifndef QT_NO_OPENSSL connect( reply, SIGNAL( sslErrors( QList< QSslError > ) ), @@ -521,15 +524,11 @@ LocalSchemeHandler::LocalSchemeHandler(ArticleNetworkAccessManager& articleNetMg void LocalSchemeHandler::requestStarted(QWebEngineUrlRequestJob *requestJob) { - QUrl url = requestJob->requestUrl(); + QUrl url = requestJob->requestUrl(); + QNetworkRequest request; + request.setUrl( url ); - QNetworkRequest request; - request.setUrl( url ); - - QNetworkReply* reply=this->mManager.createRequest(QNetworkAccessManager::GetOperation,request); - connect(reply,&QNetworkReply::finished,requestJob,[=](){ - requestJob->reply("text/html",reply); - }); - connect(requestJob, &QObject::destroyed, reply, &QObject::deleteLater); + QNetworkReply * reply = this->mManager.createRequest( QNetworkAccessManager::GetOperation, request ); + connect( reply, &QNetworkReply::finished, requestJob, [ = ]() { requestJob->reply( "text/html", reply ); } ); + connect( requestJob, &QObject::destroyed, reply, &QObject::deleteLater ); } - diff --git a/article_netmgr.hh b/article_netmgr.hh index 4400df99..2e881b28 100644 --- a/article_netmgr.hh +++ b/article_netmgr.hh @@ -66,7 +66,7 @@ public slots: // Own AllowFrameReply slots void applyMetaData(); void applyError( QNetworkReply::NetworkError code ); - void readDataFromBase(); +// void readDataFromBase(); // Redirect QNetworkReply slots virtual void abort() @@ -214,5 +214,6 @@ protected: private: ArticleNetworkAccessManager& mManager; + QNetworkAccessManager mgr; }; #endif diff --git a/articleview.cc b/articleview.cc index 3c14acc1..f366129d 100644 --- a/articleview.cc +++ b/articleview.cc @@ -347,7 +347,7 @@ ArticleView::ArticleView( QWidget * parent, ArticleNetworkAccessManager & nm, Au // Variable name for store current selection range rangeVarName = QString( "sr_%1" ).arg( QString::number( (quint64)this, 16 ) ); - connect(GlobalBroadcaster::instance(), SIGNAL( emitDictIds(ActiveDictIds)), this, + connect(GlobalBroadcaster::instance(), SIGNAL( dictionaryChanges(ActiveDictIds)), this, SLOT(setActiveDictIds(ActiveDictIds))); channel = new QWebChannel(ui.definition->page()); diff --git a/globalbroadcaster.cpp b/globalbroadcaster.cpp index 9d58a2df..761e2682 100644 --- a/globalbroadcaster.cpp +++ b/globalbroadcaster.cpp @@ -19,4 +19,11 @@ Config::Preferences * GlobalBroadcaster::getPreference() return preference; } +void GlobalBroadcaster::addWhitelist(QString url){ + whitelist.push_back(url); +} + +bool GlobalBroadcaster::existedInWhitelist(QString url){ + return std::find(whitelist.begin(), whitelist.end(), url) != whitelist.end(); +} // namespace global diff --git a/globalbroadcaster.h b/globalbroadcaster.h index 7298e3a6..3014a46e 100644 --- a/globalbroadcaster.h +++ b/globalbroadcaster.h @@ -2,6 +2,7 @@ #define GLOBAL_GLOBALBROADCASTER_H #include +#include #include "config.hh" struct ActiveDictIds @@ -15,14 +16,16 @@ class GlobalBroadcaster : public QObject Q_OBJECT private: Config::Preferences * preference; - + std::vector whitelist; public: void setPreference( Config::Preferences * _pre ); Config::Preferences * getPreference(); GlobalBroadcaster( QObject * parent = nullptr ); + void addWhitelist(QString host); + bool existedInWhitelist(QString host); static GlobalBroadcaster * instance(); signals: - void emitDictIds( ActiveDictIds ad ); + void dictionaryChanges( ActiveDictIds ad ); }; #endif // GLOBAL_GLOBALBROADCASTER_H diff --git a/goldendict.pro b/goldendict.pro index 8c119681..02d69efe 100644 --- a/goldendict.pro +++ b/goldendict.pro @@ -226,6 +226,7 @@ DEFINES += PROGRAM_VERSION=\\\"$$VERSION\\\" HEADERS += folding.hh \ article_inspect.h \ globalbroadcaster.h \ + iframeschemehandler.h \ inc_case_folding.hh \ inc_diacritic_folding.hh \ mainwindow.hh \ @@ -366,6 +367,7 @@ FORMS += groups.ui \ SOURCES += folding.cc \ article_inspect.cpp \ globalbroadcaster.cpp \ + iframeschemehandler.cpp \ main.cc \ dictionary.cc \ config.cc \ diff --git a/iframeschemehandler.cpp b/iframeschemehandler.cpp new file mode 100644 index 00000000..cc1d6d5e --- /dev/null +++ b/iframeschemehandler.cpp @@ -0,0 +1,104 @@ +#include "iframeschemehandler.h" + +#include + +IframeSchemeHandler::IframeSchemeHandler(QObject * parent):QWebEngineUrlSchemeHandler(parent){ + +} +void IframeSchemeHandler::requestStarted(QWebEngineUrlRequestJob *requestJob) +{ + QUrl url = requestJob->requestUrl(); + + // website dictionary iframe url + url = QUrl( Utils::Url::queryItemValue( url, "url" ) ); + QNetworkRequest request; + request.setUrl( url ); + + QNetworkReply * reply = mgr.get( request ); + auto finishAction = [ = ]() -> void + { + // Handle reply data + + QByteArray replyData = reply->readAll(); + QString articleString; + + QTextCodec * codec = QTextCodec::codecForHtml( replyData ); + if( codec ) + articleString = codec->toUnicode( replyData ); + else + articleString = QString::fromUtf8( replyData ); + + // Change links from relative to absolute + + QString root = reply->url().scheme() + "://" + reply->url().host(); + QString base = root + reply->url().path(); + while( !base.isEmpty() && !base.endsWith( "/" ) ) + base.chop( 1 ); + + QRegularExpression tags( "<\\s*(a|link|img|script)\\s+[^>]*(src|href)\\s*=\\s*['\"][^>]+>", + QRegularExpression::CaseInsensitiveOption ); + QRegularExpression links( "\\b(src|href)\\s*=\\s*(['\"])([^'\"]+['\"])", + QRegularExpression::CaseInsensitiveOption ); + int pos = 0; + QString articleNewString; + QRegularExpressionMatchIterator it = tags.globalMatch( articleString ); + while( it.hasNext() ) + { + QRegularExpressionMatch match = it.next(); + articleNewString += articleString.mid( pos, match.capturedStart() - pos ); + pos = match.capturedEnd(); + + QString tag = match.captured(); + + QRegularExpressionMatch match_links = links.match( tag ); + if( !match_links.hasMatch() ) + { + articleNewString += tag; + continue; + } + + QString url = match_links.captured( 3 ); + + if( url.indexOf( ":/" ) >= 0 || url.indexOf( "data:" ) >= 0 || url.indexOf( "mailto:" ) >= 0 || + url.startsWith( "#" ) || url.startsWith( "javascript:" ) ) + { + // External link, anchor or base64-encoded data + articleNewString += tag; + continue; + } + + QString newUrl = match_links.captured( 1 ) + "=" + match_links.captured( 2 ); + if( url.startsWith( "//" ) ) + newUrl += reply->url().scheme() + ":"; + else if( url.startsWith( "/" ) ) + newUrl += root; + else + newUrl += base; + newUrl += match_links.captured( 3 ); + + tag.replace( match_links.capturedStart(), match_links.capturedLength(), newUrl ); + articleNewString += tag; + } + if( pos ) + { + articleNewString += articleString.mid( pos ); + articleString = articleNewString; + articleNewString.clear(); + } + + sptr< Dictionary::DataRequestInstant > response = new Dictionary::DataRequestInstant( true ); + + auto content = articleString.toStdString(); + response->getData().resize( content.size() ); + memcpy( &( response->getData().front() ), content.data(), content.size() ); + + auto contentType="text/html"; + auto newReply = new ArticleResourceReply( this, request, response, contentType ); + + requestJob->reply( contentType, newReply ); + connect( requestJob, &QObject::destroyed, newReply, &QObject::deleteLater ); + }; + connect( reply, &QNetworkReply::finished, requestJob, finishAction ); + + connect( requestJob, &QObject::destroyed, reply, &QObject::deleteLater ); +} diff --git a/iframeschemehandler.h b/iframeschemehandler.h new file mode 100644 index 00000000..2b8005a8 --- /dev/null +++ b/iframeschemehandler.h @@ -0,0 +1,19 @@ +#ifndef IFRAMESCHEMEHANDLER_H +#define IFRAMESCHEMEHANDLER_H + +#include"article_netmgr.hh" + +class IframeSchemeHandler : public QWebEngineUrlSchemeHandler +{ + Q_OBJECT +public: + IframeSchemeHandler(QObject * parent=nullptr); + void requestStarted(QWebEngineUrlRequestJob *requestJob); + +protected: + +private: + QNetworkAccessManager mgr; +}; + +#endif // IFRAMESCHEMEHANDLER_H diff --git a/main.cc b/main.cc index 316ed352..958e2b5d 100644 --- a/main.cc +++ b/main.cc @@ -253,7 +253,7 @@ int main( int argc, char ** argv ) #endif - QStringList localSchemes={"gdlookup","gdau","gico","qrcx","bres","bword","gdprg","gdvideo","gdpicture","gdtts"}; + QStringList localSchemes={"gdlookup","gdau","gico","qrcx","bres","bword","gdprg","gdvideo","gdpicture","gdtts","ifr"}; for (int i = 0; i < localSchemes.size(); ++i) { diff --git a/mainwindow.cc b/mainwindow.cc index c4716e54..cb9e3410 100644 --- a/mainwindow.cc +++ b/mainwindow.cc @@ -145,14 +145,19 @@ MainWindow::MainWindow( Config::Class & cfg_ ): GlobalBroadcaster::instance()->setPreference(&cfg.preferences); - localSchemeHandler = new LocalSchemeHandler(articleNetMgr); - QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("gdlookup", localSchemeHandler); - QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("bword", localSchemeHandler); + localSchemeHandler = new LocalSchemeHandler( articleNetMgr ); + QWebEngineProfile::defaultProfile()->installUrlSchemeHandler( "gdlookup", localSchemeHandler ); + QWebEngineProfile::defaultProfile()->installUrlSchemeHandler( "bword", localSchemeHandler ); - QStringList localSchemes={"gdau","gico","qrcx","bres","gdprg","gdvideo","gdpicture","gdtts"}; - resourceSchemeHandler = new ResourceSchemeHandler(articleNetMgr); - for(int i=0;iinstallUrlSchemeHandler(localSchemes.at(i).toLatin1(), resourceSchemeHandler); + iframeSchemeHandler = new IframeSchemeHandler( this ); + QWebEngineProfile::defaultProfile()->installUrlSchemeHandler( "ifr", iframeSchemeHandler ); + + QStringList localSchemes = { "gdau", "gico", "qrcx", "bres", "gdprg", "gdvideo", "gdpicture", "gdtts" }; + resourceSchemeHandler = new ResourceSchemeHandler( articleNetMgr ); + for( int i = 0; i < localSchemes.size(); i++ ) + { + QWebEngineProfile::defaultProfile()->installUrlSchemeHandler( localSchemes.at( i ).toLatin1(), + resourceSchemeHandler ); } wuri = new WebUrlRequestInterceptor(); diff --git a/mainwindow.hh b/mainwindow.hh index 3867c1b0..05a30436 100644 --- a/mainwindow.hh +++ b/mainwindow.hh @@ -34,6 +34,7 @@ #include "hotkeywrapper.hh" #include "weburlrequestinterceptor.h" #include "resourceschemehandler.h" +#include "iframeschemehandler.h" #ifdef HAVE_X11 #include #endif @@ -176,8 +177,9 @@ private: QIcon starIcon, blueStarIcon; - LocalSchemeHandler *localSchemeHandler; - ResourceSchemeHandler *resourceSchemeHandler; + LocalSchemeHandler * localSchemeHandler; + IframeSchemeHandler * iframeSchemeHandler; + ResourceSchemeHandler * resourceSchemeHandler; /// Applies the qt's stylesheet, given the style's name. void applyQtStyleSheet( QString const & displayStyle, QString const & addonStyle ); diff --git a/website.cc b/website.cc index d7048cc0..6534626e 100644 --- a/website.cc +++ b/website.cc @@ -9,6 +9,7 @@ #include #include #include "gddebug.hh" +#include "globalbroadcaster.h" #include @@ -371,8 +372,12 @@ sptr< DataRequest > WebSiteDictionary::getArticle( wstring const & str, string result = "
"; + //permissive add url to global whitelist. + QUrl url(urlString); + GlobalBroadcaster::instance()->addWhitelist(url.host()); + result += string( "