lbajardsilogic@0: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ lbajardsilogic@0: lbajardsilogic@0: /* lbajardsilogic@0: Sonic Visualiser lbajardsilogic@0: An audio file viewer and annotation editor. lbajardsilogic@0: Centre for Digital Music, Queen Mary, University of London. lbajardsilogic@0: This file copyright 2007 QMUL. lbajardsilogic@0: lbajardsilogic@0: This program is free software; you can redistribute it and/or lbajardsilogic@0: modify it under the terms of the GNU General Public License as lbajardsilogic@0: published by the Free Software Foundation; either version 2 of the lbajardsilogic@0: License, or (at your option) any later version. See the file lbajardsilogic@0: COPYING included with this distribution for more information. lbajardsilogic@0: */ lbajardsilogic@0: lbajardsilogic@0: #include "RemoteFile.h" lbajardsilogic@0: #include "base/TempDirectory.h" lbajardsilogic@0: #include "base/Exceptions.h" lbajardsilogic@0: lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: #include lbajardsilogic@0: lbajardsilogic@0: int lbajardsilogic@0: RemoteFile::m_count = 0; lbajardsilogic@0: lbajardsilogic@0: QMutex lbajardsilogic@0: RemoteFile::m_fileCreationMutex; lbajardsilogic@0: lbajardsilogic@0: RemoteFile::RemoteFile(QUrl url) : lbajardsilogic@0: m_ftp(0), lbajardsilogic@0: m_http(0), lbajardsilogic@0: m_localFile(0), lbajardsilogic@0: m_ok(false), lbajardsilogic@0: m_lastStatus(0), lbajardsilogic@0: m_done(false), lbajardsilogic@0: m_progressDialog(0), lbajardsilogic@0: m_progressShowTimer(this) lbajardsilogic@0: { lbajardsilogic@0: if (!canHandleScheme(url)) { lbajardsilogic@0: std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << url.toString().toStdString() << "\"" << std::endl; lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_localFilename = createLocalFile(url); lbajardsilogic@0: if (m_localFilename == "") return; lbajardsilogic@0: m_localFile = new QFile(m_localFilename); lbajardsilogic@0: m_localFile->open(QFile::WriteOnly); lbajardsilogic@0: lbajardsilogic@0: QString scheme = url.scheme().toLower(); lbajardsilogic@0: lbajardsilogic@0: if (scheme == "http") { lbajardsilogic@0: lbajardsilogic@0: m_ok = true; lbajardsilogic@0: m_http = new QHttp(url.host(), url.port(80)); lbajardsilogic@0: connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool))); lbajardsilogic@0: connect(m_http, SIGNAL(dataReadProgress(int, int)), lbajardsilogic@0: this, SLOT(dataReadProgress(int, int))); lbajardsilogic@0: connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)), lbajardsilogic@0: this, SLOT(httpResponseHeaderReceived(const QHttpResponseHeader &))); lbajardsilogic@0: m_http->get(url.path(), m_localFile); lbajardsilogic@0: lbajardsilogic@0: } else if (scheme == "ftp") { lbajardsilogic@0: lbajardsilogic@0: m_ok = true; lbajardsilogic@0: m_ftp = new QFtp; lbajardsilogic@0: connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool))); lbajardsilogic@0: connect(m_ftp, SIGNAL(commandFinished(int, bool)), lbajardsilogic@0: this, SLOT(ftpCommandFinished(int, bool))); lbajardsilogic@0: connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)), lbajardsilogic@0: this, SLOT(dataTransferProgress(qint64, qint64))); lbajardsilogic@0: m_ftp->connectToHost(url.host(), url.port(21)); lbajardsilogic@0: lbajardsilogic@0: QString username = url.userName(); lbajardsilogic@0: if (username == "") { lbajardsilogic@0: username = "anonymous"; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString password = url.password(); lbajardsilogic@0: if (password == "") { lbajardsilogic@0: password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST")); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_ftp->login(username, password); lbajardsilogic@0: lbajardsilogic@0: QString dirpath = url.path().section('/', 0, -2); lbajardsilogic@0: QString filename = url.path().section('/', -1); lbajardsilogic@0: lbajardsilogic@0: if (dirpath == "") dirpath = "/"; lbajardsilogic@0: m_ftp->cd(dirpath); lbajardsilogic@0: m_ftp->get(filename, m_localFile); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_ok) { lbajardsilogic@0: m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(url.toString()), tr("Cancel"), 0, 100); lbajardsilogic@0: m_progressDialog->hide(); lbajardsilogic@0: connect(&m_progressShowTimer, SIGNAL(timeout()), lbajardsilogic@0: this, SLOT(showProgressDialog())); lbajardsilogic@0: connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled())); lbajardsilogic@0: m_progressShowTimer.setSingleShot(true); lbajardsilogic@0: m_progressShowTimer.start(2000); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: RemoteFile::~RemoteFile() lbajardsilogic@0: { lbajardsilogic@0: cleanup(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::cleanup() lbajardsilogic@0: { lbajardsilogic@0: // std::cerr << "RemoteFile::cleanup" << std::endl; lbajardsilogic@0: m_done = true; lbajardsilogic@0: if (m_http) { lbajardsilogic@0: delete m_http; lbajardsilogic@0: m_http = 0; lbajardsilogic@0: } lbajardsilogic@0: if (m_ftp) { lbajardsilogic@0: m_ftp->abort(); lbajardsilogic@0: m_ftp->deleteLater(); lbajardsilogic@0: m_ftp = 0; lbajardsilogic@0: } lbajardsilogic@0: delete m_progressDialog; lbajardsilogic@0: m_progressDialog = 0; lbajardsilogic@0: delete m_localFile; lbajardsilogic@0: m_localFile = 0; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: RemoteFile::canHandleScheme(QUrl url) lbajardsilogic@0: { lbajardsilogic@0: QString scheme = url.scheme().toLower(); lbajardsilogic@0: return (scheme == "http" || scheme == "ftp"); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: RemoteFile::isAvailable() lbajardsilogic@0: { lbajardsilogic@0: while (m_ok && (!m_done && m_lastStatus == 0)) { lbajardsilogic@0: QApplication::processEvents(); lbajardsilogic@0: } lbajardsilogic@0: bool available = true; lbajardsilogic@0: if (!m_ok) available = false; lbajardsilogic@0: else available = (m_lastStatus / 100 == 2); lbajardsilogic@0: std::cerr << "RemoteFile::isAvailable: " << (available ? "yes" : "no") lbajardsilogic@0: << std::endl; lbajardsilogic@0: return available; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::wait() lbajardsilogic@0: { lbajardsilogic@0: while (m_ok && !m_done) { lbajardsilogic@0: QApplication::processEvents(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: RemoteFile::isOK() const lbajardsilogic@0: { lbajardsilogic@0: return m_ok; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: bool lbajardsilogic@0: RemoteFile::isDone() const lbajardsilogic@0: { lbajardsilogic@0: return m_done; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: RemoteFile::getLocalFilename() const lbajardsilogic@0: { lbajardsilogic@0: return m_localFilename; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: RemoteFile::getErrorString() const lbajardsilogic@0: { lbajardsilogic@0: return m_errorString; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::dataReadProgress(int done, int total) lbajardsilogic@0: { lbajardsilogic@0: dataTransferProgress(done, total); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::httpResponseHeaderReceived(const QHttpResponseHeader &resp) lbajardsilogic@0: { lbajardsilogic@0: m_lastStatus = resp.statusCode(); lbajardsilogic@0: if (m_lastStatus / 100 >= 4) { lbajardsilogic@0: m_errorString = QString("%1 %2") lbajardsilogic@0: .arg(resp.statusCode()).arg(resp.reasonPhrase()); lbajardsilogic@0: std::cerr << "RemoteFile::responseHeaderReceived: " lbajardsilogic@0: << m_errorString.toStdString() << std::endl; lbajardsilogic@0: } else { lbajardsilogic@0: std::cerr << "RemoteFile::responseHeaderReceived: " lbajardsilogic@0: << m_lastStatus << std::endl; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::ftpCommandFinished(int id, bool error) lbajardsilogic@0: { lbajardsilogic@0: std::cerr << "RemoteFile::ftpCommandFinished(" << id << ", " << error << ")" << std::endl; lbajardsilogic@0: lbajardsilogic@0: if (!m_ftp) return; lbajardsilogic@0: lbajardsilogic@0: QFtp::Command command = m_ftp->currentCommand(); lbajardsilogic@0: lbajardsilogic@0: if (!error) { lbajardsilogic@0: std::cerr << "RemoteFile::ftpCommandFinished: success for command " lbajardsilogic@0: << command << std::endl; lbajardsilogic@0: return; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (command == QFtp::ConnectToHost) { lbajardsilogic@0: m_errorString = tr("Failed to connect to FTP server"); lbajardsilogic@0: } else if (command == QFtp::Login) { lbajardsilogic@0: m_errorString = tr("Login failed"); lbajardsilogic@0: } else if (command == QFtp::Cd) { lbajardsilogic@0: m_errorString = tr("Failed to change to correct directory"); lbajardsilogic@0: } else if (command == QFtp::Get) { lbajardsilogic@0: m_errorString = tr("FTP download aborted"); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_lastStatus = 400; // for done() lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::dataTransferProgress(qint64 done, qint64 total) lbajardsilogic@0: { lbajardsilogic@0: if (!m_progressDialog) return; lbajardsilogic@0: lbajardsilogic@0: int percent = int((double(done) / double(total)) * 100.0 - 0.1); lbajardsilogic@0: emit progress(percent); lbajardsilogic@0: lbajardsilogic@0: if (percent > 0) { lbajardsilogic@0: m_progressDialog->setValue(percent); lbajardsilogic@0: m_progressDialog->show(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::cancelled() lbajardsilogic@0: { lbajardsilogic@0: deleteLocalFile(); lbajardsilogic@0: m_done = true; lbajardsilogic@0: m_ok = false; lbajardsilogic@0: m_errorString = tr("Download cancelled"); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::done(bool error) lbajardsilogic@0: { lbajardsilogic@0: std::cerr << "RemoteFile::done(" << error << ")" << std::endl; lbajardsilogic@0: lbajardsilogic@0: if (m_done) return; lbajardsilogic@0: lbajardsilogic@0: emit progress(100); lbajardsilogic@0: lbajardsilogic@0: if (error) { lbajardsilogic@0: if (m_http) { lbajardsilogic@0: m_errorString = m_http->errorString(); lbajardsilogic@0: } else if (m_ftp) { lbajardsilogic@0: m_errorString = m_ftp->errorString(); lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (m_lastStatus / 100 >= 4) { lbajardsilogic@0: error = true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: cleanup(); lbajardsilogic@0: lbajardsilogic@0: if (!error) { lbajardsilogic@0: QFileInfo fi(m_localFilename); lbajardsilogic@0: if (!fi.exists()) { lbajardsilogic@0: m_errorString = tr("Failed to create local file %1").arg(m_localFilename); lbajardsilogic@0: error = true; lbajardsilogic@0: } else if (fi.size() == 0) { lbajardsilogic@0: m_errorString = tr("File contains no data!"); lbajardsilogic@0: error = true; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: if (error) { lbajardsilogic@0: deleteLocalFile(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_ok = !error; lbajardsilogic@0: m_done = true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::deleteLocalFile() lbajardsilogic@0: { lbajardsilogic@0: // std::cerr << "RemoteFile::deleteLocalFile" << std::endl; lbajardsilogic@0: lbajardsilogic@0: cleanup(); lbajardsilogic@0: lbajardsilogic@0: if (m_localFilename == "") return; lbajardsilogic@0: lbajardsilogic@0: m_fileCreationMutex.lock(); lbajardsilogic@0: lbajardsilogic@0: if (!QFile(m_localFilename).remove()) { lbajardsilogic@0: std::cerr << "RemoteFile::deleteLocalFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl; lbajardsilogic@0: } else { lbajardsilogic@0: m_localFilename = ""; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_fileCreationMutex.unlock(); lbajardsilogic@0: lbajardsilogic@0: m_done = true; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: void lbajardsilogic@0: RemoteFile::showProgressDialog() lbajardsilogic@0: { lbajardsilogic@0: if (m_progressDialog) m_progressDialog->show(); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString lbajardsilogic@0: RemoteFile::createLocalFile(QUrl url) lbajardsilogic@0: { lbajardsilogic@0: QDir dir; lbajardsilogic@0: try { lbajardsilogic@0: dir = TempDirectory::getInstance()->getSubDirectoryPath("download"); lbajardsilogic@0: } catch (DirectoryCreationFailed f) { lbajardsilogic@0: std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl; lbajardsilogic@0: return ""; lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString filepart = url.path().section('/', -1, -1, lbajardsilogic@0: QString::SectionSkipEmpty); lbajardsilogic@0: lbajardsilogic@0: QString extension = filepart.section('.', -1); lbajardsilogic@0: QString base = filepart; lbajardsilogic@0: if (extension != "") { lbajardsilogic@0: base = base.left(base.length() - extension.length() - 1); lbajardsilogic@0: } lbajardsilogic@0: if (base == "") base = "remote"; lbajardsilogic@0: lbajardsilogic@0: QString filename; lbajardsilogic@0: lbajardsilogic@0: if (extension == "") { lbajardsilogic@0: filename = base; lbajardsilogic@0: } else { lbajardsilogic@0: filename = QString("%1.%2").arg(base).arg(extension); lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: QString filepath(dir.filePath(filename)); lbajardsilogic@0: lbajardsilogic@0: 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: lbajardsilogic@0: m_fileCreationMutex.lock(); lbajardsilogic@0: ++m_count; lbajardsilogic@0: lbajardsilogic@0: if (QFileInfo(filepath).exists() || lbajardsilogic@0: !QFile(filepath).open(QFile::WriteOnly)) { lbajardsilogic@0: lbajardsilogic@0: std::cerr << "RemoteFile::createLocalFile: Failed to create local file \"" lbajardsilogic@0: << filepath.toStdString() << "\" for URL \"" lbajardsilogic@0: << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl; lbajardsilogic@0: lbajardsilogic@0: lbajardsilogic@0: if (extension == "") { lbajardsilogic@0: filename = QString("%1_%2").arg(base).arg(m_count); lbajardsilogic@0: } else { lbajardsilogic@0: filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension); lbajardsilogic@0: } lbajardsilogic@0: filepath = dir.filePath(filename); lbajardsilogic@0: lbajardsilogic@0: if (QFileInfo(filepath).exists() || lbajardsilogic@0: !QFile(filepath).open(QFile::WriteOnly)) { lbajardsilogic@0: lbajardsilogic@0: std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \"" lbajardsilogic@0: << filepath.toStdString() << "\" for URL \"" lbajardsilogic@0: << url.toString().toStdString() << "\" (or file already exists)" << std::endl; lbajardsilogic@0: lbajardsilogic@0: m_fileCreationMutex.unlock(); lbajardsilogic@0: return ""; lbajardsilogic@0: } lbajardsilogic@0: } lbajardsilogic@0: lbajardsilogic@0: m_fileCreationMutex.unlock(); lbajardsilogic@0: lbajardsilogic@0: std::cerr << "RemoteFile::createLocalFile: url " lbajardsilogic@0: << url.toString().toStdString() << " -> local filename " lbajardsilogic@0: << filepath.toStdString() << std::endl; lbajardsilogic@0: lbajardsilogic@0: return filepath; lbajardsilogic@0: }