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@211
|
60 m_ok = true;
|
Chris@208
|
61 m_http = new QHttp(url.host(), url.port(80));
|
Chris@208
|
62 connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool)));
|
Chris@208
|
63 connect(m_http, SIGNAL(dataReadProgress(int, int)),
|
Chris@208
|
64 this, SLOT(dataReadProgress(int, int)));
|
Chris@210
|
65 connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
|
Chris@210
|
66 this, SLOT(responseHeaderReceived(const QHttpResponseHeader &)));
|
Chris@208
|
67 m_http->get(url.path(), m_localFile);
|
Chris@208
|
68
|
Chris@208
|
69 } else if (scheme == "ftp") {
|
Chris@208
|
70
|
Chris@211
|
71 m_ok = true;
|
Chris@208
|
72 m_ftp = new QFtp;
|
Chris@208
|
73 connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool)));
|
Chris@208
|
74 connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
|
Chris@208
|
75 this, SLOT(dataTransferProgress(qint64, qint64)));
|
Chris@208
|
76 m_ftp->connectToHost(url.host(), url.port(21));
|
Chris@208
|
77
|
Chris@208
|
78 QString username = url.userName();
|
Chris@208
|
79 if (username == "") {
|
Chris@208
|
80 username = "anonymous";
|
Chris@208
|
81 }
|
Chris@208
|
82
|
Chris@208
|
83 QString password = url.password();
|
Chris@208
|
84 if (password == "") {
|
Chris@208
|
85 password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
|
Chris@208
|
86 }
|
Chris@208
|
87
|
Chris@208
|
88 QStringList path = url.path().split('/');
|
Chris@208
|
89 for (QStringList::iterator i = path.begin(); i != path.end(); ) {
|
Chris@208
|
90 QString bit = *i;
|
Chris@208
|
91 ++i;
|
Chris@208
|
92 if (i != path.end()) {
|
Chris@208
|
93 m_ftp->cd(*i);
|
Chris@208
|
94 } else {
|
Chris@208
|
95 m_ftp->get(*i, m_localFile);
|
Chris@208
|
96 }
|
Chris@208
|
97 }
|
Chris@208
|
98 }
|
Chris@208
|
99
|
Chris@208
|
100 if (m_ok) {
|
Chris@208
|
101 m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(url.toString()), tr("Cancel"), 0, 100);
|
Chris@210
|
102 m_progressDialog->hide();
|
Chris@210
|
103 connect(&m_progressShowTimer, SIGNAL(timeout()),
|
Chris@210
|
104 this, SLOT(showProgressDialog()));
|
Chris@210
|
105 connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled()));
|
Chris@210
|
106 m_progressShowTimer.setSingleShot(true);
|
Chris@210
|
107 m_progressShowTimer.start(2000);
|
Chris@208
|
108 }
|
Chris@208
|
109 }
|
Chris@208
|
110
|
Chris@208
|
111 RemoteFile::~RemoteFile()
|
Chris@208
|
112 {
|
Chris@211
|
113 cleanup();
|
Chris@211
|
114 }
|
Chris@211
|
115
|
Chris@211
|
116 void
|
Chris@211
|
117 RemoteFile::cleanup()
|
Chris@211
|
118 {
|
Chris@211
|
119 // std::cerr << "RemoteFile::cleanup" << std::endl;
|
Chris@211
|
120 m_done = true;
|
Chris@211
|
121 delete m_http;
|
Chris@211
|
122 m_http = 0;
|
Chris@208
|
123 delete m_ftp;
|
Chris@211
|
124 m_ftp = 0;
|
Chris@211
|
125 delete m_progressDialog;
|
Chris@211
|
126 m_progressDialog = 0;
|
Chris@208
|
127 delete m_localFile;
|
Chris@211
|
128 m_localFile = 0;
|
Chris@208
|
129 }
|
Chris@208
|
130
|
Chris@208
|
131 bool
|
Chris@208
|
132 RemoteFile::canHandleScheme(QUrl url)
|
Chris@208
|
133 {
|
Chris@208
|
134 QString scheme = url.scheme().toLower();
|
Chris@208
|
135 return (scheme == "http" || scheme == "ftp");
|
Chris@208
|
136 }
|
Chris@208
|
137
|
Chris@210
|
138 bool
|
Chris@210
|
139 RemoteFile::isAvailable()
|
Chris@210
|
140 {
|
Chris@211
|
141 while (m_ok && (!m_done && m_lastStatus == 0)) {
|
Chris@210
|
142 QApplication::processEvents();
|
Chris@210
|
143 }
|
Chris@211
|
144 bool available = true;
|
Chris@211
|
145 if (!m_ok) available = false;
|
Chris@211
|
146 else available = (m_lastStatus / 100 == 2);
|
Chris@211
|
147 std::cerr << "RemoteFile::isAvailable: " << (available ? "yes" : "no")
|
Chris@211
|
148 << std::endl;
|
Chris@211
|
149 return available;
|
Chris@210
|
150 }
|
Chris@210
|
151
|
Chris@208
|
152 void
|
Chris@208
|
153 RemoteFile::wait()
|
Chris@208
|
154 {
|
Chris@211
|
155 while (m_ok && !m_done) {
|
Chris@208
|
156 QApplication::processEvents();
|
Chris@208
|
157 }
|
Chris@208
|
158 }
|
Chris@208
|
159
|
Chris@208
|
160 bool
|
Chris@208
|
161 RemoteFile::isOK() const
|
Chris@208
|
162 {
|
Chris@208
|
163 return m_ok;
|
Chris@208
|
164 }
|
Chris@208
|
165
|
Chris@208
|
166 bool
|
Chris@208
|
167 RemoteFile::isDone() const
|
Chris@208
|
168 {
|
Chris@208
|
169 return m_done;
|
Chris@208
|
170 }
|
Chris@208
|
171
|
Chris@208
|
172 QString
|
Chris@208
|
173 RemoteFile::getLocalFilename() const
|
Chris@208
|
174 {
|
Chris@208
|
175 return m_localFilename;
|
Chris@208
|
176 }
|
Chris@208
|
177
|
Chris@208
|
178 QString
|
Chris@208
|
179 RemoteFile::getErrorString() const
|
Chris@208
|
180 {
|
Chris@208
|
181 return m_errorString;
|
Chris@208
|
182 }
|
Chris@208
|
183
|
Chris@208
|
184 void
|
Chris@208
|
185 RemoteFile::dataReadProgress(int done, int total)
|
Chris@208
|
186 {
|
Chris@208
|
187 dataTransferProgress(done, total);
|
Chris@208
|
188 }
|
Chris@208
|
189
|
Chris@208
|
190 void
|
Chris@210
|
191 RemoteFile::responseHeaderReceived(const QHttpResponseHeader &resp)
|
Chris@210
|
192 {
|
Chris@210
|
193 m_lastStatus = resp.statusCode();
|
Chris@210
|
194 if (m_lastStatus / 100 >= 4) {
|
Chris@210
|
195 m_errorString = QString("%1 %2")
|
Chris@210
|
196 .arg(resp.statusCode()).arg(resp.reasonPhrase());
|
Chris@211
|
197 std::cerr << "RemoteFile::responseHeaderReceived: "
|
Chris@211
|
198 << m_errorString.toStdString() << std::endl;
|
Chris@211
|
199 } else {
|
Chris@211
|
200 std::cerr << "RemoteFile::responseHeaderReceived: "
|
Chris@211
|
201 << m_lastStatus << std::endl;
|
Chris@211
|
202 }
|
Chris@210
|
203 }
|
Chris@210
|
204
|
Chris@210
|
205 void
|
Chris@208
|
206 RemoteFile::dataTransferProgress(qint64 done, qint64 total)
|
Chris@208
|
207 {
|
Chris@211
|
208 if (!m_progressDialog) return;
|
Chris@211
|
209
|
Chris@208
|
210 int percent = int((double(done) / double(total)) * 100.0 - 0.1);
|
Chris@208
|
211 emit progress(percent);
|
Chris@208
|
212
|
Chris@211
|
213 if (percent > 0) {
|
Chris@211
|
214 m_progressDialog->setValue(percent);
|
Chris@211
|
215 m_progressDialog->show();
|
Chris@211
|
216 }
|
Chris@210
|
217 }
|
Chris@210
|
218
|
Chris@210
|
219 void
|
Chris@210
|
220 RemoteFile::cancelled()
|
Chris@210
|
221 {
|
Chris@211
|
222 deleteLocalFile();
|
Chris@210
|
223 m_done = true;
|
Chris@210
|
224 m_ok = false;
|
Chris@210
|
225 m_errorString = tr("Download cancelled");
|
Chris@208
|
226 }
|
Chris@208
|
227
|
Chris@208
|
228 void
|
Chris@208
|
229 RemoteFile::done(bool error)
|
Chris@208
|
230 {
|
Chris@211
|
231 // std::cerr << "RemoteFile::done(" << error << ")" << std::endl;
|
Chris@211
|
232
|
Chris@211
|
233 if (m_done) return;
|
Chris@211
|
234
|
Chris@208
|
235 emit progress(100);
|
Chris@210
|
236
|
Chris@208
|
237 if (error) {
|
Chris@208
|
238 if (m_http) {
|
Chris@208
|
239 m_errorString = m_http->errorString();
|
Chris@208
|
240 } else if (m_ftp) {
|
Chris@208
|
241 m_errorString = m_ftp->errorString();
|
Chris@208
|
242 }
|
Chris@208
|
243 }
|
Chris@208
|
244
|
Chris@210
|
245 if (m_lastStatus / 100 >= 4) {
|
Chris@211
|
246 error = true;
|
Chris@210
|
247 }
|
Chris@210
|
248
|
Chris@211
|
249 cleanup();
|
Chris@208
|
250
|
Chris@211
|
251 if (!error) {
|
Chris@208
|
252 QFileInfo fi(m_localFilename);
|
Chris@208
|
253 if (!fi.exists()) {
|
Chris@208
|
254 m_errorString = tr("Failed to create local file %1").arg(m_localFilename);
|
Chris@211
|
255 error = true;
|
Chris@208
|
256 } else if (fi.size() == 0) {
|
Chris@208
|
257 m_errorString = tr("File contains no data!");
|
Chris@211
|
258 error = true;
|
Chris@208
|
259 }
|
Chris@208
|
260 }
|
Chris@211
|
261
|
Chris@211
|
262 if (error) {
|
Chris@211
|
263 deleteLocalFile();
|
Chris@211
|
264 }
|
Chris@211
|
265
|
Chris@211
|
266 m_ok = !error;
|
Chris@211
|
267 m_done = true;
|
Chris@211
|
268 }
|
Chris@211
|
269
|
Chris@211
|
270 void
|
Chris@211
|
271 RemoteFile::deleteLocalFile()
|
Chris@211
|
272 {
|
Chris@211
|
273 // std::cerr << "RemoteFile::deleteLocalFile" << std::endl;
|
Chris@211
|
274
|
Chris@211
|
275 cleanup();
|
Chris@211
|
276
|
Chris@211
|
277 if (m_localFilename == "") return;
|
Chris@211
|
278
|
Chris@211
|
279 m_fileCreationMutex.lock();
|
Chris@211
|
280
|
Chris@211
|
281 if (!QFile(m_localFilename).remove()) {
|
Chris@211
|
282 std::cerr << "RemoteFile::deleteLocalFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
|
Chris@211
|
283 } else {
|
Chris@211
|
284 m_localFilename = "";
|
Chris@211
|
285 }
|
Chris@211
|
286
|
Chris@211
|
287 m_fileCreationMutex.unlock();
|
Chris@211
|
288
|
Chris@208
|
289 m_done = true;
|
Chris@208
|
290 }
|
Chris@208
|
291
|
Chris@210
|
292 void
|
Chris@210
|
293 RemoteFile::showProgressDialog()
|
Chris@210
|
294 {
|
Chris@210
|
295 if (m_progressDialog) m_progressDialog->show();
|
Chris@210
|
296 }
|
Chris@210
|
297
|
Chris@208
|
298 QString
|
Chris@208
|
299 RemoteFile::createLocalFile(QUrl url)
|
Chris@208
|
300 {
|
Chris@208
|
301 QDir dir;
|
Chris@208
|
302 try {
|
Chris@208
|
303 dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
|
Chris@208
|
304 } catch (DirectoryCreationFailed f) {
|
Chris@208
|
305 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
|
Chris@208
|
306 return "";
|
Chris@208
|
307 }
|
Chris@208
|
308
|
Chris@208
|
309 QString filepart = url.path().section('/', -1, -1,
|
Chris@208
|
310 QString::SectionSkipEmpty);
|
Chris@208
|
311
|
Chris@208
|
312 QString extension = filepart.section('.', -1);
|
Chris@208
|
313 QString base = filepart;
|
Chris@208
|
314 if (extension != "") {
|
Chris@208
|
315 base = base.left(base.length() - extension.length() - 1);
|
Chris@208
|
316 }
|
Chris@208
|
317 if (base == "") base = "remote";
|
Chris@208
|
318
|
Chris@208
|
319 QString filename;
|
Chris@208
|
320
|
Chris@208
|
321 if (extension == "") {
|
Chris@208
|
322 filename = base;
|
Chris@208
|
323 } else {
|
Chris@208
|
324 filename = QString("%1.%2").arg(base).arg(extension);
|
Chris@208
|
325 }
|
Chris@208
|
326
|
Chris@208
|
327 QString filepath(dir.filePath(filename));
|
Chris@208
|
328
|
Chris@208
|
329 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
|
330
|
Chris@208
|
331 m_fileCreationMutex.lock();
|
Chris@208
|
332 ++m_count;
|
Chris@208
|
333
|
Chris@208
|
334 if (QFileInfo(filepath).exists() ||
|
Chris@208
|
335 !QFile(filepath).open(QFile::WriteOnly)) {
|
Chris@208
|
336
|
Chris@208
|
337 std::cerr << "RemoteFile::createLocalFile: Failed to create local file \""
|
Chris@208
|
338 << filepath.toStdString() << "\" for URL \""
|
Chris@208
|
339 << url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
|
Chris@208
|
340
|
Chris@208
|
341
|
Chris@208
|
342 if (extension == "") {
|
Chris@208
|
343 filename = QString("%1_%2").arg(base).arg(m_count);
|
Chris@208
|
344 } else {
|
Chris@208
|
345 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
|
Chris@208
|
346 }
|
Chris@208
|
347 filepath = dir.filePath(filename);
|
Chris@208
|
348
|
Chris@208
|
349 if (QFileInfo(filepath).exists() ||
|
Chris@208
|
350 !QFile(filepath).open(QFile::WriteOnly)) {
|
Chris@208
|
351
|
Chris@208
|
352 std::cerr << "RemoteFile::createLocalFile: ERROR: Failed to create local file \""
|
Chris@208
|
353 << filepath.toStdString() << "\" for URL \""
|
Chris@208
|
354 << url.toString().toStdString() << "\" (or file already exists)" << std::endl;
|
Chris@208
|
355
|
Chris@208
|
356 m_fileCreationMutex.unlock();
|
Chris@208
|
357 return "";
|
Chris@208
|
358 }
|
Chris@208
|
359 }
|
Chris@208
|
360
|
Chris@208
|
361 m_fileCreationMutex.unlock();
|
Chris@208
|
362
|
Chris@208
|
363 std::cerr << "RemoteFile::createLocalFile: url "
|
Chris@208
|
364 << url.toString().toStdString() << " -> local filename "
|
Chris@208
|
365 << filepath.toStdString() << std::endl;
|
Chris@208
|
366
|
Chris@208
|
367 return filepath;
|
Chris@208
|
368 }
|