23 #include <QNetworkAccessManager> 24 #include <QNetworkReply> 27 #include <QCoreApplication> 28 #include <QThreadStorage> 52 #ifdef DEBUG_FILE_SOURCE 53 static int extantCount = 0;
54 static std::map<QString, int> urlExtantCountMap;
55 static void incCount(QString url) {
57 if (urlExtantCountMap.find(url) == urlExtantCountMap.end()) {
58 urlExtantCountMap[url] = 1;
60 ++urlExtantCountMap[url];
62 cerr <<
"FileSource: Now " << urlExtantCountMap[url] <<
" for this url, " << extantCount <<
" total" << endl;
64 static void decCount(QString url) {
66 --urlExtantCountMap[url];
67 cerr <<
"FileSource: Now " << urlExtantCountMap[url] <<
" for this url, " << extantCount <<
" total" << endl;
71 static QThreadStorage<QNetworkAccessManager *>
nms;
74 QString preferredContentType) :
75 m_rawFileOrUrl(fileOrUrl),
76 m_url(fileOrUrl, QUrl::StrictMode),
79 m_preferredContentType(preferredContentType),
83 m_resource(fileOrUrl.startsWith(
':')),
84 m_remote(isRemote(fileOrUrl)),
86 m_leaveLocalFile(false),
91 m_url = QUrl(
"qrc" + fileOrUrl);
94 if (
m_url.toString() ==
"") {
95 m_url = QUrl(fileOrUrl, QUrl::TolerantMode);
98 #ifdef DEBUG_FILE_SOURCE 99 cerr <<
"FileSource::FileSource(" << fileOrUrl <<
"): url <" <<
m_url.toString() <<
">" << endl;
100 incCount(
m_url.toString());
104 cerr <<
"FileSource::FileSource: ERROR: Unsupported scheme in URL \"" <<
m_url.toString() <<
"\"" << endl;
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;
116 m_url = QUrl::fromEncoded(fileOrUrl.toLatin1());
117 #ifdef DEBUG_FILE_SOURCE 118 cerr <<
"FileSource::FileSource: URL is now \"" <<
m_url.toString() <<
"\"" << endl;
124 (fileOrUrl.contains(
'%') ||
125 fileOrUrl.contains(
"--"))) {
134 cerr <<
"FileSource::FileSource: Failed to retrieve URL \"" 136 <<
"\" as human-readable URL; " 137 <<
"trying again treating it as encoded URL" 147 m_url = QUrl::fromEncoded(fileOrUrl.toLatin1());
161 #ifdef DEBUG_FILE_SOURCE 162 cerr <<
"FileSource::FileSource(string) exiting" << endl;
174 m_remote(isRemote(url.toString())),
176 m_leaveLocalFile(false),
177 m_reporter(reporter),
180 #ifdef DEBUG_FILE_SOURCE 181 cerr <<
"FileSource::FileSource(" << url.toString() <<
") [as url]" << endl;
182 incCount(
m_url.toString());
186 cerr <<
"FileSource::FileSource: ERROR: Unsupported scheme in URL \"" <<
m_url.toString() <<
"\"" << endl;
193 #ifdef DEBUG_FILE_SOURCE 194 cerr <<
"FileSource::FileSource(url) exiting" << endl;
204 m_cancelled(rf.m_cancelled),
205 m_lastStatus(rf.m_lastStatus),
206 m_resource(rf.m_resource),
207 m_remote(rf.m_remote),
209 m_leaveLocalFile(false),
210 m_reporter(rf.m_reporter),
213 #ifdef DEBUG_FILE_SOURCE 214 cerr <<
"FileSource::FileSource(" <<
m_url.toString() <<
") [copy ctor]" << endl;
215 incCount(
m_url.toString());
219 cerr <<
"FileSource::FileSource: ERROR: Unsupported scheme in URL \"" <<
m_url.toString() <<
"\"" << endl;
228 #ifdef DEBUG_FILE_SOURCE 229 cerr <<
"FileSource::FileSource(copy ctor): ref count is " 234 #ifdef DEBUG_FILE_SOURCE 247 #ifdef DEBUG_FILE_SOURCE 248 cerr <<
"FileSource::FileSource(" <<
m_url.toString() <<
") [copy ctor]: note: local filename is \"" <<
m_localFilename <<
"\"" << endl;
251 #ifdef DEBUG_FILE_SOURCE 252 cerr <<
"FileSource::FileSource(copy ctor) exiting" << endl;
258 #ifdef DEBUG_FILE_SOURCE 259 cerr <<
"FileSource(" <<
m_url.toString() <<
")::~FileSource" << endl;
260 decCount(
m_url.toString());
273 if (!
nms.hasLocalData()) {
274 nms.setLocalData(
new QNetworkAccessManager());
279 #ifdef DEBUG_FILE_SOURCE 280 cerr <<
"FileSource::init: Is a resource" << endl;
282 QString resourceFile =
m_url.toString();
283 resourceFile.replace(QRegExp(
"^qrc:"),
":");
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;
289 m_url = resourceFile;
295 #ifdef DEBUG_FILE_SOURCE 296 cerr <<
"FileSource::init: Not a remote URL" << endl;
298 bool literal =
false;
304 #ifdef DEBUG_FILE_SOURCE 305 cerr <<
"FileSource::init: Trying literal local filename \"" 312 #ifdef DEBUG_FILE_SOURCE 313 cerr <<
"FileSource::init: URL translates to absolute filename \"" 324 #ifdef DEBUG_FILE_SOURCE 325 cerr <<
"FileSource::init: Local file of this name does not exist, trying URL as a literal filename" << endl;
342 #ifdef DEBUG_FILE_SOURCE 343 cerr <<
"FileSource::init: Already have this one" << endl;
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());
371 #ifdef DEBUG_FILE_SOURCE 372 cerr <<
"Copying " << ba.size() <<
" bytes from resource file to cache file" << endl;
380 if (written != ba.size()) {
381 #ifdef DEBUG_FILE_SOURCE 382 cerr <<
"Copy failed (wrote " << written <<
" bytes)" << endl;
394 QString scheme =
m_url.scheme().toLower();
396 #ifdef DEBUG_FILE_SOURCE 397 cerr <<
"FileSource::init: Don't have local copy of \"" 398 <<
m_url.toString() <<
"\", retrieving" << endl;
401 if (scheme ==
"http" || scheme ==
"https" || scheme ==
"ftp") {
403 #ifdef DEBUG_FILE_SOURCE 404 cerr <<
"FileSource: initRemote returned" << endl;
421 #ifdef DEBUG_FILE_SOURCE 422 cerr <<
"FileSource::init: Another FileSource has got there first, abandoning our download and using theirs" << endl;
440 (tr(
"Downloading %1...").arg(
m_url.toString()));
442 connect(
this, SIGNAL(
progress(
int)),
457 #ifdef DEBUG_FILE_SOURCE 458 cerr <<
"FileSource: indicating preferred content type of \"" 470 connect(
m_reply, SIGNAL(error(QNetworkReply::NetworkError)),
471 this, SLOT(
replyFailed(QNetworkReply::NetworkError)));
472 connect(
m_reply, SIGNAL(finished()),
492 if (r->error() == QNetworkReply::NoError) {
507 QString scheme = QUrl(fileOrUrl).scheme().toLower();
508 if (scheme ==
"" || scheme ==
"file" || scheme.length() == 1)
return false;
516 QString scheme = url.scheme().toLower();
517 return (scheme ==
"http" || scheme ==
"https" ||
518 scheme ==
"ftp" || scheme ==
"file" || scheme ==
"qrc" ||
519 scheme ==
"" || scheme.length() == 1);
526 bool available =
true;
533 #ifdef DEBUG_FILE_SOURCE 534 cerr <<
"FileSource::isAvailable: " << (available ?
"yes" :
"no") << endl;
544 QCoreApplication::processEvents();
553 QCoreApplication::processEvents();
597 return m_url.toString();
624 return QFileInfo(
m_url.toLocalFile()).suffix().toLower();
643 #ifdef DEBUG_FILE_SOURCE 644 cerr <<
"FileSource::metaDataChanged" << endl;
648 cerr <<
"WARNING: FileSource::metaDataChanged() called without a reply object being known to us" << endl;
655 m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
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;
665 if (location !=
"") {
666 QUrl newUrl(location);
667 if (newUrl !=
m_url) {
670 #ifdef DEBUG_FILE_SOURCE 671 decCount(
m_url.toString());
672 incCount(newUrl.toString());
692 (QNetworkRequest::HttpReasonPhraseAttribute).toString());
693 #ifdef DEBUG_FILE_SOURCE 694 cerr <<
"FileSource::metaDataChanged: " 698 #ifdef DEBUG_FILE_SOURCE 699 cerr <<
"FileSource::metaDataChanged: " 703 m_reply->header(QNetworkRequest::ContentTypeHeader).toString();
711 int percent = int((
double(done) /
double(total)) * 100.0 - 0.1);
731 #ifdef DEBUG_FILE_SOURCE 732 cerr <<
"FileSource::replyFinished()" << endl;
737 QString scheme =
m_url.scheme().toLower();
740 if (scheme ==
"ftp") {
753 }
else if (fi.size() == 0) {
760 #ifdef DEBUG_FILE_SOURCE 761 cerr <<
"FileSource::done: error is " << error <<
", deleting cache file" << endl;
777 cerr <<
"WARNING: FileSource::replyFailed() called without a reply object being known to us" << endl;
790 #ifdef DEBUG_FILE_SOURCE 791 cerr <<
"FileSource::deleteCacheFile(\"" <<
m_localFilename <<
"\")" << endl;
801 #ifdef DEBUG_FILE_SOURCE 802 cerr <<
"not a cache file" << endl;
814 #ifdef DEBUG_FILE_SOURCE 827 #ifdef DEBUG_FILE_SOURCE 828 cerr <<
"FileSource::deleteCacheFile: ERROR: Failed to delete file \"" <<
m_localFilename <<
"\"" << endl;
831 #ifdef DEBUG_FILE_SOURCE 832 cerr <<
"FileSource::deleteCacheFile: Deleted cache file \"" <<
m_localFilename <<
"\"" << endl;
848 #ifdef DEBUG_FILE_SOURCE 855 #ifdef DEBUG_FILE_SOURCE 867 #ifdef DEBUG_FILE_SOURCE 868 cerr <<
"FileSource::createCacheFile: ERROR: Failed to create temporary directory: " << f.
what() << endl;
873 QString filepart =
m_url.path().section(
'/', -1, -1,
874 QString::SectionSkipEmpty);
876 QString extension =
"";
877 if (filepart.contains(
'.')) extension = filepart.section(
'.', -1);
879 QString base = filepart;
880 if (extension !=
"") {
881 base = base.left(base.length() - extension.length() - 1);
883 if (base ==
"") base =
"remote";
887 if (extension ==
"") {
890 filename = QString(
"%1.%2").arg(base).arg(extension);
893 QString filepath(dir.filePath(filename));
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;
903 if (QFileInfo(filepath).exists() ||
904 !QFile(filepath).open(QFile::WriteOnly)) {
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;
912 if (extension ==
"") {
913 filename = QString(
"%1_%2").arg(base).arg(
m_count);
915 filename = QString(
"%1_%2.%3").arg(base).arg(
m_count).arg(extension);
917 filepath = dir.filePath(filename);
919 if (QFileInfo(filepath).exists() ||
920 !QFile(filepath).open(QFile::WriteOnly)) {
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;
932 #ifdef DEBUG_FILE_SOURCE 933 cerr <<
"FileSource::createCacheFile: url " 934 <<
m_url.toString() <<
" -> local filename "
QString getExtension() const
Return the file extension for this file, if any.
static QThreadStorage< QNetworkAccessManager * > nms
void setLeaveLocalFile(bool leave)
Specify whether any local, cached file should remain on disc after this FileSource has been destroyed...
bool isResource() const
Return true if this FileSource is referring to a QRC resource.
QString getLocation() const
Return the location filename or URL as passed to the constructor.
void downloadProgress(qint64 done, qint64 total)
FileSource(QString fileOrUrl, ProgressReporter *reporter=0, QString preferredContentType="")
Construct a FileSource using the given local file path or URL.
bool isDone() const
Return true if the entire file has been retrieved and is available.
void statusAvailable()
Emitted when the file's existence has been tested and/or response header received.
static QMutex m_fileCreationMutex
QString m_preferredContentType
static RemoteLocalMap m_remoteLocalMap
void waitForData()
Block on a sub-event-loop until the whole of the data has been retrieved (if it is remote).
void progress(int percent)
Emitted during URL retrieval, when the retrieval progress notches up to a new percentage.
void waitForStatus()
Block on a sub-event-loop until the availability of the file or remote URL is known.
bool isOK() const
Return true if the FileSource object is valid and neither error nor cancellation occurred while retri...
static TempDirectory * getInstance()
std::map< QUrl, QString > RemoteLocalMap
virtual const char * what() const
void replyFailed(QNetworkReply::NetworkError)
ProgressReporter * m_reporter
FileSource is a class used to refer to the contents of a file that may be either local or at a remote...
std::map< QUrl, int > RemoteRefCountMap
bool isRemote() const
Return true if this FileSource is referring to a remote URL.
QString getErrorString() const
Return an error message, if isOK() is false.
QString getLocalFilename() const
Return the name of the local file this FileSource refers to.
QString getBasename() const
Return the base name, i.e.
static RemoteRefCountMap m_refCountMap
bool wasCancelled() const
Return true if the operation was cancelled by the user through the ProgressReporter interface.
QString getSubDirectoryPath(QString subdir)
Create an immediate subdirectory of the root temporary directory of the given name,...
void ready()
Emitted when the entire file data has been retrieved and the local file is complete (if no error has ...
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...
QString getContentType() const
Return the MIME content type of this file, if known.
virtual void setMessage(QString text)=0
bool isAvailable()
Return true if the file or remote URL exists.