@ -33,6 +33,7 @@
# include <QDateTime>
# include <QDateTime>
# include <QDebug>
# include <QDebug>
# include <QNetworkAccessManager>
# include <QNetworkCookie>
# include <QNetworkCookie>
# include <QNetworkCookieJar>
# include <QNetworkCookieJar>
# include <QNetworkProxy>
# include <QNetworkProxy>
@ -51,101 +52,81 @@ namespace
{
{
// Disguise as Firefox to avoid web server banning
// Disguise as Firefox to avoid web server banning
const char DEFAULT_USER_AGENT [ ] = " Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0 " ;
const char DEFAULT_USER_AGENT [ ] = " Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0 " ;
}
class NetworkCookieJar final : public QNetworkCookieJar
class Net : : DownloadManager : : NetworkCookieJar final : public QNetworkCookieJar
{
public :
explicit NetworkCookieJar ( QObject * parent = nullptr )
: QNetworkCookieJar ( parent )
{
{
public :
const QDateTime now = QDateTime : : currentDateTime ( ) ;
explicit NetworkCookieJar ( QObject * parent = nullptr )
QList < QNetworkCookie > cookies = Preferences : : instance ( ) - > getNetworkCookies ( ) ;
: QNetworkCookieJar ( parent )
for ( const QNetworkCookie & cookie : asConst ( Preferences : : instance ( ) - > getNetworkCookies ( ) ) )
{
{
const QDateTime now = QDateTime : : currentDateTime ( ) ;
if ( cookie . isSessionCookie ( ) | | ( cookie . expirationDate ( ) < = now ) )
QList < QNetworkCookie > cookies = Preferences : : instance ( ) - > getNetworkCookies ( ) ;
cookies . removeAll ( cookie ) ;
for ( const QNetworkCookie & cookie : asConst ( Preferences : : instance ( ) - > getNetworkCookies ( ) ) )
{
if ( cookie . isSessionCookie ( ) | | ( cookie . expirationDate ( ) < = now ) )
cookies . removeAll ( cookie ) ;
}
setAllCookies ( cookies ) ;
}
}
~ NetworkCookieJar ( ) override
setAllCookies ( cookies ) ;
}
~ NetworkCookieJar ( ) override
{
const QDateTime now = QDateTime : : currentDateTime ( ) ;
QList < QNetworkCookie > cookies = allCookies ( ) ;
for ( const QNetworkCookie & cookie : asConst ( allCookies ( ) ) )
{
{
const QDateTime now = QDateTime : : currentDateTime ( ) ;
if ( cookie . isSessionCookie ( ) | | ( cookie . expirationDate ( ) < = now ) )
QList < QNetworkCookie > cookies = allCookies ( ) ;
cookies . removeAll ( cookie ) ;
for ( const QNetworkCookie & cookie : asConst ( allCookies ( ) ) )
{
if ( cookie . isSessionCookie ( ) | | ( cookie . expirationDate ( ) < = now ) )
cookies . removeAll ( cookie ) ;
}
Preferences : : instance ( ) - > setNetworkCookies ( cookies ) ;
}
}
using QNetworkCookieJar : : allCookies ;
Preferences : : instance ( ) - > setNetworkCookies ( cookies ) ;
using QNetworkCookieJar : : setAllCookies ;
}
QList < QNetworkCookie > cookiesForUrl ( const QUrl & url ) const override
using QNetworkCookieJar : : allCookies ;
{
using QNetworkCookieJar : : setAllCookies ;
const QDateTime now = QDateTime : : currentDateTime ( ) ;
QList < QNetworkCookie > cookies = QNetworkCookieJar : : cookiesForUrl ( url ) ;
for ( const QNetworkCookie & cookie : asConst ( QNetworkCookieJar : : cookiesForUrl ( url ) ) )
{
if ( ! cookie . isSessionCookie ( ) & & ( cookie . expirationDate ( ) < = now ) )
cookies . removeAll ( cookie ) ;
}
return cookies ;
}
bool setCookiesFromUrl ( const QList < QNetworkCookie > & cookieList , const QUrl & url ) override
QList < QNetworkCookie > cookiesForUrl ( const QUrl & url ) const override
{
const QDateTime now = QDateTime : : currentDateTime ( ) ;
QList < QNetworkCookie > cookies = QNetworkCookieJar : : cookiesForUrl ( url ) ;
for ( const QNetworkCookie & cookie : asConst ( QNetworkCookieJar : : cookiesForUrl ( url ) ) )
{
{
const QDateTime now = QDateTime : : currentDateTime ( ) ;
if ( ! cookie . isSessionCookie ( ) & & ( cookie . expirationDate ( ) < = now ) )
QList < QNetworkCookie > cookies = cookieList ;
cookies . removeAll ( cookie ) ;
for ( const QNetworkCookie & cookie : cookieList )
{
if ( ! cookie . isSessionCookie ( ) & & ( cookie . expirationDate ( ) < = now ) )
cookies . removeAll ( cookie ) ;
}
return QNetworkCookieJar : : setCookiesFromUrl ( cookies , url ) ;
}
}
} ;
QNetworkRequest createNetworkRequest ( const Net : : DownloadRequest & downloadRequest )
{
QNetworkRequest request { downloadRequest . url ( ) } ;
if ( downloadRequest . userAgent ( ) . isEmpty ( ) )
return cookies ;
request . setRawHeader ( " User-Agent " , DEFAULT_USER_AGENT ) ;
}
else
request . setRawHeader ( " User-Agent " , downloadRequest . userAgent ( ) . toUtf8 ( ) ) ;
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
bool setCookiesFromUrl ( const QList < QNetworkCookie > & cookieList , const QUrl & url ) override
request . setRawHeader ( " Referer " , request . url ( ) . toEncoded ( ) . data ( ) ) ;
{
# ifdef QT_NO_COMPRESS
const QDateTime now = QDateTime : : currentDateTime ( ) ;
// The macro "QT_NO_COMPRESS" defined in QT will disable the zlib related features
QList < QNetworkCookie > cookies = cookieList ;
// and reply data auto-decompression in QT will also be disabled. But we can support
for ( const QNetworkCookie & cookie : cookieList )
// gzip encoding and manually decompress the reply data.
{
request . setRawHeader ( " Accept-Encoding " , " gzip " ) ;
if ( ! cookie . isSessionCookie ( ) & & ( cookie . expirationDate ( ) < = now ) )
# endif
cookies . removeAll ( cookie ) ;
// Qt doesn't support Magnet protocol so we need to handle redirections manually
}
request . setAttribute ( QNetworkRequest : : RedirectPolicyAttribute , QNetworkRequest : : ManualRedirectPolicy ) ;
return request ;
return QNetworkCookieJar : : setCookiesFromUrl ( cookies , url ) ;
}
}
}
} ;
Net : : DownloadManager * Net : : DownloadManager : : m_instance = nullptr ;
Net : : DownloadManager * Net : : DownloadManager : : m_instance = nullptr ;
Net : : DownloadManager : : DownloadManager ( QObject * parent )
Net : : DownloadManager : : DownloadManager ( QObject * parent )
: QObject ( parent )
: QObject ( parent )
, m_networkCookieJar { new NetworkCookieJar ( this ) }
, m_networkManager { new QNetworkAccessManager ( this ) }
{
{
connect ( & m_networkManager , & QNetworkAccessManager : : sslErrors , this , & Net : : DownloadManager : : ignoreSslErrors ) ;
m_networkManager - > setCookieJar ( m_networkCookieJar ) ;
connect ( & m_networkManager , & QNetworkAccessManager : : finished , this , & DownloadManager : : handleReplyFinished ) ;
connect ( m_networkManager , & QNetworkAccessManager : : sslErrors , this , & Net : : DownloadManager : : ignoreSslErrors ) ;
connect ( ProxyConfigurationManager : : instance ( ) , & ProxyConfigurationManager : : proxyConfigurationChanged
connect ( ProxyConfigurationManager : : instance ( ) , & ProxyConfigurationManager : : proxyConfigurationChanged
, this , & DownloadManager : : applyProxySettings ) ;
, this , & DownloadManager : : applyProxySettings ) ;
m_networkManager . setCookieJar ( new NetworkCookieJar ( this ) ) ;
connect ( Preferences : : instance ( ) , & Preferences : : changed , this , & DownloadManager : : applyProxySettings ) ;
applyProxySettings ( ) ;
applyProxySettings ( ) ;
}
}
@ -166,14 +147,18 @@ Net::DownloadManager *Net::DownloadManager::instance()
return m_instance ;
return m_instance ;
}
}
Net : : DownloadHandler * Net : : DownloadManager : : download ( const DownloadRequest & downloadRequest )
Net : : DownloadHandler * Net : : DownloadManager : : download ( const DownloadRequest & downloadRequest , const bool useProxy )
{
{
// Process download request
// Process download request
const QNetworkRequest request = createNetworkRequest ( downloadRequest ) ;
const ServiceID id = ServiceID : : fromURL ( downloadRequest . url ( ) ) ;
const ServiceID id = ServiceID : : fromURL ( request . url ( ) ) ;
const bool isSequentialService = m_sequentialServices . contains ( id ) ;
const bool isSequentialService = m_sequentialServices . contains ( id ) ;
auto downloadHandler = new DownloadHandlerImpl { this , downloadRequest } ;
auto downloadHandler = new DownloadHandlerImpl ( this , downloadRequest , useProxy ) ;
connect ( downloadHandler , & DownloadHandler : : finished , this
, [ this , downloadHandler ]
{
handleDownloadFinished ( downloadHandler ) ;
} ) ;
connect ( downloadHandler , & DownloadHandler : : finished , downloadHandler , & QObject : : deleteLater ) ;
connect ( downloadHandler , & DownloadHandler : : finished , downloadHandler , & QObject : : deleteLater ) ;
connect ( downloadHandler , & QObject : : destroyed , this , [ this , id , downloadHandler ] ( )
connect ( downloadHandler , & QObject : : destroyed , this , [ this , id , downloadHandler ] ( )
{
{
@ -189,7 +174,7 @@ Net::DownloadHandler *Net::DownloadManager::download(const DownloadRequest &down
qDebug ( " Downloading %s... " , qUtf8Printable ( downloadRequest . url ( ) ) ) ;
qDebug ( " Downloading %s... " , qUtf8Printable ( downloadRequest . url ( ) ) ) ;
if ( isSequentialService )
if ( isSequentialService )
m_busyServices . insert ( id ) ;
m_busyServices . insert ( id ) ;
downloadHandler - > assignNetworkReply ( m_networkManager . get ( request ) ) ;
processRequest ( downloadHandler ) ;
}
}
return downloadHandler ;
return downloadHandler ;
@ -202,32 +187,32 @@ void Net::DownloadManager::registerSequentialService(const Net::ServiceID &servi
QList < QNetworkCookie > Net : : DownloadManager : : cookiesForUrl ( const QUrl & url ) const
QList < QNetworkCookie > Net : : DownloadManager : : cookiesForUrl ( const QUrl & url ) const
{
{
return m_networkManager . cookieJar ( ) - > cookiesForUrl ( url ) ;
return m_networkCookieJar - > cookiesForUrl ( url ) ;
}
}
bool Net : : DownloadManager : : setCookiesFromUrl ( const QList < QNetworkCookie > & cookieList , const QUrl & url )
bool Net : : DownloadManager : : setCookiesFromUrl ( const QList < QNetworkCookie > & cookieList , const QUrl & url )
{
{
return m_networkManager . cookieJar ( ) - > setCookiesFromUrl ( cookieList , url ) ;
return m_networkCookieJar - > setCookiesFromUrl ( cookieList , url ) ;
}
}
QList < QNetworkCookie > Net : : DownloadManager : : allCookies ( ) const
QList < QNetworkCookie > Net : : DownloadManager : : allCookies ( ) const
{
{
return static_cast < NetworkCookieJar * > ( m_networkManager . cookieJar ( ) ) - > allCookies ( ) ;
return m_networkCookieJar - > allCookies ( ) ;
}
}
void Net : : DownloadManager : : setAllCookies ( const QList < QNetworkCookie > & cookieList )
void Net : : DownloadManager : : setAllCookies ( const QList < QNetworkCookie > & cookieList )
{
{
static_cast < NetworkCookieJar * > ( m_networkManager . cookieJar ( ) ) - > setAllCookies ( cookieList ) ;
m_networkCookieJar - > setAllCookies ( cookieList ) ;
}
}
bool Net : : DownloadManager : : deleteCookie ( const QNetworkCookie & cookie )
bool Net : : DownloadManager : : deleteCookie ( const QNetworkCookie & cookie )
{
{
return static_cast < NetworkCookieJar * > ( m_networkManager . cookieJar ( ) ) - > deleteCookie ( cookie ) ;
return m_networkCookieJar - > deleteCookie ( cookie ) ;
}
}
bool Net : : DownloadManager : : hasSupportedScheme ( const QString & url )
bool Net : : DownloadManager : : hasSupportedScheme ( const QString & url )
{
{
const QStringList schemes = instance ( ) - > m_networkManager . supportedSchemes ( ) ;
const QStringList schemes = QNetworkAccessManager ( ) . supportedSchemes ( ) ;
return std : : any_of ( schemes . cbegin ( ) , schemes . cend ( ) , [ & url ] ( const QString & scheme )
return std : : any_of ( schemes . cbegin ( ) , schemes . cend ( ) , [ & url ] ( const QString & scheme )
{
{
return url . startsWith ( ( scheme + u ' : ' ) , Qt : : CaseInsensitive ) ;
return url . startsWith ( ( scheme + u ' : ' ) , Qt : : CaseInsensitive ) ;
@ -238,46 +223,47 @@ void Net::DownloadManager::applyProxySettings()
{
{
const auto * proxyManager = ProxyConfigurationManager : : instance ( ) ;
const auto * proxyManager = ProxyConfigurationManager : : instance ( ) ;
const ProxyConfiguration proxyConfig = proxyManager - > proxyConfiguration ( ) ;
const ProxyConfiguration proxyConfig = proxyManager - > proxyConfiguration ( ) ;
QNetworkProxy proxy ;
if ( ! proxyManager - > isProxyOnlyForTorrents ( ) & & ( proxyConfig . type ! = ProxyType : : None ) )
m_proxy = QNetworkProxy ( QNetworkProxy : : NoProxy ) ;
if ( proxyConfig . type ! = ProxyType : : SOCKS4 )
{
{
// Proxy enabled
// Proxy enabled
proxy . setHostName ( proxyConfig . ip ) ;
if ( proxyConfig . type = = ProxyType : : SOCKS5 )
proxy . setPort ( proxyConfig . port ) ;
// Default proxy type is HTTP, we must change if it is SOCKS5
if ( ( proxyConfig . type = = ProxyType : : SOCKS5 ) | | ( proxyConfig . type = = ProxyType : : SOCKS5_PW ) )
{
{
qDebug ( ) < < Q_FUNC_INFO < < " using SOCKS proxy " ;
qDebug ( ) < < Q_FUNC_INFO < < " using SOCKS proxy " ;
proxy . setType ( QNetworkProxy : : Socks5Proxy ) ;
m_proxy . setType ( QNetworkProxy : : Socks5Proxy ) ;
}
}
else
else
{
{
qDebug ( ) < < Q_FUNC_INFO < < " using HTTP proxy " ;
qDebug ( ) < < Q_FUNC_INFO < < " using HTTP proxy " ;
proxy . setType ( QNetworkProxy : : HttpProxy ) ;
m_ proxy. setType ( QNetworkProxy : : HttpProxy ) ;
}
}
m_proxy . setHostName ( proxyConfig . ip ) ;
m_proxy . setPort ( proxyConfig . port ) ;
// Authentication?
// Authentication?
if ( proxyManager - > isAuthenticationRequired ( ) )
if ( proxyConfig . authEnabled )
{
{
qDebug ( " Proxy requires authentication, authenticating... " ) ;
qDebug ( " Proxy requires authentication, authenticating... " ) ;
proxy . setUser ( proxyConfig . username ) ;
m_ proxy. setUser ( proxyConfig . username ) ;
proxy . setPassword ( proxyConfig . password ) ;
m_ proxy. setPassword ( proxyConfig . password ) ;
}
}
}
else
{
proxy . setType ( QNetworkProxy : : NoProxy ) ;
}
m_networkManager . setProxy ( proxy ) ;
if ( proxyConfig . hostnameLookupEnabled )
m_proxy . setCapabilities ( m_proxy . capabilities ( ) | QNetworkProxy : : HostNameLookupCapability ) ;
else
m_proxy . setCapabilities ( m_proxy . capabilities ( ) & ~ QNetworkProxy : : HostNameLookupCapability ) ;
}
}
}
void Net : : DownloadManager : : handleReplyFinished ( const QNetworkReply * reply )
void Net : : DownloadManager : : handleDownloadFinished ( DownloadHandlerImpl * finishedHandler )
{
{
// QNetworkReply::url() may be different from that of the original request
// QNetworkReply::url() may be different from that of the original request
// so we need QNetworkRequest::url() to properly process Sequential Services
// so we need QNetworkRequest::url() to properly process Sequential Services
// in the case when the redirection occurred.
// in the case when the redirection occurred.
const ServiceID id = ServiceID : : fromURL ( reply - > request ( ) . url ( ) ) ;
const ServiceID id = ServiceID : : fromURL ( finishedHandler - > url ( ) ) ;
const auto waitingJobsIter = m_waitingJobs . find ( id ) ;
const auto waitingJobsIter = m_waitingJobs . find ( id ) ;
if ( ( waitingJobsIter = = m_waitingJobs . end ( ) ) | | waitingJobsIter . value ( ) . isEmpty ( ) )
if ( ( waitingJobsIter = = m_waitingJobs . end ( ) ) | | waitingJobsIter . value ( ) . isEmpty ( ) )
{
{
@ -286,12 +272,38 @@ void Net::DownloadManager::handleReplyFinished(const QNetworkReply *reply)
return ;
return ;
}
}
auto handler = static_cast < DownloadHandlerImpl * > ( waitingJobsIter . value ( ) . dequeue ( ) ) ;
auto handler = waitingJobsIter . value ( ) . dequeue ( ) ;
qDebug ( " Downloading %s... " , qUtf8Printable ( handler - > url ( ) ) ) ;
qDebug ( " Downloading %s... " , qUtf8Printable ( handler - > url ( ) ) ) ;
handler - > assignNetwo rkReply ( m_netw orkManager . g et ( createNetwork Request( handler - > downloadRequest ( ) ) ) ) ;
p roc ess Request( handler ) ;
handler - > disconnect ( this ) ;
handler - > disconnect ( this ) ;
}
}
void Net : : DownloadManager : : processRequest ( DownloadHandlerImpl * downloadHandler )
{
m_networkManager - > setProxy ( ( downloadHandler - > useProxy ( ) = = true ) ? m_proxy : QNetworkProxy ( QNetworkProxy : : NoProxy ) ) ;
const DownloadRequest downloadRequest = downloadHandler - > downloadRequest ( ) ;
QNetworkRequest request { downloadRequest . url ( ) } ;
if ( downloadRequest . userAgent ( ) . isEmpty ( ) )
request . setRawHeader ( " User-Agent " , DEFAULT_USER_AGENT ) ;
else
request . setRawHeader ( " User-Agent " , downloadRequest . userAgent ( ) . toUtf8 ( ) ) ;
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
request . setRawHeader ( " Referer " , request . url ( ) . toEncoded ( ) . data ( ) ) ;
# ifdef QT_NO_COMPRESS
// The macro "QT_NO_COMPRESS" defined in QT will disable the zlib related features
// and reply data auto-decompression in QT will also be disabled. But we can support
// gzip encoding and manually decompress the reply data.
request . setRawHeader ( " Accept-Encoding " , " gzip " ) ;
# endif
// Qt doesn't support Magnet protocol so we need to handle redirections manually
request . setAttribute ( QNetworkRequest : : RedirectPolicyAttribute , QNetworkRequest : : ManualRedirectPolicy ) ;
downloadHandler - > assignNetworkReply ( m_networkManager - > get ( request ) ) ;
}
void Net : : DownloadManager : : ignoreSslErrors ( QNetworkReply * reply , const QList < QSslError > & errors )
void Net : : DownloadManager : : ignoreSslErrors ( QNetworkReply * reply , const QList < QSslError > & errors )
{
{
QStringList errorList ;
QStringList errorList ;