svcore  1.9
FileSource.cpp
Go to the documentation of this file.
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 "FileSource.h"
17 
18 #include "base/TempDirectory.h"
19 #include "base/Exceptions.h"
20 #include "base/ProgressReporter.h"
21 #include "system/System.h"
22 
23 #include <QNetworkAccessManager>
24 #include <QNetworkReply>
25 #include <QFileInfo>
26 #include <QDir>
27 #include <QCoreApplication>
28 #include <QThreadStorage>
29 
30 #include <iostream>
31 #include <cstdlib>
32 
33 #include <unistd.h>
34 
35 //#define DEBUG_FILE_SOURCE 1
36 
37 int
39 
40 QMutex
42 
45 
48 
49 QMutex
51 
52 #ifdef DEBUG_FILE_SOURCE
53 static int extantCount = 0;
54 static std::map<QString, int> urlExtantCountMap;
55 static void incCount(QString url) {
56  ++extantCount;
57  if (urlExtantCountMap.find(url) == urlExtantCountMap.end()) {
58  urlExtantCountMap[url] = 1;
59  } else {
60  ++urlExtantCountMap[url];
61  }
62  cerr << "FileSource: Now " << urlExtantCountMap[url] << " for this url, " << extantCount << " total" << endl;
63 }
64 static void decCount(QString url) {
65  --extantCount;
66  --urlExtantCountMap[url];
67  cerr << "FileSource: Now " << urlExtantCountMap[url] << " for this url, " << extantCount << " total" << endl;
68 }
69 #endif
70 
71 static QThreadStorage<QNetworkAccessManager *> nms;
72 
73 FileSource::FileSource(QString fileOrUrl, ProgressReporter *reporter,
74  QString preferredContentType) :
75  m_rawFileOrUrl(fileOrUrl),
76  m_url(fileOrUrl, QUrl::StrictMode),
77  m_localFile(0),
78  m_reply(0),
79  m_preferredContentType(preferredContentType),
80  m_ok(false),
81  m_cancelled(false),
82  m_lastStatus(0),
83  m_resource(fileOrUrl.startsWith(':')),
84  m_remote(isRemote(fileOrUrl)),
85  m_done(false),
86  m_leaveLocalFile(false),
87  m_reporter(reporter),
88  m_refCounted(false)
89 {
90  if (m_resource) { // qrc file
91  m_url = QUrl("qrc" + fileOrUrl);
92  }
93 
94  if (m_url.toString() == "") {
95  m_url = QUrl(fileOrUrl, QUrl::TolerantMode);
96  }
97 
98 #ifdef DEBUG_FILE_SOURCE
99  cerr << "FileSource::FileSource(" << fileOrUrl << "): url <" << m_url.toString() << ">" << endl;
100  incCount(m_url.toString());
101 #endif
102 
103  if (!canHandleScheme(m_url)) {
104  cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString() << "\"" << endl;
105  m_errorString = tr("Unsupported scheme in URL");
106  return;
107  }
108 
109  init();
110 
111  if (!isRemote() &&
112  !isAvailable()) {
113 #ifdef DEBUG_FILE_SOURCE
114  cerr << "FileSource::FileSource: Failed to open local file with URL \"" << m_url.toString() << "\"; trying again assuming filename was encoded" << endl;
115 #endif
116  m_url = QUrl::fromEncoded(fileOrUrl.toLatin1());
117 #ifdef DEBUG_FILE_SOURCE
118  cerr << "FileSource::FileSource: URL is now \"" << m_url.toString() << "\"" << endl;
119 #endif
120  init();
121  }
122 
123  if (isRemote() &&
124  (fileOrUrl.contains('%') ||
125  fileOrUrl.contains("--"))) { // for IDNA
126 
127  waitForStatus();
128 
129  if (!isAvailable()) {
130 
131  // The URL was created on the assumption that the string
132  // was human-readable. Let's try again, this time
133  // assuming it was already encoded.
134  cerr << "FileSource::FileSource: Failed to retrieve URL \""
135  << fileOrUrl
136  << "\" as human-readable URL; "
137  << "trying again treating it as encoded URL"
138  << endl;
139 
140  // even though our cache file doesn't exist (because the
141  // resource was 404), we still need to ensure we're no
142  // longer associating a filename with this url in the
143  // refcount map -- or createCacheFile will think we've
144  // already done all the work and no request will be sent
145  deleteCacheFile();
146 
147  m_url = QUrl::fromEncoded(fileOrUrl.toLatin1());
148 
149  m_ok = false;
150  m_done = false;
151  m_lastStatus = 0;
152  init();
153  }
154  }
155 
156  if (!isRemote()) {
157  emit statusAvailable();
158  emit ready();
159  }
160 
161 #ifdef DEBUG_FILE_SOURCE
162  cerr << "FileSource::FileSource(string) exiting" << endl;
163 #endif
164 }
165 
167  m_url(url),
168  m_localFile(0),
169  m_reply(0),
170  m_ok(false),
171  m_cancelled(false),
172  m_lastStatus(0),
173  m_resource(false),
174  m_remote(isRemote(url.toString())),
175  m_done(false),
176  m_leaveLocalFile(false),
177  m_reporter(reporter),
178  m_refCounted(false)
179 {
180 #ifdef DEBUG_FILE_SOURCE
181  cerr << "FileSource::FileSource(" << url.toString() << ") [as url]" << endl;
182  incCount(m_url.toString());
183 #endif
184 
185  if (!canHandleScheme(m_url)) {
186  cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString() << "\"" << endl;
187  m_errorString = tr("Unsupported scheme in URL");
188  return;
189  }
190 
191  init();
192 
193 #ifdef DEBUG_FILE_SOURCE
194  cerr << "FileSource::FileSource(url) exiting" << endl;
195 #endif
196 }
197 
199  QObject(),
200  m_url(rf.m_url),
201  m_localFile(0),
202  m_reply(0),
203  m_ok(rf.m_ok),
204  m_cancelled(rf.m_cancelled),
205  m_lastStatus(rf.m_lastStatus),
206  m_resource(rf.m_resource),
207  m_remote(rf.m_remote),
208  m_done(false),
209  m_leaveLocalFile(false),
210  m_reporter(rf.m_reporter),
211  m_refCounted(false)
212 {
213 #ifdef DEBUG_FILE_SOURCE
214  cerr << "FileSource::FileSource(" << m_url.toString() << ") [copy ctor]" << endl;
215  incCount(m_url.toString());
216 #endif
217 
218  if (!canHandleScheme(m_url)) {
219  cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString() << "\"" << endl;
220  m_errorString = tr("Unsupported scheme in URL");
221  return;
222  }
223 
224  if (!isRemote()) {
226  } else {
227  QMutexLocker locker(&m_mapMutex);
228 #ifdef DEBUG_FILE_SOURCE
229  cerr << "FileSource::FileSource(copy ctor): ref count is "
230  << m_refCountMap[m_url] << endl;
231 #endif
232  if (m_refCountMap[m_url] > 0) {
233  m_refCountMap[m_url]++;
234 #ifdef DEBUG_FILE_SOURCE
235  cerr << "raised it to " << m_refCountMap[m_url] << endl;
236 #endif
238  m_refCounted = true;
239  } else {
240  m_ok = false;
241  m_lastStatus = 404;
242  }
243  }
244 
245  m_done = true;
246 
247 #ifdef DEBUG_FILE_SOURCE
248  cerr << "FileSource::FileSource(" << m_url.toString() << ") [copy ctor]: note: local filename is \"" << m_localFilename << "\"" << endl;
249 #endif
250 
251 #ifdef DEBUG_FILE_SOURCE
252  cerr << "FileSource::FileSource(copy ctor) exiting" << endl;
253 #endif
254 }
255 
257 {
258 #ifdef DEBUG_FILE_SOURCE
259  cerr << "FileSource(" << m_url.toString() << ")::~FileSource" << endl;
260  decCount(m_url.toString());
261 #endif
262 
263  cleanup();
264 
266 }
267 
268 void
270 {
271  { // check we have a QNetworkAccessManager
272  QMutexLocker locker(&m_mapMutex);
273  if (!nms.hasLocalData()) {
274  nms.setLocalData(new QNetworkAccessManager());
275  }
276  }
277 
278  if (isResource()) {
279 #ifdef DEBUG_FILE_SOURCE
280  cerr << "FileSource::init: Is a resource" << endl;
281 #endif
282  QString resourceFile = m_url.toString();
283  resourceFile.replace(QRegExp("^qrc:"), ":");
284 
285  if (!QFileInfo(resourceFile).exists()) {
286 #ifdef DEBUG_FILE_SOURCE
287  cerr << "FileSource::init: Resource file of this name does not exist, switching to non-resource URL" << endl;
288 #endif
289  m_url = resourceFile;
290  m_resource = false;
291  }
292  }
293 
294  if (!isRemote() && !isResource()) {
295 #ifdef DEBUG_FILE_SOURCE
296  cerr << "FileSource::init: Not a remote URL" << endl;
297 #endif
298  bool literal = false;
299  m_localFilename = m_url.toLocalFile();
300 
301  if (m_localFilename == "") {
302  // QUrl may have mishandled the scheme (e.g. in a DOS path)
304 #ifdef DEBUG_FILE_SOURCE
305  cerr << "FileSource::init: Trying literal local filename \""
306  << m_localFilename << "\"" << endl;
307 #endif
308  literal = true;
309  }
310  m_localFilename = QFileInfo(m_localFilename).absoluteFilePath();
311 
312 #ifdef DEBUG_FILE_SOURCE
313  cerr << "FileSource::init: URL translates to absolute filename \""
314  << m_localFilename << "\" (with literal=" << literal << ")"
315  << endl;
316 #endif
317  m_ok = true;
318  m_lastStatus = 200;
319 
320  if (!QFileInfo(m_localFilename).exists()) {
321  if (literal) {
322  m_lastStatus = 404;
323  } else {
324 #ifdef DEBUG_FILE_SOURCE
325  cerr << "FileSource::init: Local file of this name does not exist, trying URL as a literal filename" << endl;
326 #endif
327  // Again, QUrl may have been mistreating us --
328  // e.g. dropping a part that looks like query data
330  literal = true;
331  if (!QFileInfo(m_localFilename).exists()) {
332  m_lastStatus = 404;
333  }
334  }
335  }
336 
337  m_done = true;
338  return;
339  }
340 
341  if (createCacheFile()) {
342 #ifdef DEBUG_FILE_SOURCE
343  cerr << "FileSource::init: Already have this one" << endl;
344 #endif
345  m_ok = true;
346  if (!QFileInfo(m_localFilename).exists()) {
347  m_lastStatus = 404;
348  } else {
349  m_lastStatus = 200;
350  }
351  m_done = true;
352  return;
353  }
354 
355  if (m_localFilename == "") return;
356 
357  m_localFile = new QFile(m_localFilename);
358  m_localFile->open(QFile::WriteOnly);
359 
360  if (isResource()) {
361 
362  // Absent resource file case was dealt with at the top -- this
363  // is the successful case
364 
365  QString resourceFileName = m_url.toString();
366  resourceFileName.replace(QRegExp("^qrc:"), ":");
367  QFile resourceFile(resourceFileName);
368  resourceFile.open(QFile::ReadOnly);
369  QByteArray ba(resourceFile.readAll());
370 
371 #ifdef DEBUG_FILE_SOURCE
372  cerr << "Copying " << ba.size() << " bytes from resource file to cache file" << endl;
373 #endif
374 
375  qint64 written = m_localFile->write(ba);
376  m_localFile->close();
377  delete m_localFile;
378  m_localFile = 0;
379 
380  if (written != ba.size()) {
381 #ifdef DEBUG_FILE_SOURCE
382  cerr << "Copy failed (wrote " << written << " bytes)" << endl;
383 #endif
384  m_ok = false;
385  return;
386  } else {
387  m_ok = true;
388  m_lastStatus = 200;
389  m_done = true;
390  }
391 
392  } else {
393 
394  QString scheme = m_url.scheme().toLower();
395 
396 #ifdef DEBUG_FILE_SOURCE
397  cerr << "FileSource::init: Don't have local copy of \""
398  << m_url.toString() << "\", retrieving" << endl;
399 #endif
400 
401  if (scheme == "http" || scheme == "https" || scheme == "ftp") {
402  initRemote();
403 #ifdef DEBUG_FILE_SOURCE
404  cerr << "FileSource: initRemote returned" << endl;
405 #endif
406  } else {
407  m_remote = false;
408  m_ok = false;
409  }
410  }
411 
412  if (m_ok) {
413 
414  QMutexLocker locker(&m_mapMutex);
415 
416  if (m_refCountMap[m_url] > 0) {
417  // someone else has been doing the same thing at the same time,
418  // but has got there first
419  cleanup();
420  m_refCountMap[m_url]++;
421 #ifdef DEBUG_FILE_SOURCE
422  cerr << "FileSource::init: Another FileSource has got there first, abandoning our download and using theirs" << endl;
423 #endif
425  m_refCounted = true;
426  m_ok = true;
427  if (!QFileInfo(m_localFilename).exists()) {
428  m_lastStatus = 404;
429  }
430  m_done = true;
431  return;
432  }
433 
435  m_refCountMap[m_url]++;
436  m_refCounted = true;
437 
438  if (m_reporter && !m_done) {
440  (tr("Downloading %1...").arg(m_url.toString()));
441  connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
442  connect(this, SIGNAL(progress(int)),
443  m_reporter, SLOT(setProgress(int)));
444  }
445  }
446 }
447 
448 void
450 {
451  m_ok = true;
452 
453  QNetworkRequest req;
454  req.setUrl(m_url);
455 
456  if (m_preferredContentType != "") {
457 #ifdef DEBUG_FILE_SOURCE
458  cerr << "FileSource: indicating preferred content type of \""
459  << m_preferredContentType << "\"" << endl;
460 #endif
461  req.setRawHeader
462  ("Accept",
463  QString("%1, */*").arg(m_preferredContentType).toLatin1());
464  }
465 
466  m_reply = nms.localData()->get(req);
467 
468  connect(m_reply, SIGNAL(readyRead()),
469  this, SLOT(readyRead()));
470  connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
471  this, SLOT(replyFailed(QNetworkReply::NetworkError)));
472  connect(m_reply, SIGNAL(finished()),
473  this, SLOT(replyFinished()));
474  connect(m_reply, SIGNAL(metaDataChanged()),
475  this, SLOT(metaDataChanged()));
476  connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)),
477  this, SLOT(downloadProgress(qint64, qint64)));
478 }
479 
480 void
482 {
483  if (m_done) {
484  delete m_localFile; // does not actually delete the file
485  m_localFile = 0;
486  }
487  m_done = true;
488  if (m_reply) {
489  QNetworkReply *r = m_reply;
490  m_reply = 0;
491  // Can only call abort() when there are no errors.
492  if (r->error() == QNetworkReply::NoError) {
493  r->abort();
494  }
495  r->deleteLater();
496  }
497  if (m_localFile) {
498  delete m_localFile; // does not actually delete the file
499  m_localFile = 0;
500  }
501 }
502 
503 bool
504 FileSource::isRemote(QString fileOrUrl)
505 {
506  // Note that a "scheme" with length 1 is probably a DOS drive letter
507  QString scheme = QUrl(fileOrUrl).scheme().toLower();
508  if (scheme == "" || scheme == "file" || scheme.length() == 1) return false;
509  return true;
510 }
511 
512 bool
514 {
515  // Note that a "scheme" with length 1 is probably a DOS drive letter
516  QString scheme = url.scheme().toLower();
517  return (scheme == "http" || scheme == "https" ||
518  scheme == "ftp" || scheme == "file" || scheme == "qrc" ||
519  scheme == "" || scheme.length() == 1);
520 }
521 
522 bool
524 {
525  waitForStatus();
526  bool available = true;
527  if (!m_ok) {
528  available = false;
529  } else {
530  // http 2xx status codes mean success
531  available = (m_lastStatus / 100 == 2);
532  }
533 #ifdef DEBUG_FILE_SOURCE
534  cerr << "FileSource::isAvailable: " << (available ? "yes" : "no") << endl;
535 #endif
536  return available;
537 }
538 
539 void
541 {
542  while (m_ok && (!m_done && m_lastStatus == 0)) {
543 // cerr << "waitForStatus: processing (last status " << m_lastStatus << ")" << endl;
544  QCoreApplication::processEvents();
545  }
546 }
547 
548 void
550 {
551  while (m_ok && !m_done) {
552 // cerr << "FileSource::waitForData: calling QApplication::processEvents" << endl;
553  QCoreApplication::processEvents();
554  usleep(10000);
555  }
556 }
557 
558 void
560 {
561  m_leaveLocalFile = leave;
562 }
563 
564 bool
566 {
567  return m_ok;
568 }
569 
570 bool
572 {
573  return m_done;
574 }
575 
576 bool
578 {
579  return m_cancelled;
580 }
581 
582 bool
584 {
585  return m_resource;
586 }
587 
588 bool
590 {
591  return m_remote;
592 }
593 
594 QString
596 {
597  return m_url.toString();
598 }
599 
600 QString
602 {
603  return m_localFilename;
604 }
605 
606 QString
608 {
609  return QFileInfo(m_localFilename).fileName();
610 }
611 
612 QString
614 {
615  return m_contentType;
616 }
617 
618 QString
620 {
621  if (m_localFilename != "") {
622  return QFileInfo(m_localFilename).suffix().toLower();
623  } else {
624  return QFileInfo(m_url.toLocalFile()).suffix().toLower();
625  }
626 }
627 
628 QString
630 {
631  return m_errorString;
632 }
633 
634 void
636 {
637  m_localFile->write(m_reply->readAll());
638 }
639 
640 void
642 {
643 #ifdef DEBUG_FILE_SOURCE
644  cerr << "FileSource::metaDataChanged" << endl;
645 #endif
646 
647  if (!m_reply) {
648  cerr << "WARNING: FileSource::metaDataChanged() called without a reply object being known to us" << endl;
649  return;
650  }
651 
652  // Handle http transfer status codes.
653 
654  int status =
655  m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
656 
657  // If this is a redirection (3xx) code, do the redirect
658  if (status / 100 == 3) {
659  QString location = m_reply->header
660  (QNetworkRequest::LocationHeader).toString();
661 #ifdef DEBUG_FILE_SOURCE
662  cerr << "FileSource::metaDataChanged: redirect to \""
663  << location << "\" received" << endl;
664 #endif
665  if (location != "") {
666  QUrl newUrl(location);
667  if (newUrl != m_url) {
668  cleanup();
669  deleteCacheFile();
670 #ifdef DEBUG_FILE_SOURCE
671  decCount(m_url.toString());
672  incCount(newUrl.toString());
673 #endif
674  m_url = newUrl;
675  m_localFile = 0;
676  m_lastStatus = 0;
677  m_done = false;
678  m_refCounted = false;
679  init();
680  return;
681  }
682  }
683  }
684 
685  m_lastStatus = status;
686 
687  // 400 and up are failures, get the error string
688  if (m_lastStatus / 100 >= 4) {
689  m_errorString = QString("%1 %2")
690  .arg(status)
691  .arg(m_reply->attribute
692  (QNetworkRequest::HttpReasonPhraseAttribute).toString());
693 #ifdef DEBUG_FILE_SOURCE
694  cerr << "FileSource::metaDataChanged: "
695  << m_errorString << endl;
696 #endif
697  } else {
698 #ifdef DEBUG_FILE_SOURCE
699  cerr << "FileSource::metaDataChanged: "
700  << m_lastStatus << endl;
701 #endif
702  m_contentType =
703  m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
704  }
705  emit statusAvailable();
706 }
707 
708 void
709 FileSource::downloadProgress(qint64 done, qint64 total)
710 {
711  int percent = int((double(done) / double(total)) * 100.0 - 0.1);
712  emit progress(percent);
713 }
714 
715 void
717 {
718  m_done = true;
719  cleanup();
720 
721  m_ok = false;
722  m_cancelled = true;
723  m_errorString = tr("Download cancelled");
724 }
725 
726 void
728 {
729  emit progress(100);
730 
731 #ifdef DEBUG_FILE_SOURCE
732  cerr << "FileSource::replyFinished()" << endl;
733 #endif
734 
735  if (m_done) return;
736 
737  QString scheme = m_url.scheme().toLower();
738  // For ftp transfers, replyFinished() will be called on success.
739  // metaDataChanged() is never called for ftp transfers.
740  if (scheme == "ftp") {
741  m_lastStatus = 200; // http ok
742  }
743 
744  bool error = (m_lastStatus / 100 >= 4);
745 
746  cleanup();
747 
748  if (!error) {
749  QFileInfo fi(m_localFilename);
750  if (!fi.exists()) {
751  m_errorString = tr("Failed to create local file %1").arg(m_localFilename);
752  error = true;
753  } else if (fi.size() == 0) {
754  m_errorString = tr("File contains no data!");
755  error = true;
756  }
757  }
758 
759  if (error) {
760 #ifdef DEBUG_FILE_SOURCE
761  cerr << "FileSource::done: error is " << error << ", deleting cache file" << endl;
762 #endif
763  deleteCacheFile();
764  }
765 
766  m_ok = !error;
767  if (m_localFile) m_localFile->flush();
768  m_done = true;
769  emit ready();
770 }
771 
772 void
773 FileSource::replyFailed(QNetworkReply::NetworkError)
774 {
775  emit progress(100);
776  if (!m_reply) {
777  cerr << "WARNING: FileSource::replyFailed() called without a reply object being known to us" << endl;
778  } else {
779  m_errorString = m_reply->errorString();
780  }
781  m_ok = false;
782  m_done = true;
783  cleanup();
784  emit ready();
785 }
786 
787 void
789 {
790 #ifdef DEBUG_FILE_SOURCE
791  cerr << "FileSource::deleteCacheFile(\"" << m_localFilename << "\")" << endl;
792 #endif
793 
794  cleanup();
795 
796  if (m_localFilename == "") {
797  return;
798  }
799 
800  if (!isRemote()) {
801 #ifdef DEBUG_FILE_SOURCE
802  cerr << "not a cache file" << endl;
803 #endif
804  return;
805  }
806 
807  if (m_refCounted) {
808 
809  QMutexLocker locker(&m_mapMutex);
810  m_refCounted = false;
811 
812  if (m_refCountMap[m_url] > 0) {
813  m_refCountMap[m_url]--;
814 #ifdef DEBUG_FILE_SOURCE
815  cerr << "reduced ref count to " << m_refCountMap[m_url] << endl;
816 #endif
817  if (m_refCountMap[m_url] > 0) {
818  m_done = true;
819  return;
820  }
821  }
822  }
823 
824  m_fileCreationMutex.lock();
825 
826  if (!QFile(m_localFilename).remove()) {
827 #ifdef DEBUG_FILE_SOURCE
828  cerr << "FileSource::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename << "\"" << endl;
829 #endif
830  } else {
831 #ifdef DEBUG_FILE_SOURCE
832  cerr << "FileSource::deleteCacheFile: Deleted cache file \"" << m_localFilename << "\"" << endl;
833 #endif
834  m_localFilename = "";
835  }
836 
837  m_fileCreationMutex.unlock();
838 
839  m_done = true;
840 }
841 
842 bool
844 {
845  {
846  QMutexLocker locker(&m_mapMutex);
847 
848 #ifdef DEBUG_FILE_SOURCE
849  cerr << "FileSource::createCacheFile: refcount is " << m_refCountMap[m_url] << endl;
850 #endif
851 
852  if (m_refCountMap[m_url] > 0) {
853  m_refCountMap[m_url]++;
855 #ifdef DEBUG_FILE_SOURCE
856  cerr << "raised it to " << m_refCountMap[m_url] << endl;
857 #endif
858  m_refCounted = true;
859  return true;
860  }
861  }
862 
863  QDir dir;
864  try {
865  dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
866  } catch (DirectoryCreationFailed f) {
867 #ifdef DEBUG_FILE_SOURCE
868  cerr << "FileSource::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << endl;
869 #endif
870  return false;
871  }
872 
873  QString filepart = m_url.path().section('/', -1, -1,
874  QString::SectionSkipEmpty);
875 
876  QString extension = "";
877  if (filepart.contains('.')) extension = filepart.section('.', -1);
878 
879  QString base = filepart;
880  if (extension != "") {
881  base = base.left(base.length() - extension.length() - 1);
882  }
883  if (base == "") base = "remote";
884 
885  QString filename;
886 
887  if (extension == "") {
888  filename = base;
889  } else {
890  filename = QString("%1.%2").arg(base).arg(extension);
891  }
892 
893  QString filepath(dir.filePath(filename));
894 
895 #ifdef DEBUG_FILE_SOURCE
896  cerr << "FileSource::createCacheFile: URL is \"" << m_url.toString() << "\", dir is \"" << dir.path() << "\", base \"" << base << "\", extension \"" << extension << "\", filebase \"" << filename << "\", filename \"" << filepath << "\"" << endl;
897 #endif
898 
899  QMutexLocker fcLocker(&m_fileCreationMutex);
900 
901  ++m_count;
902 
903  if (QFileInfo(filepath).exists() ||
904  !QFile(filepath).open(QFile::WriteOnly)) {
905 
906 #ifdef DEBUG_FILE_SOURCE
907  cerr << "FileSource::createCacheFile: Failed to create local file \""
908  << filepath << "\" for URL \""
909  << m_url.toString() << "\" (or file already exists): appending suffix instead" << endl;
910 #endif
911 
912  if (extension == "") {
913  filename = QString("%1_%2").arg(base).arg(m_count);
914  } else {
915  filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
916  }
917  filepath = dir.filePath(filename);
918 
919  if (QFileInfo(filepath).exists() ||
920  !QFile(filepath).open(QFile::WriteOnly)) {
921 
922 #ifdef DEBUG_FILE_SOURCE
923  cerr << "FileSource::createCacheFile: ERROR: Failed to create local file \""
924  << filepath << "\" for URL \""
925  << m_url.toString() << "\" (or file already exists)" << endl;
926 #endif
927 
928  return false;
929  }
930  }
931 
932 #ifdef DEBUG_FILE_SOURCE
933  cerr << "FileSource::createCacheFile: url "
934  << m_url.toString() << " -> local filename "
935  << filepath << endl;
936 #endif
937 
938  m_localFilename = filepath;
939 
940  return false;
941 }
942 
int m_lastStatus
Definition: FileSource.h:239
QString getExtension() const
Return the file extension for this file, if any.
Definition: FileSource.cpp:619
static QThreadStorage< QNetworkAccessManager * > nms
Definition: FileSource.cpp:71
QString m_contentType
Definition: FileSource.h:235
void setLeaveLocalFile(bool leave)
Specify whether any local, cached file should remain on disc after this FileSource has been destroyed...
Definition: FileSource.cpp:559
bool isResource() const
Return true if this FileSource is referring to a QRC resource.
Definition: FileSource.cpp:583
static int m_count
Definition: FileSource.h:264
void metaDataChanged()
Definition: FileSource.cpp:641
QString getLocation() const
Return the location filename or URL as passed to the constructor.
Definition: FileSource.cpp:595
void downloadProgress(qint64 done, qint64 total)
Definition: FileSource.cpp:709
FileSource(QString fileOrUrl, ProgressReporter *reporter=0, QString preferredContentType="")
Construct a FileSource using the given local file path or URL.
Definition: FileSource.cpp:73
QFile * m_localFile
Definition: FileSource.h:231
bool isDone() const
Return true if the entire file has been retrieved and is available.
Definition: FileSource.cpp:571
void statusAvailable()
Emitted when the file's existence has been tested and/or response header received.
bool m_done
Definition: FileSource.h:242
static QMutex m_fileCreationMutex
Definition: FileSource.h:263
QString m_preferredContentType
Definition: FileSource.h:236
void cleanup()
Definition: FileSource.cpp:481
void cancelled()
Definition: FileSource.cpp:716
static RemoteLocalMap m_remoteLocalMap
Definition: FileSource.h:249
void waitForData()
Block on a sub-event-loop until the whole of the data has been retrieved (if it is remote).
Definition: FileSource.cpp:549
QNetworkReply * m_reply
Definition: FileSource.h:232
void progress(int percent)
Emitted during URL retrieval, when the retrieval progress notches up to a new percentage.
void init()
Definition: FileSource.cpp:269
void waitForStatus()
Block on a sub-event-loop until the availability of the file or remote URL is known.
Definition: FileSource.cpp:540
void replyFinished()
Definition: FileSource.cpp:727
bool isOK() const
Return true if the FileSource object is valid and neither error nor cancellation occurred while retri...
Definition: FileSource.cpp:565
virtual ~FileSource()
Definition: FileSource.cpp:256
bool m_refCounted
Definition: FileSource.h:251
static TempDirectory * getInstance()
bool m_leaveLocalFile
Definition: FileSource.h:243
void initRemote()
Definition: FileSource.cpp:449
std::map< QUrl, QString > RemoteLocalMap
Definition: FileSource.h:247
bool m_remote
Definition: FileSource.h:241
virtual const char * what() const
Definition: Exceptions.cpp:58
void replyFailed(QNetworkReply::NetworkError)
Definition: FileSource.cpp:773
ProgressReporter * m_reporter
Definition: FileSource.h:244
FileSource is a class used to refer to the contents of a file that may be either local or at a remote...
Definition: FileSource.h:59
QString m_errorString
Definition: FileSource.h:234
std::map< QUrl, int > RemoteRefCountMap
Definition: FileSource.h:246
bool isRemote() const
Return true if this FileSource is referring to a remote URL.
Definition: FileSource.cpp:589
QString getErrorString() const
Return an error message, if isOK() is false.
Definition: FileSource.cpp:629
static QMutex m_mapMutex
Definition: FileSource.h:250
QString getLocalFilename() const
Return the name of the local file this FileSource refers to.
Definition: FileSource.cpp:601
QString getBasename() const
Return the base name, i.e.
Definition: FileSource.cpp:607
static RemoteRefCountMap m_refCountMap
Definition: FileSource.h:248
bool wasCancelled() const
Return true if the operation was cancelled by the user through the ProgressReporter interface.
Definition: FileSource.cpp:577
QUrl m_url
Definition: FileSource.h:230
QString getSubDirectoryPath(QString subdir)
Create an immediate subdirectory of the root temporary directory of the given name,...
void readyRead()
Definition: FileSource.cpp:635
bool m_resource
Definition: FileSource.h:240
void ready()
Emitted when the entire file data has been retrieved and the local file is complete (if no error has ...
QString m_localFilename
Definition: FileSource.h:233
static bool canHandleScheme(QUrl url)
Return true if FileSource can handle the retrieval scheme for the given URL (or if the URL is for a l...
Definition: FileSource.cpp:513
bool m_cancelled
Definition: FileSource.h:238
QString getContentType() const
Return the MIME content type of this file, if known.
Definition: FileSource.cpp:613
virtual void setMessage(QString text)=0
QString m_rawFileOrUrl
Definition: FileSource.h:229
bool createCacheFile()
Definition: FileSource.cpp:843
void deleteCacheFile()
Definition: FileSource.cpp:788
bool isAvailable()
Return true if the file or remote URL exists.
Definition: FileSource.cpp:523