mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-11-03 21:40:52 -05:00 
			
		
		
		
	QNetworkAccessManager which is used for all network traffic does not automatically recover from network outages. This patch tries to reestablish network accessibility before any requests are attempted. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@6471 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
		
			
				
	
	
		
			275 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			8.1 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)
 | 
						|
{
 | 
						|
  if (QNetworkAccessManager::Accessible != network_manager_->networkAccessible ()) {
 | 
						|
    // try and recover network access for QNAM
 | 
						|
    network_manager_->setNetworkAccessible (QNetworkAccessManager::Accessible);
 | 
						|
  }
 | 
						|
 | 
						|
  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 ()));
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |