mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-30 12:30:23 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			230 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #include "FileDownload.hpp"
 | |
| #include <QCoreApplication>
 | |
| #include <QUrl>
 | |
| #include <QNetworkRequest>
 | |
| #include <QtNetwork/QNetworkAccessManager>
 | |
| #include <QtNetwork/QNetworkReply>
 | |
| #include <QFileInfo>
 | |
| #include <QDir>
 | |
| #include <QIODevice>
 | |
| #include "qt_helpers.hpp"
 | |
| #include "Logger.hpp"
 | |
| 
 | |
| FileDownload::FileDownload() : QObject(nullptr)
 | |
| {
 | |
|   redirect_count_ = 0;
 | |
|   url_valid_ = false;
 | |
| }
 | |
| 
 | |
| FileDownload::~FileDownload()
 | |
| {
 | |
| }
 | |
| #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
 | |
| void FileDownload::errorOccurred(QNetworkReply::NetworkError code)
 | |
| {
 | |
|   LOG_INFO(QString{"FileDownload [%1]: errorOccurred %2 -> %3"}.arg(user_agent_).arg(code).arg(reply_->errorString()));
 | |
|   Q_EMIT error (reply_->errorString ());
 | |
|   destfile_.cancelWriting ();
 | |
|   destfile_.commit ();
 | |
| }
 | |
| #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
 | |
| 
 | |
| void FileDownload::configure(QNetworkAccessManager *network_manager, const QString &source_url, const QString &destination_path, const QString &user_agent)
 | |
| {
 | |
|   manager_ = network_manager;
 | |
|   source_url_ = source_url;
 | |
|   destination_filename_ = destination_path;
 | |
|   user_agent_ = user_agent;
 | |
| }
 | |
| 
 | |
| void FileDownload::store()
 | |
| {
 | |
|   if (destfile_.isOpen())
 | |
|     destfile_.write (reply_->read (reply_->bytesAvailable ()));
 | |
|   else
 | |
|     LOG_INFO(QString{ "FileDownload [%1]: file is not open."}.arg(user_agent_));
 | |
| }
 | |
| 
 | |
| void FileDownload::replyComplete()
 | |
| {
 | |
|   QFileInfo destination_file(destination_filename_);
 | |
|   QDir tmpdir_(destination_file.absoluteFilePath());
 | |
| 
 | |
|   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)
 | |
|   {
 | |
|     destfile_.cancelWriting();
 | |
|     destfile_.commit();
 | |
|     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 ());
 | |
|         LOG_INFO(QString{ "FileDownload [%1]: complete. File path is %2"}.arg(user_agent_).arg(destfile_.fileName()));
 | |
|         destfile_.commit();
 | |
|         emit complete(destination_filename_);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   if (reply_ && reply_->isFinished ())
 | |
|   {
 | |
|     reply_->deleteLater ();
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| 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.
 | |
| 
 | |
|   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()));
 | |
| 
 | |
| #ifdef DEBUG_FILEDOWNLOAD
 | |
|   LOG_INFO("Request Headers:");
 | |
|   Q_FOREACH (const QByteArray& hdr, request_.rawHeaderList()) {
 | |
|       LOG_INFO(QString{ "%1 -> %2"}.arg(QString(hdr)).arg(QString(request_.rawHeader(hdr))));
 | |
|   }
 | |
| 
 | |
|   LOG_INFO("Response Headers:");
 | |
|   Q_FOREACH (const QByteArray& hdr, reply_->rawHeaderList()) {
 | |
|       LOG_INFO(QString{ "%1 -> %2"}.arg(QString(hdr)).arg(QString(reply_->rawHeader(hdr))));
 | |
|   }
 | |
| #endif
 | |
|   data->deleteLater();
 | |
| }
 | |
| 
 | |
| void FileDownload::start_download()
 | |
| {
 | |
|   url_valid_ = false;
 | |
|   download(QUrl(source_url_));
 | |
| }
 | |
| 
 | |
| void FileDownload::download(QUrl qurl)
 | |
| {
 | |
|   request_.setUrl(qurl);
 | |
| 
 | |
| #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
 | |
| 
 | |
|   LOG_INFO(QString{"FileDownload [%1]: Starting download of %2 to %3"}.arg(user_agent_).arg(source_url_).arg(destination_filename_));
 | |
| 
 | |
|   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
 | |
| 
 | |
|   if (!url_valid_)
 | |
|   {
 | |
|     reply_ = manager_->head(request_);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     reply_ = manager_->get (request_);
 | |
|   }
 | |
| 
 | |
|   QObject::connect(manager_, &QNetworkAccessManager::finished, this, &FileDownload::downloadComplete, Qt::UniqueConnection);
 | |
|   QObject::connect(reply_, &QNetworkReply::downloadProgress, this, &FileDownload::downloadProgress, Qt::UniqueConnection);
 | |
|   QObject::connect(reply_, &QNetworkReply::finished, this, &FileDownload::replyComplete, Qt::UniqueConnection);
 | |
| #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
 | |
|   QObject::connect(reply_, &QNetworkReply::errorOccurred,this, &FileDownload::errorOccurred, Qt::UniqueConnection);
 | |
| #else
 | |
|   QObject::connect(reply_, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::error), this, &FileDownload::obsoleteError, Qt::UniqueConnection);
 | |
| #endif
 | |
|   QObject::connect(reply_, &QNetworkReply::readyRead, this, &FileDownload::store, Qt::UniqueConnection);
 | |
| 
 | |
|   QFileInfo destination_file(destination_filename_);
 | |
|   QString const tmpfile_base = destination_file.fileName();
 | |
|   QString const &tmpfile_path = destination_file.absolutePath();
 | |
|   QDir tmpdir{};
 | |
|   if (!tmpdir.mkpath(tmpfile_path))
 | |
|   {
 | |
|       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;
 | |
|       }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void FileDownload::downloadProgress(qint64 received, qint64 total)
 | |
| {
 | |
|   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));
 | |
| }
 | |
| 
 | |
| void FileDownload::abort ()
 | |
| {
 | |
|   if (reply_ && reply_->isRunning ())
 | |
|   {
 | |
|     reply_->abort ();
 | |
|   }
 | |
| }
 |