annotate data/fileio/RemoteFile.cpp @ 208:8a3d68910b37

* Framework for retrieving files from remote locations
author Chris Cannam
date Mon, 08 Jan 2007 17:04:35 +0000
parents
children a06afefe45ee
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@208 26
Chris@208 27 #include <iostream>
Chris@208 28
Chris@208 29 int
Chris@208 30 RemoteFile::m_count = 0;
Chris@208 31
Chris@208 32 QMutex
Chris@208 33 RemoteFile::m_fileCreationMutex;
Chris@208 34
Chris@208 35 RemoteFile::RemoteFile(QUrl url) :
Chris@208 36 m_ftp(0),
Chris@208 37 m_http(0),
Chris@208 38 m_localFile(0),
Chris@208 39 m_ok(false),
Chris@208 40 m_done(false),
Chris@208 41 m_progressDialog(0)
Chris@208 42 {
Chris@208 43 if (!canHandleScheme(url)) {
Chris@208 44 std::cerr << "RemoteFile::RemoteFile: ERROR: Unsupported scheme in URL \"" << url.toString().toStdString() << "\"" << std::endl;
Chris@208 45 return;
Chris@208 46 }
Chris@208 47
Chris@208 48 m_localFilename = createLocalFile(url);
Chris@208 49 if (m_localFilename == "") return;
Chris@208 50 m_localFile = new QFile(m_localFilename);
Chris@208 51 m_localFile->open(QFile::WriteOnly);
Chris@208 52
Chris@208 53 QString scheme = url.scheme().toLower();
Chris@208 54
Chris@208 55 if (scheme == "http") {
Chris@208 56
Chris@208 57 m_http = new QHttp(url.host(), url.port(80));
Chris@208 58 connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool)));
Chris@208 59 connect(m_http, SIGNAL(dataReadProgress(int, int)),
Chris@208 60 this, SLOT(dataReadProgress(int, int)));
Chris@208 61 m_http->get(url.path(), m_localFile);
Chris@208 62 m_ok = true;
Chris@208 63
Chris@208 64 } else if (scheme == "ftp") {
Chris@208 65
Chris@208 66 m_ftp = new QFtp;
Chris@208 67 connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool)));
Chris@208 68 connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
Chris@208 69 this, SLOT(dataTransferProgress(qint64, qint64)));
Chris@208 70 m_ftp->connectToHost(url.host(), url.port(21));
Chris@208 71
Chris@208 72 QString username = url.userName();
Chris@208 73 if (username == "") {
Chris@208 74 username = "anonymous";
Chris@208 75 }
Chris@208 76
Chris@208 77 QString password = url.password();
Chris@208 78 if (password == "") {
Chris@208 79 password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
Chris@208 80 }
Chris@208 81
Chris@208 82 QStringList path = url.path().split('/');
Chris@208 83 for (QStringList::iterator i = path.begin(); i != path.end(); ) {
Chris@208 84 QString bit = *i;
Chris@208 85 ++i;
Chris@208 86 if (i != path.end()) {
Chris@208 87 m_ftp->cd(*i);
Chris@208 88 } else {
Chris@208 89 m_ftp->get(*i, m_localFile);
Chris@208 90 }
Chris@208 91 }
Chris@208 92
Chris@208 93 m_ok = true;
Chris@208 94 }
Chris@208 95
Chris@208 96 if (m_ok) {
Chris@208 97 m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(url.toString()), tr("Cancel"), 0, 100);
Chris@208 98 m_progressDialog->show();
Chris@208 99 }
Chris@208 100 }
Chris@208 101
Chris@208 102 RemoteFile::~RemoteFile()
Chris@208 103 {
Chris@208 104 delete m_ftp;
Chris@208 105 delete m_http;
Chris@208 106 delete m_localFile;
Chris@208 107 delete m_progressDialog;
Chris@208 108 }
Chris@208 109
Chris@208 110 bool
Chris@208 111 RemoteFile::canHandleScheme(QUrl url)
Chris@208 112 {
Chris@208 113 QString scheme = url.scheme().toLower();
Chris@208 114 return (scheme == "http" || scheme == "ftp");
Chris@208 115 }
Chris@208 116
Chris@208 117 void
Chris@208 118 RemoteFile::wait()
Chris@208 119 {
Chris@208 120 while (!m_done) {
Chris@208 121 QApplication::processEvents();
Chris@208 122 }
Chris@208 123 }
Chris@208 124
Chris@208 125 bool
Chris@208 126 RemoteFile::isOK() const
Chris@208 127 {
Chris@208 128 return m_ok;
Chris@208 129 }
Chris@208 130
Chris@208 131 bool
Chris@208 132 RemoteFile::isDone() const
Chris@208 133 {
Chris@208 134 return m_done;
Chris@208 135 }
Chris@208 136
Chris@208 137 QString
Chris@208 138 RemoteFile::getLocalFilename() const
Chris@208 139 {
Chris@208 140 return m_localFilename;
Chris@208 141 }
Chris@208 142
Chris@208 143 QString
Chris@208 144 RemoteFile::getErrorString() const
Chris@208 145 {
Chris@208 146 return m_errorString;
Chris@208 147 }
Chris@208 148
Chris@208 149 void
Chris@208 150 RemoteFile::dataReadProgress(int done, int total)
Chris@208 151 {
Chris@208 152 dataTransferProgress(done, total);
Chris@208 153 }
Chris@208 154
Chris@208 155 void
Chris@208 156 RemoteFile::dataTransferProgress(qint64 done, qint64 total)
Chris@208 157 {
Chris@208 158 int percent = int((double(done) / double(total)) * 100.0 - 0.1);
Chris@208 159 emit progress(percent);
Chris@208 160
Chris@208 161 m_progressDialog->setValue(percent);
Chris@208 162 }
Chris@208 163
Chris@208 164 void
Chris@208 165 RemoteFile::done(bool error)
Chris@208 166 {
Chris@208 167 //!!! need to identify 404s etc in the return headers
Chris@208 168
Chris@208 169 emit progress(100);
Chris@208 170 m_ok = !error;
Chris@208 171 if (error) {
Chris@208 172 if (m_http) {
Chris@208 173 m_errorString = m_http->errorString();
Chris@208 174 } else if (m_ftp) {
Chris@208 175 m_errorString = m_ftp->errorString();
Chris@208 176 }
Chris@208 177 }
Chris@208 178
Chris@208 179 delete m_localFile;
Chris@208 180 m_localFile = 0;
Chris@208 181
Chris@208 182 delete m_progressDialog;
Chris@208 183 m_progressDialog = 0;
Chris@208 184
Chris@208 185 if (m_ok) {
Chris@208 186 QFileInfo fi(m_localFilename);
Chris@208 187 if (!fi.exists()) {
Chris@208 188 m_errorString = tr("Failed to create local file %1").arg(m_localFilename);
Chris@208 189 m_ok = false;
Chris@208 190 } else if (fi.size() == 0) {
Chris@208 191 m_errorString = tr("File contains no data!");
Chris@208 192 m_ok = false;
Chris@208 193 }
Chris@208 194 }
Chris@208 195 m_done = true;
Chris@208 196 }
Chris@208 197
Chris@208 198 QString
Chris@208 199 RemoteFile::createLocalFile(QUrl url)
Chris@208 200 {
Chris@208 201 //!!! should we actually put up dialogs for these errors? or propagate an exception?
Chris@208 202
Chris@208 203 QDir dir;
Chris@208 204 try {
Chris@208 205 dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
Chris@208 206 } catch (DirectoryCreationFailed f) {
Chris@208 207 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
Chris@208 208 return "";
Chris@208 209 }
Chris@208 210
Chris@208 211 QString filepart = url.path().section('/', -1, -1,
Chris@208 212 QString::SectionSkipEmpty);
Chris@208 213
Chris@208 214 QString extension = filepart.section('.', -1);
Chris@208 215 QString base = filepart;
Chris@208 216 if (extension != "") {
Chris@208 217 base = base.left(base.length() - extension.length() - 1);
Chris@208 218 }
Chris@208 219 if (base == "") base = "remote";
Chris@208 220
Chris@208 221 QString filename;
Chris@208 222
Chris@208 223 if (extension == "") {
Chris@208 224 filename = base;
Chris@208 225 } else {
Chris@208 226 filename = QString("%1.%2").arg(base).arg(extension);
Chris@208 227 }
Chris@208 228
Chris@208 229 QString filepath(dir.filePath(filename));
Chris@208 230
Chris@208 231 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 232
Chris@208 233 m_fileCreationMutex.lock();
Chris@208 234 ++m_count;
Chris@208 235
Chris@208 236 if (QFileInfo(filepath).exists() ||
Chris@208 237 !QFile(filepath).open(QFile::WriteOnly)) {
Chris@208 238
Chris@208 239 std::cerr << "RemoteFile::createLocalFile: Failed to create local file \""
Chris@208 240 << filepath.toStdString() << "\" for URL \""
Chris@208 241 << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
Chris@208 242
Chris@208 243
Chris@208 244 if (extension == "") {
Chris@208 245 filename = QString("%1_%2").arg(base).arg(m_count);
Chris@208 246 } else {
Chris@208 247 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
Chris@208 248 }
Chris@208 249 filepath = dir.filePath(filename);
Chris@208 250
Chris@208 251 if (QFileInfo(filepath).exists() ||
Chris@208 252 !QFile(filepath).open(QFile::WriteOnly)) {
Chris@208 253
Chris@208 254 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \""
Chris@208 255 << filepath.toStdString() << "\" for URL \""
Chris@208 256 << url.toString().toStdString() << "\" (or file already exists)" << std::endl;
Chris@208 257
Chris@208 258 m_fileCreationMutex.unlock();
Chris@208 259 return "";
Chris@208 260 }
Chris@208 261 }
Chris@208 262
Chris@208 263 m_fileCreationMutex.unlock();
Chris@208 264
Chris@208 265 std::cerr << "RemoteFile::createLocalFile: url "
Chris@208 266 << url.toString().toStdString() << " -> local filename "
Chris@208 267 << filepath.toStdString() << std::endl;
Chris@208 268
Chris@208 269 return filepath;
Chris@208 270 }