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@317
|
16 #include "FileSource.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@317
|
31 FileSource::m_count = 0;
|
Chris@208
|
32
|
Chris@208
|
33 QMutex
|
Chris@317
|
34 FileSource::m_fileCreationMutex;
|
Chris@208
|
35
|
Chris@317
|
36 FileSource::RemoteRefCountMap
|
Chris@317
|
37 FileSource::m_refCountMap;
|
Chris@304
|
38
|
Chris@317
|
39 FileSource::RemoteLocalMap
|
Chris@317
|
40 FileSource::m_remoteLocalMap;
|
Chris@304
|
41
|
Chris@304
|
42 QMutex
|
Chris@317
|
43 FileSource::m_mapMutex;
|
Chris@304
|
44
|
Chris@317
|
45 FileSource::FileSource(QString fileOrUrl, bool showProgress) :
|
Chris@316
|
46 m_url(fileOrUrl),
|
Chris@316
|
47 m_ftp(0),
|
Chris@316
|
48 m_http(0),
|
Chris@316
|
49 m_localFile(0),
|
Chris@316
|
50 m_ok(false),
|
Chris@316
|
51 m_lastStatus(0),
|
Chris@316
|
52 m_remote(isRemote(fileOrUrl)),
|
Chris@316
|
53 m_done(false),
|
Chris@316
|
54 m_leaveLocalFile(false),
|
Chris@316
|
55 m_progressDialog(0),
|
Chris@316
|
56 m_progressShowTimer(this),
|
Chris@316
|
57 m_refCounted(false)
|
Chris@316
|
58 {
|
Chris@317
|
59 std::cerr << "FileSource::FileSource(" << fileOrUrl.toStdString() << ")" << std::endl;
|
Chris@316
|
60
|
Chris@316
|
61 if (!canHandleScheme(m_url)) {
|
Chris@317
|
62 std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl;
|
Chris@316
|
63 m_errorString = tr("Unsupported scheme in URL");
|
Chris@316
|
64 return;
|
Chris@316
|
65 }
|
Chris@316
|
66
|
Chris@316
|
67 init(showProgress);
|
Chris@316
|
68
|
Chris@316
|
69 if (isRemote() &&
|
Chris@316
|
70 (fileOrUrl.contains('%') ||
|
Chris@316
|
71 fileOrUrl.contains("--"))) { // for IDNA
|
Chris@316
|
72
|
Chris@316
|
73 waitForStatus();
|
Chris@316
|
74
|
Chris@316
|
75 if (!isAvailable()) {
|
Chris@316
|
76 // The URL was created on the assumption that the string
|
Chris@316
|
77 // was human-readable. Let's try again, this time
|
Chris@316
|
78 // assuming it was already encoded.
|
Chris@317
|
79 std::cerr << "FileSource::FileSource: Failed to retrieve URL \""
|
Chris@316
|
80 << fileOrUrl.toStdString()
|
Chris@316
|
81 << "\" as human-readable URL; "
|
Chris@316
|
82 << "trying again treating it as encoded URL"
|
Chris@316
|
83 << std::endl;
|
Chris@316
|
84 m_url.setEncodedUrl(fileOrUrl.toAscii());
|
Chris@316
|
85 init(showProgress);
|
Chris@316
|
86 }
|
Chris@316
|
87 }
|
Chris@325
|
88
|
Chris@325
|
89 if (!isRemote()) {
|
Chris@325
|
90 emit statusAvailable();
|
Chris@325
|
91 emit ready();
|
Chris@325
|
92 }
|
Chris@316
|
93 }
|
Chris@316
|
94
|
Chris@317
|
95 FileSource::FileSource(QUrl url, bool showProgress) :
|
Chris@304
|
96 m_url(url),
|
Chris@208
|
97 m_ftp(0),
|
Chris@208
|
98 m_http(0),
|
Chris@208
|
99 m_localFile(0),
|
Chris@208
|
100 m_ok(false),
|
Chris@210
|
101 m_lastStatus(0),
|
Chris@316
|
102 m_remote(isRemote(url.toString())),
|
Chris@208
|
103 m_done(false),
|
Chris@316
|
104 m_leaveLocalFile(false),
|
Chris@210
|
105 m_progressDialog(0),
|
Chris@304
|
106 m_progressShowTimer(this),
|
Chris@316
|
107 m_refCounted(false)
|
Chris@208
|
108 {
|
Chris@317
|
109 std::cerr << "FileSource::FileSource(" << url.toString().toStdString() << ") [as url]" << std::endl;
|
Chris@316
|
110
|
Chris@316
|
111 if (!canHandleScheme(m_url)) {
|
Chris@317
|
112 std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl;
|
Chris@316
|
113 m_errorString = tr("Unsupported scheme in URL");
|
Chris@208
|
114 return;
|
Chris@208
|
115 }
|
Chris@208
|
116
|
Chris@316
|
117 init(showProgress);
|
Chris@316
|
118 }
|
Chris@304
|
119
|
Chris@317
|
120 FileSource::FileSource(const FileSource &rf) :
|
Chris@316
|
121 QObject(),
|
Chris@316
|
122 m_url(rf.m_url),
|
Chris@316
|
123 m_ftp(0),
|
Chris@316
|
124 m_http(0),
|
Chris@316
|
125 m_localFile(0),
|
Chris@316
|
126 m_ok(rf.m_ok),
|
Chris@316
|
127 m_lastStatus(rf.m_lastStatus),
|
Chris@316
|
128 m_remote(rf.m_remote),
|
Chris@316
|
129 m_done(false),
|
Chris@316
|
130 m_leaveLocalFile(false),
|
Chris@316
|
131 m_progressDialog(0),
|
Chris@316
|
132 m_progressShowTimer(0),
|
Chris@316
|
133 m_refCounted(false)
|
Chris@316
|
134 {
|
Chris@317
|
135 std::cerr << "FileSource::FileSource(" << m_url.toString().toStdString() << ") [copy ctor]" << std::endl;
|
Chris@304
|
136
|
Chris@316
|
137 if (!canHandleScheme(m_url)) {
|
Chris@317
|
138 std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl;
|
Chris@316
|
139 m_errorString = tr("Unsupported scheme in URL");
|
Chris@304
|
140 return;
|
Chris@304
|
141 }
|
Chris@304
|
142
|
Chris@316
|
143 if (!isRemote()) {
|
Chris@316
|
144 m_localFilename = rf.m_localFilename;
|
Chris@316
|
145 } else {
|
Chris@316
|
146 QMutexLocker locker(&m_mapMutex);
|
Chris@317
|
147 std::cerr << "FileSource::FileSource(copy ctor): ref count is "
|
Chris@316
|
148 << m_refCountMap[m_url] << std::endl;
|
Chris@316
|
149 if (m_refCountMap[m_url] > 0) {
|
Chris@316
|
150 m_refCountMap[m_url]++;
|
Chris@316
|
151 std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl;
|
Chris@316
|
152 m_localFilename = m_remoteLocalMap[m_url];
|
Chris@316
|
153 m_refCounted = true;
|
Chris@316
|
154 } else {
|
Chris@316
|
155 m_ok = false;
|
Chris@316
|
156 m_lastStatus = 404;
|
Chris@316
|
157 }
|
Chris@316
|
158 }
|
Chris@316
|
159
|
Chris@316
|
160 m_done = true;
|
Chris@316
|
161 }
|
Chris@316
|
162
|
Chris@317
|
163 FileSource::~FileSource()
|
Chris@316
|
164 {
|
Chris@317
|
165 std::cerr << "FileSource(" << m_url.toString().toStdString() << ")::~FileSource" << std::endl;
|
Chris@316
|
166
|
Chris@316
|
167 cleanup();
|
Chris@316
|
168
|
Chris@316
|
169 if (isRemote() && !m_leaveLocalFile) deleteCacheFile();
|
Chris@316
|
170 }
|
Chris@316
|
171
|
Chris@316
|
172 void
|
Chris@317
|
173 FileSource::init(bool showProgress)
|
Chris@316
|
174 {
|
Chris@316
|
175 if (!isRemote()) {
|
Chris@316
|
176 m_localFilename = m_url.toLocalFile();
|
Chris@316
|
177 m_ok = true;
|
Chris@316
|
178 if (!QFileInfo(m_localFilename).exists()) {
|
Chris@316
|
179 m_lastStatus = 404;
|
Chris@316
|
180 } else {
|
Chris@316
|
181 m_lastStatus = 200;
|
Chris@316
|
182 }
|
Chris@316
|
183 m_done = true;
|
Chris@316
|
184 return;
|
Chris@316
|
185 }
|
Chris@316
|
186
|
Chris@316
|
187 if (createCacheFile()) {
|
Chris@317
|
188 std::cerr << "FileSource::init: Already have this one" << std::endl;
|
Chris@316
|
189 m_ok = true;
|
Chris@316
|
190 if (!QFileInfo(m_localFilename).exists()) {
|
Chris@316
|
191 m_lastStatus = 404;
|
Chris@316
|
192 } else {
|
Chris@316
|
193 m_lastStatus = 200;
|
Chris@316
|
194 }
|
Chris@316
|
195 m_done = true;
|
Chris@316
|
196 return;
|
Chris@316
|
197 }
|
Chris@316
|
198
|
Chris@208
|
199 if (m_localFilename == "") return;
|
Chris@208
|
200 m_localFile = new QFile(m_localFilename);
|
Chris@208
|
201 m_localFile->open(QFile::WriteOnly);
|
Chris@208
|
202
|
Chris@316
|
203 QString scheme = m_url.scheme().toLower();
|
Chris@316
|
204
|
Chris@317
|
205 std::cerr << "FileSource::init: Don't have local copy of \""
|
Chris@316
|
206 << m_url.toString().toStdString() << "\", retrieving" << std::endl;
|
Chris@208
|
207
|
Chris@208
|
208 if (scheme == "http") {
|
Chris@316
|
209 initHttp();
|
Chris@208
|
210 } else if (scheme == "ftp") {
|
Chris@316
|
211 initFtp();
|
Chris@316
|
212 } else {
|
Chris@316
|
213 m_remote = false;
|
Chris@316
|
214 m_ok = false;
|
Chris@208
|
215 }
|
Chris@208
|
216
|
Chris@208
|
217 if (m_ok) {
|
Chris@316
|
218
|
Chris@316
|
219 QMutexLocker locker(&m_mapMutex);
|
Chris@316
|
220
|
Chris@316
|
221 if (m_refCountMap[m_url] > 0) {
|
Chris@316
|
222 // someone else has been doing the same thing at the same time,
|
Chris@316
|
223 // but has got there first
|
Chris@316
|
224 cleanup();
|
Chris@316
|
225 m_refCountMap[m_url]++;
|
Chris@317
|
226 std::cerr << "FileSource::init: Another FileSource has got there first, abandoning our download and using theirs" << std::endl;
|
Chris@316
|
227 m_localFilename = m_remoteLocalMap[m_url];
|
Chris@316
|
228 m_refCounted = true;
|
Chris@316
|
229 m_ok = true;
|
Chris@316
|
230 if (!QFileInfo(m_localFilename).exists()) {
|
Chris@316
|
231 m_lastStatus = 404;
|
Chris@316
|
232 }
|
Chris@316
|
233 m_done = true;
|
Chris@316
|
234 return;
|
Chris@316
|
235 }
|
Chris@304
|
236
|
Chris@304
|
237 m_remoteLocalMap[m_url] = m_localFilename;
|
Chris@304
|
238 m_refCountMap[m_url]++;
|
Chris@316
|
239 m_refCounted = true;
|
Chris@304
|
240
|
Chris@316
|
241 if (showProgress) {
|
Chris@316
|
242 m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(m_url.toString()), tr("Cancel"), 0, 100);
|
Chris@316
|
243 m_progressDialog->hide();
|
Chris@316
|
244 connect(&m_progressShowTimer, SIGNAL(timeout()),
|
Chris@316
|
245 this, SLOT(showProgressDialog()));
|
Chris@316
|
246 connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled()));
|
Chris@316
|
247 m_progressShowTimer.setSingleShot(true);
|
Chris@316
|
248 m_progressShowTimer.start(2000);
|
Chris@316
|
249 }
|
Chris@208
|
250 }
|
Chris@208
|
251 }
|
Chris@208
|
252
|
Chris@316
|
253 void
|
Chris@317
|
254 FileSource::initHttp()
|
Chris@208
|
255 {
|
Chris@316
|
256 m_ok = true;
|
Chris@316
|
257 m_http = new QHttp(m_url.host(), m_url.port(80));
|
Chris@316
|
258 connect(m_http, SIGNAL(done(bool)), this, SLOT(done(bool)));
|
Chris@316
|
259 connect(m_http, SIGNAL(dataReadProgress(int, int)),
|
Chris@316
|
260 this, SLOT(dataReadProgress(int, int)));
|
Chris@316
|
261 connect(m_http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
|
Chris@316
|
262 this, SLOT(httpResponseHeaderReceived(const QHttpResponseHeader &)));
|
Chris@316
|
263
|
Chris@316
|
264 // I don't quite understand this. url.path() returns a path
|
Chris@316
|
265 // without percent encoding; for example, spaces appear as
|
Chris@316
|
266 // literal spaces. This generally won't work if sent to the
|
Chris@316
|
267 // server directly. You can retrieve a correctly encoded URL
|
Chris@316
|
268 // from QUrl using url.toEncoded(), but that gives you the
|
Chris@316
|
269 // whole URL; there doesn't seem to be any way to retrieve
|
Chris@316
|
270 // only an encoded path. Furthermore there doesn't seem to be
|
Chris@316
|
271 // any way to convert a retrieved path into an encoded path
|
Chris@316
|
272 // without explicitly specifying that you don't want the path
|
Chris@316
|
273 // separators ("/") to be encoded. (Besides being painful to
|
Chris@316
|
274 // manage, I don't see how this can work correctly in any case
|
Chris@316
|
275 // where a percent-encoded "/" is supposed to appear within a
|
Chris@316
|
276 // path element?) There also seems to be no way to retrieve
|
Chris@316
|
277 // the path plus query string, i.e. everything that I need to
|
Chris@316
|
278 // send to the HTTP server. And no way for QHttp to take a
|
Chris@316
|
279 // QUrl argument. I'm obviously missing something.
|
Chris@316
|
280
|
Chris@316
|
281 // So, two ways to do this: query the bits from the URL,
|
Chris@316
|
282 // encode them individually, and glue them back together
|
Chris@316
|
283 // again...
|
Chris@316
|
284 /*
|
Chris@316
|
285 QString path = QUrl::toPercentEncoding(m_url.path(), "/");
|
Chris@316
|
286 QList<QPair<QString, QString> > query = m_url.queryItems();
|
Chris@316
|
287 if (!query.empty()) {
|
Chris@316
|
288 QStringList q2;
|
Chris@316
|
289 for (QList<QPair<QString, QString> >::iterator i = query.begin();
|
Chris@316
|
290 i != query.end(); ++i) {
|
Chris@316
|
291 q2.push_back(QString("%1=%3")
|
Chris@316
|
292 .arg(QString(QUrl::toPercentEncoding(i->first)))
|
Chris@316
|
293 .arg(QString(QUrl::toPercentEncoding(i->second))));
|
Chris@316
|
294 }
|
Chris@316
|
295 path = QString("%1%2%3")
|
Chris@316
|
296 .arg(path).arg("?")
|
Chris@316
|
297 .arg(q2.join("&"));
|
Chris@316
|
298 }
|
Chris@316
|
299 */
|
Chris@316
|
300
|
Chris@316
|
301 // ...or, much simpler but relying on knowledge about the
|
Chris@316
|
302 // scheme://host/path/path/query etc format of the URL, we can
|
Chris@316
|
303 // get the whole URL ready-encoded and then split it on "/" as
|
Chris@316
|
304 // appropriate...
|
Chris@316
|
305
|
Chris@316
|
306 QString path = "/" + QString(m_url.toEncoded()).section('/', 3);
|
Chris@316
|
307
|
Chris@317
|
308 std::cerr << "FileSource: path is \""
|
Chris@316
|
309 << path.toStdString() << "\"" << std::endl;
|
Chris@316
|
310
|
Chris@316
|
311 m_http->get(path, m_localFile);
|
Chris@316
|
312 }
|
Chris@316
|
313
|
Chris@316
|
314 void
|
Chris@317
|
315 FileSource::initFtp()
|
Chris@316
|
316 {
|
Chris@316
|
317 m_ok = true;
|
Chris@316
|
318 m_ftp = new QFtp;
|
Chris@316
|
319 connect(m_ftp, SIGNAL(done(bool)), this, SLOT(done(bool)));
|
Chris@316
|
320 connect(m_ftp, SIGNAL(commandFinished(int, bool)),
|
Chris@316
|
321 this, SLOT(ftpCommandFinished(int, bool)));
|
Chris@316
|
322 connect(m_ftp, SIGNAL(dataTransferProgress(qint64, qint64)),
|
Chris@316
|
323 this, SLOT(dataTransferProgress(qint64, qint64)));
|
Chris@316
|
324 m_ftp->connectToHost(m_url.host(), m_url.port(21));
|
Chris@316
|
325
|
Chris@316
|
326 QString username = m_url.userName();
|
Chris@316
|
327 if (username == "") {
|
Chris@316
|
328 username = "anonymous";
|
Chris@316
|
329 }
|
Chris@316
|
330
|
Chris@316
|
331 QString password = m_url.password();
|
Chris@316
|
332 if (password == "") {
|
Chris@316
|
333 password = QString("%1@%2").arg(getenv("USER")).arg(getenv("HOST"));
|
Chris@316
|
334 }
|
Chris@316
|
335
|
Chris@316
|
336 m_ftp->login(username, password);
|
Chris@316
|
337
|
Chris@316
|
338 QString dirpath = m_url.path().section('/', 0, -2);
|
Chris@316
|
339 QString filename = m_url.path().section('/', -1);
|
Chris@316
|
340
|
Chris@316
|
341 if (dirpath == "") dirpath = "/";
|
Chris@316
|
342 m_ftp->cd(dirpath);
|
Chris@316
|
343 m_ftp->get(filename, m_localFile);
|
Chris@211
|
344 }
|
Chris@211
|
345
|
Chris@211
|
346 void
|
Chris@317
|
347 FileSource::cleanup()
|
Chris@211
|
348 {
|
Chris@211
|
349 m_done = true;
|
Chris@214
|
350 if (m_http) {
|
Chris@287
|
351 QHttp *h = m_http;
|
Chris@214
|
352 m_http = 0;
|
Chris@287
|
353 h->abort();
|
Chris@287
|
354 h->deleteLater();
|
Chris@214
|
355 }
|
Chris@214
|
356 if (m_ftp) {
|
Chris@287
|
357 QFtp *f = m_ftp;
|
Chris@214
|
358 m_ftp = 0;
|
Chris@287
|
359 f->abort();
|
Chris@287
|
360 f->deleteLater();
|
Chris@214
|
361 }
|
Chris@211
|
362 delete m_progressDialog;
|
Chris@211
|
363 m_progressDialog = 0;
|
Chris@304
|
364 delete m_localFile; // does not actually delete the file
|
Chris@211
|
365 m_localFile = 0;
|
Chris@208
|
366 }
|
Chris@208
|
367
|
Chris@208
|
368 bool
|
Chris@317
|
369 FileSource::isRemote(QString fileOrUrl)
|
Chris@304
|
370 {
|
Chris@316
|
371 QString scheme = QUrl(fileOrUrl).scheme().toLower();
|
Chris@316
|
372 return (scheme == "http" || scheme == "ftp");
|
Chris@304
|
373 }
|
Chris@304
|
374
|
Chris@304
|
375 bool
|
Chris@317
|
376 FileSource::canHandleScheme(QUrl url)
|
Chris@208
|
377 {
|
Chris@208
|
378 QString scheme = url.scheme().toLower();
|
Chris@316
|
379 return (scheme == "http" || scheme == "ftp" ||
|
Chris@316
|
380 scheme == "file" || scheme == "");
|
Chris@208
|
381 }
|
Chris@208
|
382
|
Chris@210
|
383 bool
|
Chris@317
|
384 FileSource::isAvailable()
|
Chris@210
|
385 {
|
Chris@316
|
386 waitForStatus();
|
Chris@211
|
387 bool available = true;
|
Chris@211
|
388 if (!m_ok) available = false;
|
Chris@211
|
389 else available = (m_lastStatus / 100 == 2);
|
Chris@317
|
390 std::cerr << "FileSource::isAvailable: " << (available ? "yes" : "no")
|
Chris@211
|
391 << std::endl;
|
Chris@211
|
392 return available;
|
Chris@210
|
393 }
|
Chris@210
|
394
|
Chris@208
|
395 void
|
Chris@317
|
396 FileSource::waitForStatus()
|
Chris@316
|
397 {
|
Chris@316
|
398 while (m_ok && (!m_done && m_lastStatus == 0)) {
|
Chris@316
|
399 // std::cerr << "waitForStatus: processing (last status " << m_lastStatus << ")" << std::endl;
|
Chris@316
|
400 QApplication::processEvents();
|
Chris@316
|
401 }
|
Chris@316
|
402 }
|
Chris@316
|
403
|
Chris@316
|
404 void
|
Chris@317
|
405 FileSource::waitForData()
|
Chris@208
|
406 {
|
Chris@211
|
407 while (m_ok && !m_done) {
|
Chris@208
|
408 QApplication::processEvents();
|
Chris@208
|
409 }
|
Chris@208
|
410 }
|
Chris@208
|
411
|
Chris@316
|
412 void
|
Chris@317
|
413 FileSource::setLeaveLocalFile(bool leave)
|
Chris@316
|
414 {
|
Chris@316
|
415 m_leaveLocalFile = leave;
|
Chris@316
|
416 }
|
Chris@316
|
417
|
Chris@208
|
418 bool
|
Chris@317
|
419 FileSource::isOK() const
|
Chris@208
|
420 {
|
Chris@208
|
421 return m_ok;
|
Chris@208
|
422 }
|
Chris@208
|
423
|
Chris@208
|
424 bool
|
Chris@317
|
425 FileSource::isDone() const
|
Chris@208
|
426 {
|
Chris@208
|
427 return m_done;
|
Chris@208
|
428 }
|
Chris@208
|
429
|
Chris@316
|
430 bool
|
Chris@317
|
431 FileSource::isRemote() const
|
Chris@316
|
432 {
|
Chris@316
|
433 return m_remote;
|
Chris@316
|
434 }
|
Chris@316
|
435
|
Chris@316
|
436 QString
|
Chris@317
|
437 FileSource::getLocation() const
|
Chris@316
|
438 {
|
Chris@316
|
439 return m_url.toString();
|
Chris@316
|
440 }
|
Chris@316
|
441
|
Chris@208
|
442 QString
|
Chris@317
|
443 FileSource::getLocalFilename() const
|
Chris@208
|
444 {
|
Chris@208
|
445 return m_localFilename;
|
Chris@208
|
446 }
|
Chris@208
|
447
|
Chris@208
|
448 QString
|
Chris@317
|
449 FileSource::getContentType() const
|
Chris@316
|
450 {
|
Chris@316
|
451 return m_contentType;
|
Chris@316
|
452 }
|
Chris@316
|
453
|
Chris@316
|
454 QString
|
Chris@317
|
455 FileSource::getExtension() const
|
Chris@316
|
456 {
|
Chris@316
|
457 if (m_localFilename != "") {
|
Chris@316
|
458 return QFileInfo(m_localFilename).suffix().toLower();
|
Chris@316
|
459 } else {
|
Chris@316
|
460 return QFileInfo(m_url.toLocalFile()).suffix().toLower();
|
Chris@316
|
461 }
|
Chris@316
|
462 }
|
Chris@316
|
463
|
Chris@316
|
464 QString
|
Chris@317
|
465 FileSource::getErrorString() const
|
Chris@208
|
466 {
|
Chris@208
|
467 return m_errorString;
|
Chris@208
|
468 }
|
Chris@208
|
469
|
Chris@208
|
470 void
|
Chris@317
|
471 FileSource::dataReadProgress(int done, int total)
|
Chris@208
|
472 {
|
Chris@208
|
473 dataTransferProgress(done, total);
|
Chris@208
|
474 }
|
Chris@208
|
475
|
Chris@208
|
476 void
|
Chris@317
|
477 FileSource::httpResponseHeaderReceived(const QHttpResponseHeader &resp)
|
Chris@210
|
478 {
|
Chris@210
|
479 m_lastStatus = resp.statusCode();
|
Chris@210
|
480 if (m_lastStatus / 100 >= 4) {
|
Chris@210
|
481 m_errorString = QString("%1 %2")
|
Chris@210
|
482 .arg(resp.statusCode()).arg(resp.reasonPhrase());
|
Chris@317
|
483 std::cerr << "FileSource::responseHeaderReceived: "
|
Chris@211
|
484 << m_errorString.toStdString() << std::endl;
|
Chris@211
|
485 } else {
|
Chris@317
|
486 std::cerr << "FileSource::responseHeaderReceived: "
|
Chris@211
|
487 << m_lastStatus << std::endl;
|
Chris@315
|
488 if (resp.hasContentType()) m_contentType = resp.contentType();
|
Chris@325
|
489 }
|
Chris@325
|
490 emit statusAvailable();
|
Chris@210
|
491 }
|
Chris@210
|
492
|
Chris@210
|
493 void
|
Chris@317
|
494 FileSource::ftpCommandFinished(int id, bool error)
|
Chris@214
|
495 {
|
Chris@317
|
496 std::cerr << "FileSource::ftpCommandFinished(" << id << ", " << error << ")" << std::endl;
|
Chris@214
|
497
|
Chris@214
|
498 if (!m_ftp) return;
|
Chris@214
|
499
|
Chris@214
|
500 QFtp::Command command = m_ftp->currentCommand();
|
Chris@214
|
501
|
Chris@214
|
502 if (!error) {
|
Chris@317
|
503 std::cerr << "FileSource::ftpCommandFinished: success for command "
|
Chris@214
|
504 << command << std::endl;
|
Chris@214
|
505 return;
|
Chris@214
|
506 }
|
Chris@214
|
507
|
Chris@214
|
508 if (command == QFtp::ConnectToHost) {
|
Chris@214
|
509 m_errorString = tr("Failed to connect to FTP server");
|
Chris@214
|
510 } else if (command == QFtp::Login) {
|
Chris@214
|
511 m_errorString = tr("Login failed");
|
Chris@214
|
512 } else if (command == QFtp::Cd) {
|
Chris@214
|
513 m_errorString = tr("Failed to change to correct directory");
|
Chris@214
|
514 } else if (command == QFtp::Get) {
|
Chris@214
|
515 m_errorString = tr("FTP download aborted");
|
Chris@214
|
516 }
|
Chris@214
|
517
|
Chris@214
|
518 m_lastStatus = 400; // for done()
|
Chris@214
|
519 }
|
Chris@214
|
520
|
Chris@214
|
521 void
|
Chris@317
|
522 FileSource::dataTransferProgress(qint64 done, qint64 total)
|
Chris@208
|
523 {
|
Chris@211
|
524 if (!m_progressDialog) return;
|
Chris@211
|
525
|
Chris@208
|
526 int percent = int((double(done) / double(total)) * 100.0 - 0.1);
|
Chris@208
|
527 emit progress(percent);
|
Chris@208
|
528
|
Chris@211
|
529 if (percent > 0) {
|
Chris@211
|
530 m_progressDialog->setValue(percent);
|
Chris@211
|
531 m_progressDialog->show();
|
Chris@211
|
532 }
|
Chris@210
|
533 }
|
Chris@210
|
534
|
Chris@210
|
535 void
|
Chris@317
|
536 FileSource::cancelled()
|
Chris@210
|
537 {
|
Chris@210
|
538 m_done = true;
|
Chris@316
|
539 cleanup();
|
Chris@316
|
540
|
Chris@210
|
541 m_ok = false;
|
Chris@210
|
542 m_errorString = tr("Download cancelled");
|
Chris@208
|
543 }
|
Chris@208
|
544
|
Chris@208
|
545 void
|
Chris@317
|
546 FileSource::done(bool error)
|
Chris@208
|
547 {
|
Chris@317
|
548 std::cerr << "FileSource::done(" << error << ")" << std::endl;
|
Chris@211
|
549
|
Chris@211
|
550 if (m_done) return;
|
Chris@211
|
551
|
Chris@208
|
552 emit progress(100);
|
Chris@210
|
553
|
Chris@208
|
554 if (error) {
|
Chris@208
|
555 if (m_http) {
|
Chris@208
|
556 m_errorString = m_http->errorString();
|
Chris@208
|
557 } else if (m_ftp) {
|
Chris@208
|
558 m_errorString = m_ftp->errorString();
|
Chris@208
|
559 }
|
Chris@208
|
560 }
|
Chris@208
|
561
|
Chris@210
|
562 if (m_lastStatus / 100 >= 4) {
|
Chris@211
|
563 error = true;
|
Chris@210
|
564 }
|
Chris@210
|
565
|
Chris@211
|
566 cleanup();
|
Chris@208
|
567
|
Chris@211
|
568 if (!error) {
|
Chris@208
|
569 QFileInfo fi(m_localFilename);
|
Chris@208
|
570 if (!fi.exists()) {
|
Chris@208
|
571 m_errorString = tr("Failed to create local file %1").arg(m_localFilename);
|
Chris@211
|
572 error = true;
|
Chris@208
|
573 } else if (fi.size() == 0) {
|
Chris@208
|
574 m_errorString = tr("File contains no data!");
|
Chris@211
|
575 error = true;
|
Chris@208
|
576 }
|
Chris@208
|
577 }
|
Chris@211
|
578
|
Chris@211
|
579 if (error) {
|
Chris@317
|
580 std::cerr << "FileSource::done: error is " << error << ", deleting cache file" << std::endl;
|
Chris@316
|
581 deleteCacheFile();
|
Chris@211
|
582 }
|
Chris@211
|
583
|
Chris@211
|
584 m_ok = !error;
|
Chris@211
|
585 m_done = true;
|
Chris@304
|
586 emit ready();
|
Chris@211
|
587 }
|
Chris@211
|
588
|
Chris@211
|
589 void
|
Chris@317
|
590 FileSource::deleteCacheFile()
|
Chris@211
|
591 {
|
Chris@317
|
592 std::cerr << "FileSource::deleteCacheFile(\"" << m_localFilename.toStdString() << "\")" << std::endl;
|
Chris@211
|
593
|
Chris@211
|
594 cleanup();
|
Chris@211
|
595
|
Chris@316
|
596 if (m_localFilename == "") {
|
Chris@316
|
597 return;
|
Chris@316
|
598 }
|
Chris@211
|
599
|
Chris@316
|
600 if (!isRemote()) {
|
Chris@316
|
601 std::cerr << "not a cache file" << std::endl;
|
Chris@316
|
602 return;
|
Chris@316
|
603 }
|
Chris@316
|
604
|
Chris@316
|
605 if (m_refCounted) {
|
Chris@304
|
606
|
Chris@304
|
607 QMutexLocker locker(&m_mapMutex);
|
Chris@316
|
608 m_refCounted = false;
|
Chris@304
|
609
|
Chris@304
|
610 if (m_refCountMap[m_url] > 0) {
|
Chris@304
|
611 m_refCountMap[m_url]--;
|
Chris@316
|
612 std::cerr << "reduced ref count to " << m_refCountMap[m_url] << std::endl;
|
Chris@304
|
613 if (m_refCountMap[m_url] > 0) {
|
Chris@304
|
614 m_done = true;
|
Chris@304
|
615 return;
|
Chris@304
|
616 }
|
Chris@304
|
617 }
|
Chris@304
|
618 }
|
Chris@304
|
619
|
Chris@211
|
620 m_fileCreationMutex.lock();
|
Chris@211
|
621
|
Chris@211
|
622 if (!QFile(m_localFilename).remove()) {
|
Chris@317
|
623 std::cerr << "FileSource::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
|
Chris@211
|
624 } else {
|
Chris@317
|
625 std::cerr << "FileSource::deleteCacheFile: Deleted cache file \"" << m_localFilename.toStdString() << "\"" << std::endl;
|
Chris@211
|
626 m_localFilename = "";
|
Chris@211
|
627 }
|
Chris@211
|
628
|
Chris@211
|
629 m_fileCreationMutex.unlock();
|
Chris@211
|
630
|
Chris@208
|
631 m_done = true;
|
Chris@208
|
632 }
|
Chris@208
|
633
|
Chris@210
|
634 void
|
Chris@317
|
635 FileSource::showProgressDialog()
|
Chris@210
|
636 {
|
Chris@210
|
637 if (m_progressDialog) m_progressDialog->show();
|
Chris@210
|
638 }
|
Chris@210
|
639
|
Chris@316
|
640 bool
|
Chris@317
|
641 FileSource::createCacheFile()
|
Chris@208
|
642 {
|
Chris@316
|
643 {
|
Chris@316
|
644 QMutexLocker locker(&m_mapMutex);
|
Chris@316
|
645
|
Chris@317
|
646 std::cerr << "FileSource::createCacheFile: refcount is " << m_refCountMap[m_url] << std::endl;
|
Chris@316
|
647
|
Chris@316
|
648 if (m_refCountMap[m_url] > 0) {
|
Chris@316
|
649 m_refCountMap[m_url]++;
|
Chris@316
|
650 m_localFilename = m_remoteLocalMap[m_url];
|
Chris@316
|
651 std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl;
|
Chris@316
|
652 m_refCounted = true;
|
Chris@316
|
653 return true;
|
Chris@316
|
654 }
|
Chris@316
|
655 }
|
Chris@316
|
656
|
Chris@208
|
657 QDir dir;
|
Chris@208
|
658 try {
|
Chris@208
|
659 dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
|
Chris@208
|
660 } catch (DirectoryCreationFailed f) {
|
Chris@317
|
661 std::cerr << "FileSource::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
|
Chris@208
|
662 return "";
|
Chris@208
|
663 }
|
Chris@208
|
664
|
Chris@316
|
665 QString filepart = m_url.path().section('/', -1, -1,
|
Chris@316
|
666 QString::SectionSkipEmpty);
|
Chris@208
|
667
|
Chris@208
|
668 QString extension = filepart.section('.', -1);
|
Chris@208
|
669 QString base = filepart;
|
Chris@208
|
670 if (extension != "") {
|
Chris@208
|
671 base = base.left(base.length() - extension.length() - 1);
|
Chris@208
|
672 }
|
Chris@208
|
673 if (base == "") base = "remote";
|
Chris@208
|
674
|
Chris@208
|
675 QString filename;
|
Chris@208
|
676
|
Chris@208
|
677 if (extension == "") {
|
Chris@208
|
678 filename = base;
|
Chris@208
|
679 } else {
|
Chris@208
|
680 filename = QString("%1.%2").arg(base).arg(extension);
|
Chris@208
|
681 }
|
Chris@208
|
682
|
Chris@208
|
683 QString filepath(dir.filePath(filename));
|
Chris@208
|
684
|
Chris@317
|
685 std::cerr << "FileSource::createCacheFile: URL is \"" << m_url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl;
|
Chris@208
|
686
|
Chris@316
|
687 QMutexLocker fcLocker(&m_fileCreationMutex);
|
Chris@316
|
688
|
Chris@208
|
689 ++m_count;
|
Chris@208
|
690
|
Chris@208
|
691 if (QFileInfo(filepath).exists() ||
|
Chris@208
|
692 !QFile(filepath).open(QFile::WriteOnly)) {
|
Chris@208
|
693
|
Chris@317
|
694 std::cerr << "FileSource::createCacheFile: Failed to create local file \""
|
Chris@208
|
695 << filepath.toStdString() << "\" for URL \""
|
Chris@316
|
696 << m_url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
|
Chris@208
|
697
|
Chris@208
|
698
|
Chris@208
|
699 if (extension == "") {
|
Chris@208
|
700 filename = QString("%1_%2").arg(base).arg(m_count);
|
Chris@208
|
701 } else {
|
Chris@208
|
702 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
|
Chris@208
|
703 }
|
Chris@208
|
704 filepath = dir.filePath(filename);
|
Chris@208
|
705
|
Chris@208
|
706 if (QFileInfo(filepath).exists() ||
|
Chris@208
|
707 !QFile(filepath).open(QFile::WriteOnly)) {
|
Chris@208
|
708
|
Chris@317
|
709 std::cerr << "FileSource::createCacheFile: ERROR: Failed to create local file \""
|
Chris@208
|
710 << filepath.toStdString() << "\" for URL \""
|
Chris@316
|
711 << m_url.toString().toStdString() << "\" (or file already exists)" << std::endl;
|
Chris@208
|
712
|
Chris@208
|
713 return "";
|
Chris@208
|
714 }
|
Chris@208
|
715 }
|
Chris@208
|
716
|
Chris@317
|
717 std::cerr << "FileSource::createCacheFile: url "
|
Chris@316
|
718 << m_url.toString().toStdString() << " -> local filename "
|
Chris@316
|
719 << filepath.toStdString() << std::endl;
|
Chris@316
|
720
|
Chris@316
|
721 m_localFilename = filepath;
|
Chris@208
|
722
|
Chris@316
|
723 return false;
|
Chris@208
|
724 }
|