2023-03-09 12:10:20 -08:00
#include "FileDownload.hpp"
#include <QCoreApplication>
#include <QUrl>
#include <QNetworkRequest>
2023-03-15 20:42:03 -07:00
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
2023-03-09 12:10:20 -08:00
#include <QFileInfo>
#include <QDir>
2023-03-16 20:49:08 -07:00
#include <QIODevice>
2023-03-09 12:10:20 -08:00
#include "qt_helpers.hpp"
#include "Logger.hpp"
FileDownload :: FileDownload () : QObject ( nullptr )
{
2023-03-15 20:42:03 -07:00
redirect_count_ = 0 ;
url_valid_ = false ;
2023-03-09 12:10:20 -08:00
}
FileDownload ::~ FileDownload ()
{
}
2023-03-17 07:52:03 -07:00
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2023-03-09 12:10:20 -08:00
void FileDownload :: errorOccurred ( QNetworkReply :: NetworkError code )
{
2023-03-15 20:42:03 -07:00
LOG_INFO ( QString { "FileDownload [%1]: errorOccurred %2 -> %3" }. arg ( user_agent_ ). arg ( code ). arg ( reply_ -> errorString ()));
Q_EMIT error ( reply_ -> errorString ());
2023-03-16 19:13:59 -07:00
destfile_ . cancelWriting ();
destfile_ . commit ();
2023-03-09 12:10:20 -08:00
}
2023-03-17 07:52:03 -07:00
#else
void FileDownload :: obsoleteError ()
{
LOG_INFO ( QString { "FileDownload [%1]: error -> %3" }. arg ( user_agent_ ). arg ( reply_ -> errorString ()));
Q_EMIT error ( reply_ -> errorString ());
destfile_ . cancelWriting ();
destfile_ . commit ();
}
#endif
2023-03-09 12:10:20 -08:00
2023-03-15 20:42:03 -07:00
void FileDownload :: configure ( QNetworkAccessManager * network_manager , const QString & source_url , const QString & destination_path , const QString & user_agent )
2023-03-09 12:10:20 -08:00
{
2023-03-15 20:42:03 -07:00
manager_ = network_manager ;
2023-03-09 12:10:20 -08:00
source_url_ = source_url ;
destination_filename_ = destination_path ;
2023-03-15 20:42:03 -07:00
user_agent_ = user_agent ;
2023-03-09 12:10:20 -08:00
}
void FileDownload :: store ()
{
2023-03-16 19:13:59 -07:00
if ( destfile_ . isOpen ())
destfile_ . write ( reply_ -> read ( reply_ -> bytesAvailable ()));
2023-03-09 12:10:20 -08:00
else
2023-03-16 19:13:59 -07:00
LOG_INFO ( QString { "FileDownload [%1]: file is not open." }. arg ( user_agent_ ));
2023-03-09 12:10:20 -08:00
}
void FileDownload :: replyComplete ()
{
2023-03-15 20:42:03 -07:00
QFileInfo destination_file ( destination_filename_ );
2023-03-16 19:13:59 -07:00
QDir tmpdir_ ( destination_file . absoluteFilePath ());
2023-03-15 20:42:03 -07:00
LOG_DEBUG ( QString { "FileDownload [%1]: replyComplete" }. arg ( user_agent_ ));
if ( ! reply_ )
{
Q_EMIT load_finished ();
return ; // we probably deleted it in an earlier call
}
QUrl redirect_url { reply_ -> attribute ( QNetworkRequest :: RedirectionTargetAttribute ). toUrl ()};
if ( reply_ -> error () == QNetworkReply :: NoError && ! redirect_url . isEmpty ())
{
if ( "https" == redirect_url . scheme () && ! QSslSocket :: supportsSsl ())
{
Q_EMIT download_error ( tr ( "Network Error - SSL/TLS support not installed, cannot fetch: \n\' %1 \' " )
. arg ( redirect_url . toDisplayString ()));
url_valid_ = false ; // reset
Q_EMIT load_finished ();
}
else if ( ++ redirect_count_ < 10 ) // maintain sanity
{
// follow redirect
download ( reply_ -> url (). resolved ( redirect_url ));
}
else
{
Q_EMIT download_error ( tr ( "Network Error - Too many redirects: \n\' %1 \' " )
. arg ( redirect_url . toDisplayString ()));
url_valid_ = false ; // reset
Q_EMIT load_finished ();
}
}
else if ( reply_ -> error () != QNetworkReply :: NoError )
{
2023-03-16 19:13:59 -07:00
destfile_ . cancelWriting ();
destfile_ . commit ();
2023-03-15 20:42:03 -07:00
url_valid_ = false ; // reset
// report errors that are not due to abort
if ( QNetworkReply :: OperationCanceledError != reply_ -> error ())
{
Q_EMIT download_error ( tr ( "Network Error: \n %1" )
. arg ( reply_ -> errorString ()));
}
Q_EMIT load_finished ();
}
else
{
if ( ! url_valid_ )
{
// now get the body content
url_valid_ = true ;
download ( reply_ -> url (). resolved ( redirect_url ));
}
else // the body has completed. Save it.
{
url_valid_ = false ; // reset
// load the database asynchronously
// future_load_ = std::async (std::launch::async, &LotWUsers::impl::load_dictionary, this, csv_file_.fileName ());
2023-03-16 19:13:59 -07:00
LOG_INFO ( QString { "FileDownload [%1]: complete. File path is %2" }. arg ( user_agent_ ). arg ( destfile_ . fileName ()));
destfile_ . commit ();
2023-03-15 20:42:03 -07:00
emit complete ( destination_filename_ );
}
}
2023-03-09 12:10:20 -08:00
if ( reply_ && reply_ -> isFinished ())
{
reply_ -> deleteLater ();
}
2023-03-15 20:42:03 -07:00
2023-03-09 12:10:20 -08:00
}
void FileDownload :: downloadComplete ( QNetworkReply * data )
{
// make a temp file in the same place as the file we're downloading. Needs to be on the same
// filesystem as where we eventually want to 'mv' it.
2023-03-15 20:42:03 -07:00
QUrl r = request_ . url ();
LOG_INFO ( QString { "FileDownload [%1]: finished %2 of %3 -> %4 (%5)" }. arg ( user_agent_ ). arg ( data -> operation ()). arg ( source_url_ ). arg ( destination_filename_ ). arg ( r . url ()));
2023-03-09 12:10:20 -08:00
2023-03-15 20:42:03 -07:00
#ifdef DEBUG_FILEDOWNLOAD
2023-03-09 12:10:20 -08:00
LOG_INFO ( "Request Headers:" );
2023-03-15 20:42:03 -07:00
Q_FOREACH ( const QByteArray & hdr , request_ . rawHeaderList ()) {
LOG_INFO ( QString { "%1 -> %2" }. arg ( QString ( hdr )). arg ( QString ( request_ . rawHeader ( hdr ))));
}
2023-03-09 12:10:20 -08:00
LOG_INFO ( "Response Headers:" );
Q_FOREACH ( const QByteArray & hdr , reply_ -> rawHeaderList ()) {
LOG_INFO ( QString { "%1 -> %2" }. arg ( QString ( hdr )). arg ( QString ( reply_ -> rawHeader ( hdr ))));
}
2023-03-15 20:42:03 -07:00
#endif
2023-03-09 12:10:20 -08:00
data -> deleteLater ();
}
2023-03-15 20:42:03 -07:00
void FileDownload :: start_download ()
2023-03-09 12:10:20 -08:00
{
2023-03-15 20:42:03 -07:00
url_valid_ = false ;
download ( QUrl ( source_url_ ));
}
2023-03-09 12:10:20 -08:00
2023-03-15 20:42:03 -07:00
void FileDownload :: download ( QUrl qurl )
{
request_ . setUrl ( qurl );
2023-03-09 12:10:20 -08:00
2023-03-15 20:42:03 -07:00
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
if ( QNetworkAccessManager :: Accessible != manager_ -> networkAccessible ())
{
// try and recover network access for QNAM
manager_ -> setNetworkAccessible ( QNetworkAccessManager :: Accessible );
}
#endif
2023-03-09 12:10:20 -08:00
2023-03-15 20:42:03 -07:00
LOG_INFO ( QString { "FileDownload [%1]: Starting download of %2 to %3" }. arg ( user_agent_ ). arg ( source_url_ ). arg ( destination_filename_ ));
2023-03-09 12:10:20 -08:00
2023-03-15 20:42:03 -07:00
request_ . setAttribute ( QNetworkRequest :: FollowRedirectsAttribute , true );
request_ . setRawHeader ( "Accept" , "*/*" );
request_ . setRawHeader ( "User-Agent" , user_agent_ . toLocal8Bit ()); // Must have a UA for some sites, like country-files
2023-03-09 12:10:20 -08:00
2023-03-15 20:42:03 -07:00
if ( ! url_valid_ )
{
reply_ = manager_ -> head ( request_ );
}
else
{
reply_ = manager_ -> get ( request_ );
}
2023-03-09 12:10:20 -08:00
2023-03-15 20:42:03 -07:00
QObject :: connect ( manager_ , & QNetworkAccessManager :: finished , this , & FileDownload :: downloadComplete , Qt :: UniqueConnection );
QObject :: connect ( reply_ , & QNetworkReply :: downloadProgress , this , & FileDownload :: downloadProgress , Qt :: UniqueConnection );
2023-03-17 12:00:05 +01:00
QObject :: connect ( reply_ , & QNetworkReply :: finished , this , & FileDownload :: replyComplete , Qt :: UniqueConnection );
2023-03-16 19:13:59 -07:00
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
2023-03-17 12:00:05 +01:00
QObject :: connect ( reply_ , & QNetworkReply :: errorOccurred , this , & FileDownload :: errorOccurred , Qt :: UniqueConnection );
2023-03-16 19:13:59 -07:00
#else
2023-03-17 07:52:03 -07:00
QObject :: connect ( reply_ , QOverload < QNetworkReply :: NetworkError >:: of ( & QNetworkReply :: error ), this , & FileDownload :: obsoleteError , Qt :: UniqueConnection );
2023-03-16 19:13:59 -07:00
#endif
2023-03-17 12:00:05 +01:00
QObject :: connect ( reply_ , & QNetworkReply :: readyRead , this , & FileDownload :: store , Qt :: UniqueConnection );
2023-03-09 12:10:20 -08:00
2023-03-15 20:42:03 -07:00
QFileInfo destination_file ( destination_filename_ );
QString const tmpfile_base = destination_file . fileName ();
2023-03-16 20:49:08 -07:00
QString const & tmpfile_path = destination_file . absolutePath ();
QDir tmpdir {};
if ( ! tmpdir . mkpath ( tmpfile_path ))
2023-03-09 12:10:20 -08:00
{
2023-03-16 20:49:08 -07:00
LOG_INFO ( QString { "FileDownload [%1]: Directory %2 does not exist" }. arg ( user_agent_ ). arg ( tmpfile_path ). arg (
destfile_ . errorString ()));
}
if ( url_valid_ ) {
destfile_ . setFileName ( destination_file . absoluteFilePath ());
if ( ! destfile_ . open ( QSaveFile :: WriteOnly | QIODevice :: WriteOnly )) {
LOG_INFO ( QString { "FileDownload [%1]: Unable to open %2: %3" }. arg ( user_agent_ ). arg ( destfile_ . fileName ()). arg (
destfile_ . errorString ()));
return ;
}
2023-03-09 12:10:20 -08:00
}
}
void FileDownload :: downloadProgress ( qint64 received , qint64 total )
{
2023-03-15 20:42:03 -07:00
LOG_DEBUG ( QString { "FileDownload: [%1] Progress %2 from %3, total %4, so far %5" }. arg ( user_agent_ ). arg ( destination_filename_ ). arg ( source_url_ ). arg ( total ). arg ( received ));
Q_EMIT progress ( QString { "%4 bytes downloaded" }. arg ( received ));
2023-03-09 12:10:20 -08:00
}
2023-03-15 20:42:03 -07:00
void FileDownload :: abort ()
{
if ( reply_ && reply_ -> isRunning ())
{
reply_ -> abort ();
}
2023-03-17 12:00:05 +01:00
}