mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-15 08:31:57 -05:00
270 lines
7.9 KiB
C++
270 lines
7.9 KiB
C++
|
#include "RemoteFile.hpp"
|
||
|
|
||
|
#include <utility>
|
||
|
|
||
|
#include <QNetworkAccessManager>
|
||
|
#include <QNetworkReply>
|
||
|
#include <QDir>
|
||
|
#include <QByteArray>
|
||
|
|
||
|
#include "moc_RemoteFile.cpp"
|
||
|
|
||
|
RemoteFile::RemoteFile (ListenerInterface * listener, QNetworkAccessManager * network_manager
|
||
|
, QString const& local_file_path, QObject * parent)
|
||
|
: QObject {parent}
|
||
|
, listener_ {listener}
|
||
|
, network_manager_ {network_manager}
|
||
|
, local_file_ {local_file_path}
|
||
|
, reply_ {nullptr}
|
||
|
, 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 ()))
|
||
|
{
|
||
|
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 ()));
|
||
|
}
|
||
|
}
|
||
|
std::swap (local_file_, new_file);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool RemoteFile::local () const
|
||
|
{
|
||
|
auto is_local = (reply_ && !reply_->isFinished ()) || local_file_.exists ();
|
||
|
if (is_local)
|
||
|
{
|
||
|
auto size = local_file_.size ();
|
||
|
listener_->download_progress (size, size);
|
||
|
listener_->download_finished (true);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
listener_->download_progress (-1, 0);
|
||
|
}
|
||
|
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 ()))
|
||
|
{
|
||
|
listener_->download_progress (-1, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
listener_->error (tr ("File System Error")
|
||
|
, tr ("Cannot delete file:\n\"%1\"")
|
||
|
.arg (local_file_.absoluteFilePath ()));
|
||
|
return false;
|
||
|
}
|
||
|
path.rmpath (".");
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void RemoteFile::download (QUrl const& url)
|
||
|
{
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
connect (reply_, &QNetworkReply::finished, this, &RemoteFile::reply_finished);
|
||
|
connect (reply_, &QNetworkReply::readyRead, this, &RemoteFile::store);
|
||
|
connect (reply_, &QNetworkReply::downloadProgress
|
||
|
, [this] (qint64 bytes_received, qint64 total_bytes) {
|
||
|
// report progress of wanted file
|
||
|
if (is_valid_)
|
||
|
{
|
||
|
listener_->download_progress (bytes_received, total_bytes);
|
||
|
}
|
||
|
});
|
||
|
connect (reply_, &QNetworkReply::sslErrors, [this] (QList<QSslError> const& errors) {
|
||
|
QString message;
|
||
|
for (auto const& error: errors)
|
||
|
{
|
||
|
message += '\n' + reply_->request ().url ().toDisplayString () + ": "
|
||
|
+ error.errorString ();
|
||
|
}
|
||
|
listener_->error ("Network SSL Errors", message);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
void RemoteFile::abort ()
|
||
|
{
|
||
|
if (reply_ && reply_->isRunning ())
|
||
|
{
|
||
|
reply_->abort ();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RemoteFile::reply_finished ()
|
||
|
{
|
||
|
auto saved_reply = reply_;
|
||
|
auto redirect_url = reply_->attribute (QNetworkRequest::RedirectionTargetAttribute).toUrl ();
|
||
|
if (!redirect_url.isEmpty ())
|
||
|
{
|
||
|
if (listener_->redirect_request (redirect_url))
|
||
|
{
|
||
|
if (++redirect_count_ < 10) // maintain sanity
|
||
|
{
|
||
|
// follow redirect
|
||
|
download (reply_->url ().resolved (redirect_url));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
listener_->download_finished (false);
|
||
|
listener_->error (tr ("Network Error")
|
||
|
, tr ("Too many redirects: %1")
|
||
|
.arg (redirect_url.toDisplayString ()));
|
||
|
is_valid_ = false; // reset
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
listener_->download_finished (false);
|
||
|
listener_->error (tr ("Network Error")
|
||
|
, tr ("Redirect not followed: %1")
|
||
|
.arg (redirect_url.toDisplayString ()));
|
||
|
is_valid_ = false; // reset
|
||
|
}
|
||
|
}
|
||
|
else if (reply_->error () != QNetworkReply::NoError)
|
||
|
{
|
||
|
file_.cancelWriting ();
|
||
|
file_.commit ();
|
||
|
listener_->download_finished (false);
|
||
|
is_valid_ = false; // reset
|
||
|
// report errors that are not due to abort
|
||
|
if (QNetworkReply::OperationCanceledError != reply_->error ())
|
||
|
{
|
||
|
listener_->error (tr ("Network Error"), reply_->errorString ());
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
auto path = QFileInfo {file_.fileName ()}.absoluteDir ();
|
||
|
if (is_valid_ && !file_.commit ())
|
||
|
{
|
||
|
listener_->error (tr ("File System Error")
|
||
|
, tr ("Cannot commit changes to:\n\"%1\"")
|
||
|
.arg (file_.fileName ()));
|
||
|
path.rmpath ("."); // tidy empty directories
|
||
|
listener_->download_finished (false);
|
||
|
is_valid_ = false; // reset
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!is_valid_)
|
||
|
{
|
||
|
// now get the body content
|
||
|
is_valid_ = true;
|
||
|
download (reply_->url () .resolved (redirect_url));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
listener_->download_finished (true);
|
||
|
is_valid_ = false; // reset
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (reply_->isFinished ()) reply_ = nullptr;
|
||
|
disconnect (saved_reply);
|
||
|
saved_reply->deleteLater (); // finished with QNetworkReply
|
||
|
}
|
||
|
|
||
|
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 ();
|
||
|
listener_->error (tr ("File System Error")
|
||
|
, tr ("Cannot open file:\n\"%1\"\nError(%2): %3")
|
||
|
.arg (path.path ())
|
||
|
.arg (file_.error ())
|
||
|
.arg (file_.errorString ()));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
abort ();
|
||
|
listener_->error (tr ("File System Error")
|
||
|
, tr ("Cannot make path:\n\"%1\"")
|
||
|
.arg (path.path ()));
|
||
|
}
|
||
|
}
|
||
|
if (file_.write (reply_->read (reply_->bytesAvailable ())) < 0)
|
||
|
{
|
||
|
abort ();
|
||
|
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 ()));
|
||
|
}
|
||
|
}
|
||
|
}
|