Websites(Qt 5.2+): Ignore X-Frame-Options header like in Qt 4.x

This commit is contained in:
Abs62 2017-03-23 18:03:17 +03:00
parent 792b9a4f2f
commit 747185c490
2 changed files with 245 additions and 54 deletions

View file

@ -16,6 +16,165 @@
using std::string; using std::string;
#if QT_VERSION >= 0x050200 // Qt 5.2+
// SecurityWhiteList
SecurityWhiteList & SecurityWhiteList::operator=( SecurityWhiteList const & swl )
{
swlDelete();
swlCopy( swl );
return *this;
}
QWebSecurityOrigin * SecurityWhiteList::setOrigin( QUrl const & url )
{
swlDelete();
originUri = url.toString( QUrl::FullyDecoded );
origin = new QWebSecurityOrigin( url );
return origin;
}
void SecurityWhiteList::swlCopy( SecurityWhiteList const & swl )
{
if( swl.origin )
{
hostsToAccess = swl.hostsToAccess;
originUri = swl.originUri;
origin = new QWebSecurityOrigin( QUrl( originUri ) );
for( QSet< QPair< QString, QString > >::iterator it = hostsToAccess.begin();
it != hostsToAccess.end(); ++it )
origin->addAccessWhitelistEntry( it->first, it->second, QWebSecurityOrigin::AllowSubdomains );
}
}
void SecurityWhiteList::swlDelete()
{
if( origin )
{
for( QSet< QPair< QString, QString > >::iterator it = hostsToAccess.begin();
it != hostsToAccess.end(); ++it )
origin->removeAccessWhitelistEntry( it->first, it->second, QWebSecurityOrigin::AllowSubdomains );
delete origin;
origin = 0;
}
hostsToAccess.clear();
originUri.clear();
}
// AllowFrameReply
AllowFrameReply::AllowFrameReply( QNetworkReply * _reply ) :
baseReply( _reply )
{
// Set base data
setOperation( baseReply->operation() );
setRequest( baseReply->request() );
setUrl( baseReply->url() );
// Signals to own slots
connect( baseReply, SIGNAL( metaDataChanged() ), this, SLOT( applyMetaData() ) );
connect( baseReply, SIGNAL( error( QNetworkReply::NetworkError) ),
this, SLOT( applyError( QNetworkReply::NetworkError ) ) );
// Redirect QNetworkReply signals
connect( baseReply, SIGNAL( downloadProgress( qint64, qint64 ) ),
this, SIGNAL( downloadProgress( qint64, qint64 ) ) );
connect( baseReply, SIGNAL( encrypted() ), this, SIGNAL( encrypted() ) );
connect( baseReply, SIGNAL( finished() ), this, SIGNAL( finished() ) );
connect( baseReply, SIGNAL( preSharedKeyAuthenticationRequired( QSslPreSharedKeyAuthenticator * ) ),
this, SIGNAL( preSharedKeyAuthenticationRequired( QSslPreSharedKeyAuthenticator * ) ) );
connect( baseReply, SIGNAL( redirected( const QUrl & ) ), this, SIGNAL( redirected( const QUrl & ) ) );
connect( baseReply, SIGNAL( sslErrors( const QList< QSslError > & ) ),
this, SIGNAL( sslErrors( const QList< QSslError > & ) ) );
connect( baseReply, SIGNAL( uploadProgress( qint64, qint64 ) ),
this, SIGNAL( uploadProgress( qint64, qint64 ) ) );
// Redirect QIODevice signals
connect( baseReply, SIGNAL( aboutToClose() ), this, SIGNAL( aboutToClose() ) );
connect( baseReply, SIGNAL( bytesWritten( qint64 ) ), this, SIGNAL( bytesWritten( qint64 ) ) );
connect( baseReply, SIGNAL( readChannelFinished() ), this, SIGNAL( readChannelFinished() ) );
connect( baseReply, SIGNAL( readyRead() ), this, SIGNAL( readyRead() ) );
setOpenMode( QIODevice::ReadOnly );
}
void AllowFrameReply::applyMetaData()
{
// Set raw headers except X-Frame-Options
QList< QByteArray > rawHeaders = baseReply->rawHeaderList();
for( QList< QByteArray >::iterator it = rawHeaders.begin(); it != rawHeaders.end(); ++it )
{
if( it->toLower() != "x-frame-options" )
setRawHeader( *it, baseReply->rawHeader( *it ) );
}
// Set known headers
setHeader( QNetworkRequest::ContentDispositionHeader,
baseReply->header( QNetworkRequest::ContentDispositionHeader ) );
setHeader( QNetworkRequest::ContentTypeHeader,
baseReply->header( QNetworkRequest::ContentTypeHeader ) );
setHeader( QNetworkRequest::ContentLengthHeader,
baseReply->header( QNetworkRequest::ContentLengthHeader ) );
setHeader( QNetworkRequest::LocationHeader,
baseReply->header( QNetworkRequest::LocationHeader ) );
setHeader( QNetworkRequest::LastModifiedHeader,
baseReply->header( QNetworkRequest::LastModifiedHeader ) );
setHeader( QNetworkRequest::CookieHeader,
baseReply->header( QNetworkRequest::CookieHeader ) );
setHeader( QNetworkRequest::SetCookieHeader,
baseReply->header( QNetworkRequest::SetCookieHeader ) );
setHeader( QNetworkRequest::UserAgentHeader,
baseReply->header( QNetworkRequest::UserAgentHeader ) );
setHeader( QNetworkRequest::ServerHeader,
baseReply->header( QNetworkRequest::ServerHeader ) );
// Set attributes
setAttribute( QNetworkRequest::HttpStatusCodeAttribute,
baseReply->attribute( QNetworkRequest::HttpStatusCodeAttribute ) );
setAttribute( QNetworkRequest::HttpReasonPhraseAttribute,
baseReply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ) );
setAttribute( QNetworkRequest::RedirectionTargetAttribute,
baseReply->attribute( QNetworkRequest::RedirectionTargetAttribute ) );
setAttribute( QNetworkRequest::ConnectionEncryptedAttribute,
baseReply->attribute( QNetworkRequest::ConnectionEncryptedAttribute ) );
setAttribute( QNetworkRequest::SourceIsFromCacheAttribute,
baseReply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ) );
setAttribute( QNetworkRequest::HttpPipeliningWasUsedAttribute,
baseReply->attribute( QNetworkRequest::HttpPipeliningWasUsedAttribute ) );
setAttribute( QNetworkRequest::BackgroundRequestAttribute,
baseReply->attribute( QNetworkRequest::BackgroundRequestAttribute ) );
setAttribute( QNetworkRequest::SpdyWasUsedAttribute,
baseReply->attribute( QNetworkRequest::SpdyWasUsedAttribute ) );
emit metaDataChanged();
}
void AllowFrameReply::applyError( QNetworkReply::NetworkError code )
{
setError( code, baseReply->errorString() );
emit error( code );
}
#endif
namespace namespace
{ {
/// Uses some heuristics to chop off the first domain name from the host name, /// Uses some heuristics to chop off the first domain name from the host name,
@ -69,7 +228,7 @@ QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op,
return QNetworkAccessManager::createRequest( op, newReq, outgoingData ); return QNetworkAccessManager::createRequest( op, newReq, outgoingData );
} }
#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) #if QT_VERSION >= 0x050200 // Qt 5.2+
// Workaround of same-origin policy // Workaround of same-origin policy
if( ( req.url().scheme().startsWith( "http" ) || req.url().scheme() == "ftp" ) if( ( req.url().scheme().startsWith( "http" ) || req.url().scheme() == "ftp" )
&& req.hasRawHeader( "Referer" ) ) && req.hasRawHeader( "Referer" ) )
@ -171,7 +330,11 @@ QNetworkReply * ArticleNetworkAccessManager::createRequest( Operation op,
#endif #endif
} }
#if QT_VERSION >= 0x050200 // Qt 5.2+
return new AllowFrameReply( reply );
#else
return reply; return reply;
#endif
} }
sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource( sptr< Dictionary::DataRequest > ArticleNetworkAccessManager::getResource(

View file

@ -6,7 +6,7 @@
#include <QtNetwork> #include <QtNetwork>
#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) #if QT_VERSION >= 0x050200 // Qt 5.2+
#include <QWebSecurityOrigin> #include <QWebSecurityOrigin>
#include <QSet> #include <QSet>
#include <QMap> #include <QMap>
@ -21,7 +21,7 @@ using std::vector;
/// A custom QNetworkAccessManager version which fetches images from the /// A custom QNetworkAccessManager version which fetches images from the
/// dictionaries when requested. /// dictionaries when requested.
#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) #if QT_VERSION >= 0x050200 // Qt 5.2+
// White lists for QWebSecurityOrigin // White lists for QWebSecurityOrigin
struct SecurityWhiteList struct SecurityWhiteList
@ -35,64 +35,92 @@ struct SecurityWhiteList
{} {}
~SecurityWhiteList() ~SecurityWhiteList()
{ { swlDelete(); }
swlDelete();
}
SecurityWhiteList( SecurityWhiteList const & swl ) : SecurityWhiteList( SecurityWhiteList const & swl ) :
origin( 0 ) origin( 0 )
{ { swlCopy( swl ); }
swlCopy( swl );
}
SecurityWhiteList & operator=( SecurityWhiteList const & swl ) SecurityWhiteList & operator=( SecurityWhiteList const & swl );
{ QWebSecurityOrigin * setOrigin( QUrl const & url );
swlDelete();
swlCopy( swl );
return *this;
}
QWebSecurityOrigin * setOrigin( QUrl const & url )
{
swlDelete();
originUri = url.toString( QUrl::FullyDecoded );
origin = new QWebSecurityOrigin( url );
return origin;
}
private: private:
void swlCopy( SecurityWhiteList const & swl );
void swlCopy( SecurityWhiteList const & swl ) void swlDelete();
{
if( swl.origin )
{
hostsToAccess = swl.hostsToAccess;
originUri = swl.originUri;
origin = new QWebSecurityOrigin( QUrl( originUri ) );
for( QSet< QPair< QString, QString > >::iterator it = hostsToAccess.begin();
it != hostsToAccess.end(); ++it )
origin->addAccessWhitelistEntry( it->first, it->second, QWebSecurityOrigin::AllowSubdomains );
}
}
void swlDelete()
{
if( origin )
{
for( QSet< QPair< QString, QString > >::iterator it = hostsToAccess.begin();
it != hostsToAccess.end(); ++it )
origin->removeAccessWhitelistEntry( it->first, it->second, QWebSecurityOrigin::AllowSubdomains );
delete origin;
origin = 0;
}
hostsToAccess.clear();
originUri.clear();
}
}; };
typedef QMap< QString, SecurityWhiteList > Origins; typedef QMap< QString, SecurityWhiteList > Origins;
// Proxy class for QNetworkReply to remove X-Frame-Options header
// It allow to show websites in <iframe> tag like Qt 4.x
class AllowFrameReply : public QNetworkReply
{
Q_OBJECT
private:
QNetworkReply * baseReply;
AllowFrameReply();
AllowFrameReply( AllowFrameReply const & );
public:
explicit AllowFrameReply( QNetworkReply * _reply );
~AllowFrameReply()
{ delete baseReply; }
// QNetworkReply virtual functions
void setReadBufferSize( qint64 size )
{ baseReply->setReadBufferSize( size ); }
void close()
{ baseReply->close(); }
// QIODevice virtual functions
bool atEnd() const
{ return baseReply->atEnd(); }
qint64 bytesAvailable() const
{ return baseReply->bytesAvailable(); }
qint64 bytesToWrite() const
{ return baseReply->bytesToWrite(); }
bool canReadLine() const
{ return baseReply->canReadLine(); }
bool isSequential() const
{ return baseReply->isSequential(); }
bool waitForReadyRead( int msecs )
{ return baseReply->waitForReadyRead( msecs ); }
bool waitForBytesWritten( int msecs )
{ return baseReply->waitForBytesWritten( msecs ); }
bool reset()
{ return baseReply->reset(); }
public slots:
// Own AllowFrameReply slots
void applyMetaData();
void applyError( QNetworkReply::NetworkError code );
// Redirect QNetworkReply slots
virtual void abort()
{ baseReply->abort(); }
virtual void ignoreSslErrors()
{ baseReply->ignoreSslErrors(); }
protected:
// QNetworkReply virtual functions
void ignoreSslErrorsImplementation( const QList< QSslError > & errors )
{ baseReply->ignoreSslErrors( errors ); }
void setSslConfigurationImplementation( const QSslConfiguration & configuration )
{ baseReply->setSslConfiguration( configuration ); }
void sslConfigurationImplementation( QSslConfiguration & configuration ) const
{ configuration = baseReply->sslConfiguration(); }
// QIODevice virtual functions
qint64 readData( char * data, qint64 maxSize )
{ return baseReply->read( data, maxSize ); }
qint64 readLineData( char * data, qint64 maxSize )
{ return baseReply->readLine( data, maxSize ); }
qint64 writeData( const char * data, qint64 maxSize )
{ return baseReply->write( data, maxSize ); }
};
#endif #endif
class ArticleNetworkAccessManager: public QNetworkAccessManager class ArticleNetworkAccessManager: public QNetworkAccessManager
@ -101,7 +129,7 @@ class ArticleNetworkAccessManager: public QNetworkAccessManager
ArticleMaker const & articleMaker; ArticleMaker const & articleMaker;
bool const & disallowContentFromOtherSites; bool const & disallowContentFromOtherSites;
bool const & hideGoldenDictHeader; bool const & hideGoldenDictHeader;
#if QT_VERSION >= QT_VERSION_CHECK(5,2,0) #if QT_VERSION >= 0x050200 // Qt 5.2+
Origins allOrigins; Origins allOrigins;
#endif #endif
public: public: