annotate data/fileio/RemoteFile.cpp @ 210:a06afefe45ee

* Cancel when downloading file * Handle status codes (404 etc) * Add RemoteFile::isAvailable * Start on FileFinder for looking up files referred to in distant sessions
author Chris Cannam
date Wed, 10 Jan 2007 17:26:39 +0000
parents 8a3d68910b37
children e2bbb58e6df6
rev   line source
Chris@208 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@208 2
Chris@208 3 /*
Chris@208 4 Sonic Visualiser
Chris@208 5 An audio file viewer and annotation editor.
Chris@208 6 Centre for Digital Music, Queen Mary, University of London.
Chris@208 7 This file copyright 2007 QMUL.
Chris@208 8
Chris@208 9 This program is free software; you can redistribute it and/or
Chris@208 10 modify it under the terms of the GNU General Public License as
Chris@208 11 published by the Free Software Foundation; either version 2 of the
Chris@208 12 License, or (at your option) any later version. See the file
Chris@208 13 COPYING included with this distribution for more information.
Chris@208 14 */
Chris@208 15
Chris@208 16 #include "RemoteFile.h"
Chris@208 17 #include "base/TempDirectory.h"
Chris@208 18 #include "base/Exceptions.h"
Chris@208 19
Chris@208 20 #include <QHttp>
Chris@208 21 #include <QFtp>
Chris@208 22 #include <QFileInfo>
Chris@208 23 #include <QDir>
Chris@208 24 #include <QApplication>
Chris@208 25 #include <QProgressDialog>
Chris@210 26 #include <QHttpResponseHeader>
Chris@208 27
Chris@208 28 #include <iostream>
Chris@208 29
Chris@208 30 int
Chris@208 31 RemoteFile::m_count = 0;
Chris@208 32
Chris@208 33 QMutex
Chris@208 34 RemoteFile::m_fileCreationMutex;
Chris@208 35
Chris@208 36 RemoteFile::RemoteFile(QUrl url) :
Chris@208 37 m_ftp(0),
Chris@208 38 m_http(0),
Chris@208 39 m_localFile(0),
Chris@208 40 m_ok(false),
Chris@210 41 m_lastStatus(0),
Chris@208 42 m_done(false),
Chris@210 43 m_progressDialog(0),
Chris@210 44 m_progressShowTimer(this)
Chris@208 45 {
Chris@208 46 if (!canHandleScheme(url)) {
Chris@208 47 std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << url.toString().toStdString() << "\"" << std::endl;
Chris@208 48 return;
Chris@208 49 }
Chris@208 50
Chris@208 51 m_localFilename = createLocalFile(url);
Chris@208 52 if (m_localFilename == "") return;
Chris@208 53 m_localFile = new QFile(m_localFilename);
Chris@208 54 m_localFile->open(QFile::WriteOnly);
Chris@208 55
Chris@208 56 QString scheme = url.scheme().toLower();
Chris@208 57
Chris@208 58 if (scheme == "http") {
Chris@208 59
Chris@208 60 m_http = new QHttp(url.host(), url.port(80));
Chris@208 61 connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool)));
Chris@208 62 connect(m_http, SIGNAL(dataReadProgress(int, int)),
Chris@208 63 this, SLOT(dataReadProgress(int, int)));
Chris@210 64 connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
Chris@210 65 this, SLOT(responseHeaderReceived(const QHttpResponseHeader &)));
Chris@208 66 m_http->get(url.path(), m_localFile);
Chris@208 67 m_ok = true;
Chris@208 68
Chris@208 69 } else if (scheme == "ftp") {
Chris@208 70
Chris@208 71 m_ftp = new QFtp;
Chris@208 72 connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool)));
Chris@208 73 connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
Chris@208 74 this, SLOT(dataTransferProgress(qint64, qint64)));
Chris@208 75 m_ftp->connectToHost(url.host(), url.port(21));
Chris@208 76
Chris@208 77 QString username = url.userName();
Chris@208 78 if (username == "") {
Chris@208 79 username = "anonymous";
Chris@208 80 }
Chris@208 81
Chris@208 82 QString password = url.password();
Chris@208 83 if (password == "") {
Chris@208 84 password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
Chris@208 85 }
Chris@208 86
Chris@208 87 QStringList path = url.path().split('/');
Chris@208 88 for (QStringList::iterator i = path.begin(); i != path.end(); ) {
Chris@208 89 QString bit = *i;
Chris@208 90 ++i;
Chris@208 91 if (i != path.end()) {
Chris@208 92 m_ftp->cd(*i);
Chris@208 93 } else {
Chris@208 94 m_ftp->get(*i, m_localFile);
Chris@208 95 }
Chris@208 96 }
Chris@208 97
Chris@208 98 m_ok = true;
Chris@208 99 }
Chris@208 100
Chris@208 101 if (m_ok) {
Chris@208 102 m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(url.toString()), tr("Cancel"), 0, 100);
Chris@210 103 m_progressDialog->hide();
Chris@210 104 connect(&m_progressShowTimer, SIGNAL(timeout()),
Chris@210 105 this, SLOT(showProgressDialog()));
Chris@210 106 connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled()));
Chris@210 107 m_progressShowTimer.setSingleShot(true);
Chris@210 108 m_progressShowTimer.start(2000);
Chris@208 109 }
Chris@208 110 }
Chris@208 111
Chris@208 112 RemoteFile::~RemoteFile()
Chris@208 113 {
Chris@208 114 delete m_ftp;
Chris@208 115 delete m_http;
Chris@208 116 delete m_localFile;
Chris@208 117 delete m_progressDialog;
Chris@208 118 }
Chris@208 119
Chris@208 120 bool
Chris@208 121 RemoteFile::canHandleScheme(QUrl url)
Chris@208 122 {
Chris@208 123 QString scheme = url.scheme().toLower();
Chris@208 124 return (scheme == "http" || scheme == "ftp");
Chris@208 125 }
Chris@208 126
Chris@210 127 bool
Chris@210 128 RemoteFile::isAvailable()
Chris@210 129 {
Chris@210 130 while (!m_done && m_lastStatus == 0) {
Chris@210 131 QApplication::processEvents();
Chris@210 132 }
Chris@210 133 return (m_lastStatus / 100 == 2);
Chris@210 134 }
Chris@210 135
Chris@208 136 void
Chris@208 137 RemoteFile::wait()
Chris@208 138 {
Chris@208 139 while (!m_done) {
Chris@208 140 QApplication::processEvents();
Chris@208 141 }
Chris@208 142 }
Chris@208 143
Chris@208 144 bool
Chris@208 145 RemoteFile::isOK() const
Chris@208 146 {
Chris@208 147 return m_ok;
Chris@208 148 }
Chris@208 149
Chris@208 150 bool
Chris@208 151 RemoteFile::isDone() const
Chris@208 152 {
Chris@208 153 return m_done;
Chris@208 154 }
Chris@208 155
Chris@208 156 QString
Chris@208 157 RemoteFile::getLocalFilename() const
Chris@208 158 {
Chris@208 159 return m_localFilename;
Chris@208 160 }
Chris@208 161
Chris@208 162 QString
Chris@208 163 RemoteFile::getErrorString() const
Chris@208 164 {
Chris@208 165 return m_errorString;
Chris@208 166 }
Chris@208 167
Chris@208 168 void
Chris@208 169 RemoteFile::dataReadProgress(int done, int total)
Chris@208 170 {
Chris@208 171 dataTransferProgress(done, total);
Chris@208 172 }
Chris@208 173
Chris@208 174 void
Chris@210 175 RemoteFile::responseHeaderReceived(const QHttpResponseHeader &resp)
Chris@210 176 {
Chris@210 177 m_lastStatus = resp.statusCode();
Chris@210 178 if (m_lastStatus / 100 >= 4) {
Chris@210 179 m_errorString = QString("%1 %2")
Chris@210 180 .arg(resp.statusCode()).arg(resp.reasonPhrase());
Chris@210 181 }
Chris@210 182 }
Chris@210 183
Chris@210 184 void
Chris@208 185 RemoteFile::dataTransferProgress(qint64 done, qint64 total)
Chris@208 186 {
Chris@208 187 int percent = int((double(done) / double(total)) * 100.0 - 0.1);
Chris@208 188 emit progress(percent);
Chris@208 189
Chris@208 190 m_progressDialog->setValue(percent);
Chris@210 191 m_progressDialog->show();
Chris@210 192 }
Chris@210 193
Chris@210 194 void
Chris@210 195 RemoteFile::cancelled()
Chris@210 196 {
Chris@210 197 delete m_http;
Chris@210 198 m_http = 0;
Chris@210 199 delete m_ftp;
Chris@210 200 m_ftp = 0;
Chris@210 201 delete m_progressDialog;
Chris@210 202 m_progressDialog = 0;
Chris@210 203 delete m_localFile;
Chris@210 204 m_localFile = 0;
Chris@210 205 m_done = true;
Chris@210 206 m_ok = false;
Chris@210 207 m_errorString = tr("Download cancelled");
Chris@208 208 }
Chris@208 209
Chris@208 210 void
Chris@208 211 RemoteFile::done(bool error)
Chris@208 212 {
Chris@208 213 emit progress(100);
Chris@208 214 m_ok = !error;
Chris@210 215
Chris@208 216 if (error) {
Chris@208 217 if (m_http) {
Chris@208 218 m_errorString = m_http->errorString();
Chris@208 219 } else if (m_ftp) {
Chris@208 220 m_errorString = m_ftp->errorString();
Chris@208 221 }
Chris@208 222 }
Chris@208 223
Chris@210 224 if (m_lastStatus / 100 >= 4) {
Chris@210 225 m_ok = false;
Chris@210 226 }
Chris@210 227
Chris@208 228 delete m_localFile;
Chris@208 229 m_localFile = 0;
Chris@208 230
Chris@208 231 delete m_progressDialog;
Chris@208 232 m_progressDialog = 0;
Chris@208 233
Chris@208 234 if (m_ok) {
Chris@208 235 QFileInfo fi(m_localFilename);
Chris@208 236 if (!fi.exists()) {
Chris@208 237 m_errorString = tr("Failed to create local file %1").arg(m_localFilename);
Chris@208 238 m_ok = false;
Chris@208 239 } else if (fi.size() == 0) {
Chris@208 240 m_errorString = tr("File contains no data!");
Chris@208 241 m_ok = false;
Chris@208 242 }
Chris@208 243 }
Chris@208 244 m_done = true;
Chris@208 245 }
Chris@208 246
Chris@210 247 void
Chris@210 248 RemoteFile::showProgressDialog()
Chris@210 249 {
Chris@210 250 if (m_progressDialog) m_progressDialog->show();
Chris@210 251 }
Chris@210 252
Chris@208 253 QString
Chris@208 254 RemoteFile::createLocalFile(QUrl url)
Chris@208 255 {
Chris@208 256 //!!! should we actually put up dialogs for these errors? or propagate an exception?
Chris@208 257
Chris@208 258 QDir dir;
Chris@208 259 try {
Chris@208 260 dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
Chris@208 261 } catch (DirectoryCreationFailed f) {
Chris@208 262 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
Chris@208 263 return "";
Chris@208 264 }
Chris@208 265
Chris@208 266 QString filepart = url.path().section('/', -1, -1,
Chris@208 267 QString::SectionSkipEmpty);
Chris@208 268
Chris@208 269 QString extension = filepart.section('.', -1);
Chris@208 270 QString base = filepart;
Chris@208 271 if (extension != "") {
Chris@208 272 base = base.left(base.length() - extension.length() - 1);
Chris@208 273 }
Chris@208 274 if (base == "") base = "remote";
Chris@208 275
Chris@208 276 QString filename;
Chris@208 277
Chris@208 278 if (extension == "") {
Chris@208 279 filename = base;
Chris@208 280 } else {
Chris@208 281 filename = QString("%1.%2").arg(base).arg(extension);
Chris@208 282 }
Chris@208 283
Chris@208 284 QString filepath(dir.filePath(filename));
Chris@208 285
Chris@208 286 std::cerr << "RemoteFile::createLocalFile: URL is \"" << url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl;
Chris@208 287
Chris@208 288 m_fileCreationMutex.lock();
Chris@208 289 ++m_count;
Chris@208 290
Chris@208 291 if (QFileInfo(filepath).exists() ||
Chris@208 292 !QFile(filepath).open(QFile::WriteOnly)) {
Chris@208 293
Chris@208 294 std::cerr << "RemoteFile::createLocalFile: Failed to create local file \""
Chris@208 295 << filepath.toStdString() << "\" for URL \""
Chris@208 296 << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
Chris@208 297
Chris@208 298
Chris@208 299 if (extension == "") {
Chris@208 300 filename = QString("%1_%2").arg(base).arg(m_count);
Chris@208 301 } else {
Chris@208 302 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
Chris@208 303 }
Chris@208 304 filepath = dir.filePath(filename);
Chris@208 305
Chris@208 306 if (QFileInfo(filepath).exists() ||
Chris@208 307 !QFile(filepath).open(QFile::WriteOnly)) {
Chris@208 308
Chris@208 309 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \""
Chris@208 310 << filepath.toStdString() << "\" for URL \""
Chris@208 311 << url.toString().toStdString() << "\" (or file already exists)" << std::endl;
Chris@208 312
Chris@208 313 m_fileCreationMutex.unlock();
Chris@208 314 return "";
Chris@208 315 }
Chris@208 316 }
Chris@208 317
Chris@208 318 m_fileCreationMutex.unlock();
Chris@208 319
Chris@208 320 std::cerr << "RemoteFile::createLocalFile: url "
Chris@208 321 << url.toString().toStdString() << " -> local filename "
Chris@208 322 << filepath.toStdString() << std::endl;
Chris@208 323
Chris@208 324 return filepath;
Chris@208 325 }