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