annotate data/fileio/RemoteFile.cpp @ 282:d9319859a4cf tip

(none)
author benoitrigolleau
date Fri, 31 Oct 2008 11:00:24 +0000
parents fc9323a41f5a
children
rev   line source
lbajardsilogic@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
lbajardsilogic@0 2
lbajardsilogic@0 3 /*
lbajardsilogic@0 4 Sonic Visualiser
lbajardsilogic@0 5 An audio file viewer and annotation editor.
lbajardsilogic@0 6 Centre for Digital Music, Queen Mary, University of London.
lbajardsilogic@0 7 This file copyright 2007 QMUL.
lbajardsilogic@0 8
lbajardsilogic@0 9 This program is free software; you can redistribute it and/or
lbajardsilogic@0 10 modify it under the terms of the GNU General Public License as
lbajardsilogic@0 11 published by the Free Software Foundation; either version 2 of the
lbajardsilogic@0 12 License, or (at your option) any later version. See the file
lbajardsilogic@0 13 COPYING included with this distribution for more information.
lbajardsilogic@0 14 */
lbajardsilogic@0 15
lbajardsilogic@0 16 #include "RemoteFile.h"
lbajardsilogic@0 17 #include "base/TempDirectory.h"
lbajardsilogic@0 18 #include "base/Exceptions.h"
lbajardsilogic@0 19
lbajardsilogic@0 20 #include <QHttp>
lbajardsilogic@0 21 #include <QFtp>
lbajardsilogic@0 22 #include <QFileInfo>
lbajardsilogic@0 23 #include <QDir>
lbajardsilogic@0 24 #include <QApplication>
lbajardsilogic@0 25 #include <QProgressDialog>
lbajardsilogic@0 26 #include <QHttpResponseHeader>
lbajardsilogic@0 27
lbajardsilogic@0 28 #include <iostream>
lbajardsilogic@0 29
lbajardsilogic@0 30 int
lbajardsilogic@0 31 RemoteFile::m_count = 0;
lbajardsilogic@0 32
lbajardsilogic@0 33 QMutex
lbajardsilogic@0 34 RemoteFile::m_fileCreationMutex;
lbajardsilogic@0 35
lbajardsilogic@0 36 RemoteFile::RemoteFile(QUrl url) :
lbajardsilogic@0 37 m_ftp(0),
lbajardsilogic@0 38 m_http(0),
lbajardsilogic@0 39 m_localFile(0),
lbajardsilogic@0 40 m_ok(false),
lbajardsilogic@0 41 m_lastStatus(0),
lbajardsilogic@0 42 m_done(false),
lbajardsilogic@0 43 m_progressDialog(0),
lbajardsilogic@0 44 m_progressShowTimer(this)
lbajardsilogic@0 45 {
lbajardsilogic@0 46 if (!canHandleScheme(url)) {
lbajardsilogic@0 47 std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << url.toString().toStdString() << "\"" << std::endl;
lbajardsilogic@0 48 return;
lbajardsilogic@0 49 }
lbajardsilogic@0 50
lbajardsilogic@0 51 m_localFilename = createLocalFile(url);
lbajardsilogic@0 52 if (m_localFilename == "") return;
lbajardsilogic@0 53 m_localFile = new QFile(m_localFilename);
lbajardsilogic@0 54 m_localFile->open(QFile::WriteOnly);
lbajardsilogic@0 55
lbajardsilogic@0 56 QString scheme = url.scheme().toLower();
lbajardsilogic@0 57
lbajardsilogic@0 58 if (scheme == "http") {
lbajardsilogic@0 59
lbajardsilogic@0 60 m_ok = true;
lbajardsilogic@0 61 m_http = new QHttp(url.host(), url.port(80));
lbajardsilogic@0 62 connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool)));
lbajardsilogic@0 63 connect(m_http, SIGNAL(dataReadProgress(int, int)),
lbajardsilogic@0 64 this, SLOT(dataReadProgress(int, int)));
lbajardsilogic@0 65 connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
lbajardsilogic@0 66 this, SLOT(httpResponseHeaderReceived(const QHttpResponseHeader &)));
lbajardsilogic@0 67 m_http->get(url.path(), m_localFile);
lbajardsilogic@0 68
lbajardsilogic@0 69 } else if (scheme == "ftp") {
lbajardsilogic@0 70
lbajardsilogic@0 71 m_ok = true;
lbajardsilogic@0 72 m_ftp = new QFtp;
lbajardsilogic@0 73 connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool)));
lbajardsilogic@0 74 connect(m_ftp, SIGNAL(commandFinished(int, bool)),
lbajardsilogic@0 75 this, SLOT(ftpCommandFinished(int, bool)));
lbajardsilogic@0 76 connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
lbajardsilogic@0 77 this, SLOT(dataTransferProgress(qint64, qint64)));
lbajardsilogic@0 78 m_ftp->connectToHost(url.host(), url.port(21));
lbajardsilogic@0 79
lbajardsilogic@0 80 QString username = url.userName();
lbajardsilogic@0 81 if (username == "") {
lbajardsilogic@0 82 username = "anonymous";
lbajardsilogic@0 83 }
lbajardsilogic@0 84
lbajardsilogic@0 85 QString password = url.password();
lbajardsilogic@0 86 if (password == "") {
lbajardsilogic@0 87 password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
lbajardsilogic@0 88 }
lbajardsilogic@0 89
lbajardsilogic@0 90 m_ftp->login(username, password);
lbajardsilogic@0 91
lbajardsilogic@0 92 QString dirpath = url.path().section('/', 0, -2);
lbajardsilogic@0 93 QString filename = url.path().section('/', -1);
lbajardsilogic@0 94
lbajardsilogic@0 95 if (dirpath == "") dirpath = "/";
lbajardsilogic@0 96 m_ftp->cd(dirpath);
lbajardsilogic@0 97 m_ftp->get(filename, m_localFile);
lbajardsilogic@0 98 }
lbajardsilogic@0 99
lbajardsilogic@0 100 if (m_ok) {
lbajardsilogic@0 101 m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(url.toString()), tr("Cancel"), 0, 100);
lbajardsilogic@0 102 m_progressDialog->hide();
lbajardsilogic@0 103 connect(&m_progressShowTimer, SIGNAL(timeout()),
lbajardsilogic@0 104 this, SLOT(showProgressDialog()));
lbajardsilogic@0 105 connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled()));
lbajardsilogic@0 106 m_progressShowTimer.setSingleShot(true);
lbajardsilogic@0 107 m_progressShowTimer.start(2000);
lbajardsilogic@0 108 }
lbajardsilogic@0 109 }
lbajardsilogic@0 110
lbajardsilogic@0 111 RemoteFile::~RemoteFile()
lbajardsilogic@0 112 {
lbajardsilogic@0 113 cleanup();
lbajardsilogic@0 114 }
lbajardsilogic@0 115
lbajardsilogic@0 116 void
lbajardsilogic@0 117 RemoteFile::cleanup()
lbajardsilogic@0 118 {
lbajardsilogic@0 119 // std::cerr << "RemoteFile::cleanup" << std::endl;
lbajardsilogic@0 120 m_done = true;
lbajardsilogic@0 121 if (m_http) {
lbajardsilogic@0 122 delete m_http;
lbajardsilogic@0 123 m_http = 0;
lbajardsilogic@0 124 }
lbajardsilogic@0 125 if (m_ftp) {
lbajardsilogic@0 126 m_ftp->abort();
lbajardsilogic@0 127 m_ftp->deleteLater();
lbajardsilogic@0 128 m_ftp = 0;
lbajardsilogic@0 129 }
lbajardsilogic@0 130 delete m_progressDialog;
lbajardsilogic@0 131 m_progressDialog = 0;
lbajardsilogic@0 132 delete m_localFile;
lbajardsilogic@0 133 m_localFile = 0;
lbajardsilogic@0 134 }
lbajardsilogic@0 135
lbajardsilogic@0 136 bool
lbajardsilogic@0 137 RemoteFile::canHandleScheme(QUrl url)
lbajardsilogic@0 138 {
lbajardsilogic@0 139 QString scheme = url.scheme().toLower();
lbajardsilogic@0 140 return (scheme == "http" || scheme == "ftp");
lbajardsilogic@0 141 }
lbajardsilogic@0 142
lbajardsilogic@0 143 bool
lbajardsilogic@0 144 RemoteFile::isAvailable()
lbajardsilogic@0 145 {
lbajardsilogic@0 146 while (m_ok && (!m_done && m_lastStatus == 0)) {
lbajardsilogic@0 147 QApplication::processEvents();
lbajardsilogic@0 148 }
lbajardsilogic@0 149 bool available = true;
lbajardsilogic@0 150 if (!m_ok) available = false;
lbajardsilogic@0 151 else available = (m_lastStatus / 100 == 2);
lbajardsilogic@0 152 std::cerr << "RemoteFile::isAvailable: " << (available ? "yes" : "no")
lbajardsilogic@0 153 << std::endl;
lbajardsilogic@0 154 return available;
lbajardsilogic@0 155 }
lbajardsilogic@0 156
lbajardsilogic@0 157 void
lbajardsilogic@0 158 RemoteFile::wait()
lbajardsilogic@0 159 {
lbajardsilogic@0 160 while (m_ok && !m_done) {
lbajardsilogic@0 161 QApplication::processEvents();
lbajardsilogic@0 162 }
lbajardsilogic@0 163 }
lbajardsilogic@0 164
lbajardsilogic@0 165 bool
lbajardsilogic@0 166 RemoteFile::isOK() const
lbajardsilogic@0 167 {
lbajardsilogic@0 168 return m_ok;
lbajardsilogic@0 169 }
lbajardsilogic@0 170
lbajardsilogic@0 171 bool
lbajardsilogic@0 172 RemoteFile::isDone() const
lbajardsilogic@0 173 {
lbajardsilogic@0 174 return m_done;
lbajardsilogic@0 175 }
lbajardsilogic@0 176
lbajardsilogic@0 177 QString
lbajardsilogic@0 178 RemoteFile::getLocalFilename() const
lbajardsilogic@0 179 {
lbajardsilogic@0 180 return m_localFilename;
lbajardsilogic@0 181 }
lbajardsilogic@0 182
lbajardsilogic@0 183 QString
lbajardsilogic@0 184 RemoteFile::getErrorString() const
lbajardsilogic@0 185 {
lbajardsilogic@0 186 return m_errorString;
lbajardsilogic@0 187 }
lbajardsilogic@0 188
lbajardsilogic@0 189 void
lbajardsilogic@0 190 RemoteFile::dataReadProgress(int done, int total)
lbajardsilogic@0 191 {
lbajardsilogic@0 192 dataTransferProgress(done, total);
lbajardsilogic@0 193 }
lbajardsilogic@0 194
lbajardsilogic@0 195 void
lbajardsilogic@0 196 RemoteFile::httpResponseHeaderReceived(const QHttpResponseHeader &resp)
lbajardsilogic@0 197 {
lbajardsilogic@0 198 m_lastStatus = resp.statusCode();
lbajardsilogic@0 199 if (m_lastStatus / 100 >= 4) {
lbajardsilogic@0 200 m_errorString = QString("%1 %2")
lbajardsilogic@0 201 .arg(resp.statusCode()).arg(resp.reasonPhrase());
lbajardsilogic@0 202 std::cerr << "RemoteFile::responseHeaderReceived: "
lbajardsilogic@0 203 << m_errorString.toStdString() << std::endl;
lbajardsilogic@0 204 } else {
lbajardsilogic@0 205 std::cerr << "RemoteFile::responseHeaderReceived: "
lbajardsilogic@0 206 << m_lastStatus << std::endl;
lbajardsilogic@0 207 }
lbajardsilogic@0 208 }
lbajardsilogic@0 209
lbajardsilogic@0 210 void
lbajardsilogic@0 211 RemoteFile::ftpCommandFinished(int id, bool error)
lbajardsilogic@0 212 {
lbajardsilogic@0 213 std::cerr << "RemoteFile::ftpCommandFinished(" << id << ", " << error << ")" << std::endl;
lbajardsilogic@0 214
lbajardsilogic@0 215 if (!m_ftp) return;
lbajardsilogic@0 216
lbajardsilogic@0 217 QFtp::Command command = m_ftp->currentCommand();
lbajardsilogic@0 218
lbajardsilogic@0 219 if (!error) {
lbajardsilogic@0 220 std::cerr << "RemoteFile::ftpCommandFinished: success for command "
lbajardsilogic@0 221 << command << std::endl;
lbajardsilogic@0 222 return;
lbajardsilogic@0 223 }
lbajardsilogic@0 224
lbajardsilogic@0 225 if (command == QFtp::ConnectToHost) {
lbajardsilogic@0 226 m_errorString = tr("Failed to connect to FTP server");
lbajardsilogic@0 227 } else if (command == QFtp::Login) {
lbajardsilogic@0 228 m_errorString = tr("Login failed");
lbajardsilogic@0 229 } else if (command == QFtp::Cd) {
lbajardsilogic@0 230 m_errorString = tr("Failed to change to correct directory");
lbajardsilogic@0 231 } else if (command == QFtp::Get) {
lbajardsilogic@0 232 m_errorString = tr("FTP download aborted");
lbajardsilogic@0 233 }
lbajardsilogic@0 234
lbajardsilogic@0 235 m_lastStatus = 400; // for done()
lbajardsilogic@0 236 }
lbajardsilogic@0 237
lbajardsilogic@0 238 void
lbajardsilogic@0 239 RemoteFile::dataTransferProgress(qint64 done, qint64 total)
lbajardsilogic@0 240 {
lbajardsilogic@0 241 if (!m_progressDialog) return;
lbajardsilogic@0 242
lbajardsilogic@0 243 int percent = int((double(done) / double(total)) * 100.0 - 0.1);
lbajardsilogic@0 244 emit progress(percent);
lbajardsilogic@0 245
lbajardsilogic@0 246 if (percent > 0) {
lbajardsilogic@0 247 m_progressDialog->setValue(percent);
lbajardsilogic@0 248 m_progressDialog->show();
lbajardsilogic@0 249 }
lbajardsilogic@0 250 }
lbajardsilogic@0 251
lbajardsilogic@0 252 void
lbajardsilogic@0 253 RemoteFile::cancelled()
lbajardsilogic@0 254 {
lbajardsilogic@0 255 deleteLocalFile();
lbajardsilogic@0 256 m_done = true;
lbajardsilogic@0 257 m_ok = false;
lbajardsilogic@0 258 m_errorString = tr("Download cancelled");
lbajardsilogic@0 259 }
lbajardsilogic@0 260
lbajardsilogic@0 261 void
lbajardsilogic@0 262 RemoteFile::done(bool error)
lbajardsilogic@0 263 {
lbajardsilogic@0 264 std::cerr << "RemoteFile::done(" << error << ")" << std::endl;
lbajardsilogic@0 265
lbajardsilogic@0 266 if (m_done) return;
lbajardsilogic@0 267
lbajardsilogic@0 268 emit progress(100);
lbajardsilogic@0 269
lbajardsilogic@0 270 if (error) {
lbajardsilogic@0 271 if (m_http) {
lbajardsilogic@0 272 m_errorString = m_http->errorString();
lbajardsilogic@0 273 } else if (m_ftp) {
lbajardsilogic@0 274 m_errorString = m_ftp->errorString();
lbajardsilogic@0 275 }
lbajardsilogic@0 276 }
lbajardsilogic@0 277
lbajardsilogic@0 278 if (m_lastStatus / 100 >= 4) {
lbajardsilogic@0 279 error = true;
lbajardsilogic@0 280 }
lbajardsilogic@0 281
lbajardsilogic@0 282 cleanup();
lbajardsilogic@0 283
lbajardsilogic@0 284 if (!error) {
lbajardsilogic@0 285 QFileInfo fi(m_localFilename);
lbajardsilogic@0 286 if (!fi.exists()) {
lbajardsilogic@0 287 m_errorString = tr("Failed to create local file %1").arg(m_localFilename);
lbajardsilogic@0 288 error = true;
lbajardsilogic@0 289 } else if (fi.size() == 0) {
lbajardsilogic@0 290 m_errorString = tr("File contains no data!");
lbajardsilogic@0 291 error = true;
lbajardsilogic@0 292 }
lbajardsilogic@0 293 }
lbajardsilogic@0 294
lbajardsilogic@0 295 if (error) {
lbajardsilogic@0 296 deleteLocalFile();
lbajardsilogic@0 297 }
lbajardsilogic@0 298
lbajardsilogic@0 299 m_ok = !error;
lbajardsilogic@0 300 m_done = true;
lbajardsilogic@0 301 }
lbajardsilogic@0 302
lbajardsilogic@0 303 void
lbajardsilogic@0 304 RemoteFile::deleteLocalFile()
lbajardsilogic@0 305 {
lbajardsilogic@0 306 // std::cerr << "RemoteFile::deleteLocalFile" << std::endl;
lbajardsilogic@0 307
lbajardsilogic@0 308 cleanup();
lbajardsilogic@0 309
lbajardsilogic@0 310 if (m_localFilename == "") return;
lbajardsilogic@0 311
lbajardsilogic@0 312 m_fileCreationMutex.lock();
lbajardsilogic@0 313
lbajardsilogic@0 314 if (!QFile(m_localFilename).remove()) {
lbajardsilogic@0 315 std::cerr << "RemoteFile::deleteLocalFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
lbajardsilogic@0 316 } else {
lbajardsilogic@0 317 m_localFilename = "";
lbajardsilogic@0 318 }
lbajardsilogic@0 319
lbajardsilogic@0 320 m_fileCreationMutex.unlock();
lbajardsilogic@0 321
lbajardsilogic@0 322 m_done = true;
lbajardsilogic@0 323 }
lbajardsilogic@0 324
lbajardsilogic@0 325 void
lbajardsilogic@0 326 RemoteFile::showProgressDialog()
lbajardsilogic@0 327 {
lbajardsilogic@0 328 if (m_progressDialog) m_progressDialog->show();
lbajardsilogic@0 329 }
lbajardsilogic@0 330
lbajardsilogic@0 331 QString
lbajardsilogic@0 332 RemoteFile::createLocalFile(QUrl url)
lbajardsilogic@0 333 {
lbajardsilogic@0 334 QDir dir;
lbajardsilogic@0 335 try {
lbajardsilogic@0 336 dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
lbajardsilogic@0 337 } catch (DirectoryCreationFailed f) {
lbajardsilogic@0 338 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
lbajardsilogic@0 339 return "";
lbajardsilogic@0 340 }
lbajardsilogic@0 341
lbajardsilogic@0 342 QString filepart = url.path().section('/', -1, -1,
lbajardsilogic@0 343 QString::SectionSkipEmpty);
lbajardsilogic@0 344
lbajardsilogic@0 345 QString extension = filepart.section('.', -1);
lbajardsilogic@0 346 QString base = filepart;
lbajardsilogic@0 347 if (extension != "") {
lbajardsilogic@0 348 base = base.left(base.length() - extension.length() - 1);
lbajardsilogic@0 349 }
lbajardsilogic@0 350 if (base == "") base = "remote";
lbajardsilogic@0 351
lbajardsilogic@0 352 QString filename;
lbajardsilogic@0 353
lbajardsilogic@0 354 if (extension == "") {
lbajardsilogic@0 355 filename = base;
lbajardsilogic@0 356 } else {
lbajardsilogic@0 357 filename = QString("%1.%2").arg(base).arg(extension);
lbajardsilogic@0 358 }
lbajardsilogic@0 359
lbajardsilogic@0 360 QString filepath(dir.filePath(filename));
lbajardsilogic@0 361
lbajardsilogic@0 362 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;
lbajardsilogic@0 363
lbajardsilogic@0 364 m_fileCreationMutex.lock();
lbajardsilogic@0 365 ++m_count;
lbajardsilogic@0 366
lbajardsilogic@0 367 if (QFileInfo(filepath).exists() ||
lbajardsilogic@0 368 !QFile(filepath).open(QFile::WriteOnly)) {
lbajardsilogic@0 369
lbajardsilogic@0 370 std::cerr << "RemoteFile::createLocalFile: Failed to create local file \""
lbajardsilogic@0 371 << filepath.toStdString() << "\" for URL \""
lbajardsilogic@0 372 << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
lbajardsilogic@0 373
lbajardsilogic@0 374
lbajardsilogic@0 375 if (extension == "") {
lbajardsilogic@0 376 filename = QString("%1_%2").arg(base).arg(m_count);
lbajardsilogic@0 377 } else {
lbajardsilogic@0 378 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
lbajardsilogic@0 379 }
lbajardsilogic@0 380 filepath = dir.filePath(filename);
lbajardsilogic@0 381
lbajardsilogic@0 382 if (QFileInfo(filepath).exists() ||
lbajardsilogic@0 383 !QFile(filepath).open(QFile::WriteOnly)) {
lbajardsilogic@0 384
lbajardsilogic@0 385 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \""
lbajardsilogic@0 386 << filepath.toStdString() << "\" for URL \""
lbajardsilogic@0 387 << url.toString().toStdString() << "\" (or file already exists)" << std::endl;
lbajardsilogic@0 388
lbajardsilogic@0 389 m_fileCreationMutex.unlock();
lbajardsilogic@0 390 return "";
lbajardsilogic@0 391 }
lbajardsilogic@0 392 }
lbajardsilogic@0 393
lbajardsilogic@0 394 m_fileCreationMutex.unlock();
lbajardsilogic@0 395
lbajardsilogic@0 396 std::cerr << "RemoteFile::createLocalFile: url "
lbajardsilogic@0 397 << url.toString().toStdString() << " -> local filename "
lbajardsilogic@0 398 << filepath.toStdString() << std::endl;
lbajardsilogic@0 399
lbajardsilogic@0 400 return filepath;
lbajardsilogic@0 401 }