| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  | #include "RemoteFile.hpp"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <utility>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <QNetworkAccessManager>
 | 
					
						
							|  |  |  | #include <QNetworkReply>
 | 
					
						
							|  |  |  | #include <QDir>
 | 
					
						
							|  |  |  | #include <QByteArray>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "moc_RemoteFile.cpp"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | RemoteFile::RemoteFile (ListenerInterface * listener, QNetworkAccessManager * network_manager | 
					
						
							| 
									
										
										
										
											2016-12-11 21:19:31 +00:00
										 |  |  |                         , QString const& local_file_path, bool http_only, QObject * parent) | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |   : QObject {parent} | 
					
						
							|  |  |  |   , listener_ {listener} | 
					
						
							|  |  |  |   , network_manager_ {network_manager} | 
					
						
							|  |  |  |   , local_file_ {local_file_path} | 
					
						
							| 
									
										
										
										
											2016-12-11 21:19:31 +00:00
										 |  |  |   , http_only_ {http_only} | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |   , is_valid_ {false} | 
					
						
							|  |  |  |   , redirect_count_ {0} | 
					
						
							|  |  |  |   , file_ {local_file_path} | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   local_file_.setCaching (false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RemoteFile::local_file_path (QString const& name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   QFileInfo new_file {name}; | 
					
						
							|  |  |  |   new_file.setCaching (false); | 
					
						
							|  |  |  |   if (new_file != local_file_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (local_file_.exists ()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           QFile file {local_file_.absoluteFilePath ()}; | 
					
						
							|  |  |  |           if (!file.rename (new_file.absoluteFilePath ())) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |               if (listener_) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   listener_->error (tr ("File System Error") | 
					
						
							|  |  |  |                                     , tr ("Cannot rename file:\n\"%1\"\nto: \"%2\"\nError(%3): %4") | 
					
						
							|  |  |  |                                     .arg (file.fileName ()) | 
					
						
							|  |  |  |                                     .arg (new_file.absoluteFilePath ()) | 
					
						
							|  |  |  |                                     .arg (file.error ()) | 
					
						
							|  |  |  |                                     .arg (file.errorString ())); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       std::swap (local_file_, new_file); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool RemoteFile::local () const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   auto is_local = (reply_ && !reply_->isFinished ()) || local_file_.exists (); | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |   if (listener_) | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |       if (is_local) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           auto size = local_file_.size (); | 
					
						
							|  |  |  |           listener_->download_progress (size, size); | 
					
						
							|  |  |  |           listener_->download_finished (true); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           listener_->download_progress (-1, 0); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   return is_local; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool RemoteFile::sync (QUrl const& url, bool local, bool force) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (local) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (!reply_ || reply_->isFinished ()) // not active download
 | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (force || !local_file_.exists () || url != url_) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               url_ = url; | 
					
						
							|  |  |  |               redirect_count_ = 0; | 
					
						
							|  |  |  |               Q_ASSERT (!is_valid_); | 
					
						
							|  |  |  |               download (url_); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (reply_ && reply_->isRunning ()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           reply_->abort (); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       if (local_file_.exists ()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           auto path = local_file_.absoluteDir (); | 
					
						
							|  |  |  |           if (path.remove (local_file_.fileName ())) | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |               if (listener_) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   listener_->download_progress (-1, 0); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |               if (listener_) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   listener_->error (tr ("File System Error") | 
					
						
							|  |  |  |                                     , tr ("Cannot delete file:\n\"%1\"") | 
					
						
							|  |  |  |                                     .arg (local_file_.absoluteFilePath ())); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |               return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           path.rmpath ("."); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-11 21:19:31 +00:00
										 |  |  | void RemoteFile::download (QUrl url) | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-06-13 16:04:41 +01:00
										 |  |  | #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
 | 
					
						
							| 
									
										
										
										
											2016-02-20 12:55:18 +00:00
										 |  |  |   if (QNetworkAccessManager::Accessible != network_manager_->networkAccessible ()) { | 
					
						
							|  |  |  |     // try and recover network access for QNAM
 | 
					
						
							|  |  |  |     network_manager_->setNetworkAccessible (QNetworkAccessManager::Accessible); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-06-13 16:04:41 +01:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2016-02-20 12:55:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-11 21:19:31 +00:00
										 |  |  |   if (url.isValid () && (!QSslSocket::supportsSsl () || http_only_)) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       url.setScheme ("http"); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |   QNetworkRequest request {url}; | 
					
						
							|  |  |  |   request.setRawHeader ("User-Agent", "WSJT Sample Downloader"); | 
					
						
							|  |  |  |   request.setOriginatingObject (this); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // this blocks for a second or two the first time it is used on
 | 
					
						
							|  |  |  |   // Windows - annoying
 | 
					
						
							|  |  |  |   if (!is_valid_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       reply_ = network_manager_->head (request); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       reply_ = network_manager_->get (request); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-12 18:26:06 +00:00
										 |  |  |   connect (reply_.data (), &QNetworkReply::finished, this, &RemoteFile::reply_finished); | 
					
						
							|  |  |  |   connect (reply_.data (), &QNetworkReply::readyRead, this, &RemoteFile::store); | 
					
						
							|  |  |  |   connect (reply_.data (), &QNetworkReply::downloadProgress | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |            , [this] (qint64 bytes_received, qint64 total_bytes) { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |              if (listener_) | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |                { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |                  // report progress of wanted file
 | 
					
						
							|  |  |  |                  if (is_valid_) | 
					
						
							|  |  |  |                    { | 
					
						
							|  |  |  |                      listener_->download_progress (bytes_received, total_bytes); | 
					
						
							|  |  |  |                    } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |                } | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RemoteFile::abort () | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (reply_ && reply_->isRunning ()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       reply_->abort (); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RemoteFile::reply_finished () | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2016-12-11 21:19:31 +00:00
										 |  |  |   if (!reply_) 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 ()) | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |       if (!listener_ || listener_->redirect_request (redirect_url)) | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |         { | 
					
						
							|  |  |  |           if (++redirect_count_ < 10) // maintain sanity
 | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               // follow redirect
 | 
					
						
							|  |  |  |               download (reply_->url ().resolved (redirect_url)); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |               if (listener_) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   listener_->download_finished (false); | 
					
						
							|  |  |  |                   listener_->error (tr ("Network Error") | 
					
						
							|  |  |  |                                     , tr ("Too many redirects: %1") | 
					
						
							|  |  |  |                                     .arg (redirect_url.toDisplayString ())); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |               is_valid_ = false; // reset
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |           if (listener_) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               listener_->download_finished (false); | 
					
						
							|  |  |  |               listener_->error (tr ("Network Error") | 
					
						
							|  |  |  |                                 , tr ("Redirect not followed: %1") | 
					
						
							|  |  |  |                                 .arg (redirect_url.toDisplayString ())); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |           is_valid_ = false;    // reset
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else if (reply_->error () != QNetworkReply::NoError) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       file_.cancelWriting (); | 
					
						
							|  |  |  |       file_.commit (); | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |       if (listener_) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           listener_->download_finished (false); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |       is_valid_ = false;        // reset
 | 
					
						
							|  |  |  |       // report errors that are not due to abort
 | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |       if (listener_ && QNetworkReply::OperationCanceledError != reply_->error ()) | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |         { | 
					
						
							|  |  |  |           listener_->error (tr ("Network Error"), reply_->errorString ()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       auto path = QFileInfo {file_.fileName ()}.absoluteDir (); | 
					
						
							|  |  |  |       if (is_valid_ && !file_.commit ()) | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |           if (listener_) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               listener_->error (tr ("File System Error") | 
					
						
							|  |  |  |                                 , tr ("Cannot commit changes to:\n\"%1\"") | 
					
						
							|  |  |  |                                 .arg (file_.fileName ())); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |           path.rmpath (".");    // tidy empty directories
 | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |           if (listener_) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               listener_->download_finished (false); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |           is_valid_ = false;    // reset
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           if (!is_valid_) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               // now get the body content
 | 
					
						
							|  |  |  |               is_valid_ = true; | 
					
						
							| 
									
										
										
										
											2016-12-11 21:19:31 +00:00
										 |  |  |               download (reply_->url ().resolved (redirect_url)); | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |               if (listener_) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   listener_->download_finished (true); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |               is_valid_ = false; // reset
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-12-11 21:19:31 +00:00
										 |  |  |   if (reply_ && reply_->isFinished ()) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       reply_->deleteLater (); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void RemoteFile::store () | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   if (is_valid_) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       if (!file_.isOpen ()) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           // create temporary file in the final location
 | 
					
						
							|  |  |  |           auto path = QFileInfo {file_.fileName ()}.absoluteDir (); | 
					
						
							|  |  |  |           if (path.mkpath (".")) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               if (!file_.open (QSaveFile::WriteOnly)) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   abort (); | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |                   if (listener_) | 
					
						
							|  |  |  |                     { | 
					
						
							|  |  |  |                       listener_->error (tr ("File System Error") | 
					
						
							|  |  |  |                                         , tr ("Cannot open file:\n\"%1\"\nError(%2): %3") | 
					
						
							|  |  |  |                                         .arg (path.path ()) | 
					
						
							|  |  |  |                                         .arg (file_.error ()) | 
					
						
							|  |  |  |                                         .arg (file_.errorString ())); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               abort (); | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |               if (listener_) | 
					
						
							|  |  |  |                 { | 
					
						
							|  |  |  |                   listener_->error (tr ("File System Error") | 
					
						
							|  |  |  |                                     , tr ("Cannot make path:\n\"%1\"") | 
					
						
							|  |  |  |                                     .arg (path.path ())); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       if (file_.write (reply_->read (reply_->bytesAvailable ())) < 0) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           abort (); | 
					
						
							| 
									
										
										
										
											2018-10-01 21:19:21 +01:00
										 |  |  |           if (listener_) | 
					
						
							|  |  |  |             { | 
					
						
							|  |  |  |               listener_->error (tr ("File System Error") | 
					
						
							|  |  |  |                                 , tr ("Cannot write to file:\n\"%1\"\nError(%2): %3") | 
					
						
							|  |  |  |                                 .arg (file_.fileName ()) | 
					
						
							|  |  |  |                                 .arg (file_.error ()) | 
					
						
							|  |  |  |                                 .arg (file_.errorString ())); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2015-12-24 11:41:05 +00:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |