svcore
1.9
|
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 00002 00003 /* 00004 Sonic Visualiser 00005 An audio file viewer and annotation editor. 00006 Centre for Digital Music, Queen Mary, University of London. 00007 This file copyright 2007 QMUL. 00008 00009 This program is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU General Public License as 00011 published by the Free Software Foundation; either version 2 of the 00012 License, or (at your option) any later version. See the file 00013 COPYING included with this distribution for more information. 00014 */ 00015 00016 #include "FileSource.h" 00017 00018 #include "base/TempDirectory.h" 00019 #include "base/Exceptions.h" 00020 #include "base/ProgressReporter.h" 00021 #include "system/System.h" 00022 00023 #include <QNetworkAccessManager> 00024 #include <QNetworkReply> 00025 #include <QFileInfo> 00026 #include <QDir> 00027 #include <QCoreApplication> 00028 #include <QThreadStorage> 00029 00030 #include <iostream> 00031 #include <cstdlib> 00032 00033 #include <unistd.h> 00034 00035 //#define DEBUG_FILE_SOURCE 1 00036 00037 int 00038 FileSource::m_count = 0; 00039 00040 QMutex 00041 FileSource::m_fileCreationMutex; 00042 00043 FileSource::RemoteRefCountMap 00044 FileSource::m_refCountMap; 00045 00046 FileSource::RemoteLocalMap 00047 FileSource::m_remoteLocalMap; 00048 00049 QMutex 00050 FileSource::m_mapMutex; 00051 00052 #ifdef DEBUG_FILE_SOURCE 00053 static int extantCount = 0; 00054 static std::map<QString, int> urlExtantCountMap; 00055 static void incCount(QString url) { 00056 ++extantCount; 00057 if (urlExtantCountMap.find(url) == urlExtantCountMap.end()) { 00058 urlExtantCountMap[url] = 1; 00059 } else { 00060 ++urlExtantCountMap[url]; 00061 } 00062 cerr << "FileSource: Now " << urlExtantCountMap[url] << " for this url, " << extantCount << " total" << endl; 00063 } 00064 static void decCount(QString url) { 00065 --extantCount; 00066 --urlExtantCountMap[url]; 00067 cerr << "FileSource: Now " << urlExtantCountMap[url] << " for this url, " << extantCount << " total" << endl; 00068 } 00069 #endif 00070 00071 static QThreadStorage<QNetworkAccessManager *> nms; 00072 00073 FileSource::FileSource(QString fileOrUrl, ProgressReporter *reporter, 00074 QString preferredContentType) : 00075 m_rawFileOrUrl(fileOrUrl), 00076 m_url(fileOrUrl, QUrl::StrictMode), 00077 m_localFile(0), 00078 m_reply(0), 00079 m_preferredContentType(preferredContentType), 00080 m_ok(false), 00081 m_cancelled(false), 00082 m_lastStatus(0), 00083 m_resource(fileOrUrl.startsWith(':')), 00084 m_remote(isRemote(fileOrUrl)), 00085 m_done(false), 00086 m_leaveLocalFile(false), 00087 m_reporter(reporter), 00088 m_refCounted(false) 00089 { 00090 if (m_resource) { // qrc file 00091 m_url = QUrl("qrc" + fileOrUrl); 00092 } 00093 00094 if (m_url.toString() == "") { 00095 m_url = QUrl(fileOrUrl, QUrl::TolerantMode); 00096 } 00097 00098 #ifdef DEBUG_FILE_SOURCE 00099 cerr << "FileSource::FileSource(" << fileOrUrl << "): url <" << m_url.toString() << ">" << endl; 00100 incCount(m_url.toString()); 00101 #endif 00102 00103 if (!canHandleScheme(m_url)) { 00104 cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString() << "\"" << endl; 00105 m_errorString = tr("Unsupported scheme in URL"); 00106 return; 00107 } 00108 00109 init(); 00110 00111 if (!isRemote() && 00112 !isAvailable()) { 00113 #ifdef DEBUG_FILE_SOURCE 00114 cerr << "FileSource::FileSource: Failed to open local file with URL \"" << m_url.toString() << "\"; trying again assuming filename was encoded" << endl; 00115 #endif 00116 m_url = QUrl::fromEncoded(fileOrUrl.toLatin1()); 00117 #ifdef DEBUG_FILE_SOURCE 00118 cerr << "FileSource::FileSource: URL is now \"" << m_url.toString() << "\"" << endl; 00119 #endif 00120 init(); 00121 } 00122 00123 if (isRemote() && 00124 (fileOrUrl.contains('%') || 00125 fileOrUrl.contains("--"))) { // for IDNA 00126 00127 waitForStatus(); 00128 00129 if (!isAvailable()) { 00130 00131 // The URL was created on the assumption that the string 00132 // was human-readable. Let's try again, this time 00133 // assuming it was already encoded. 00134 cerr << "FileSource::FileSource: Failed to retrieve URL \"" 00135 << fileOrUrl 00136 << "\" as human-readable URL; " 00137 << "trying again treating it as encoded URL" 00138 << endl; 00139 00140 // even though our cache file doesn't exist (because the 00141 // resource was 404), we still need to ensure we're no 00142 // longer associating a filename with this url in the 00143 // refcount map -- or createCacheFile will think we've 00144 // already done all the work and no request will be sent 00145 deleteCacheFile(); 00146 00147 m_url = QUrl::fromEncoded(fileOrUrl.toLatin1()); 00148 00149 m_ok = false; 00150 m_done = false; 00151 m_lastStatus = 0; 00152 init(); 00153 } 00154 } 00155 00156 if (!isRemote()) { 00157 emit statusAvailable(); 00158 emit ready(); 00159 } 00160 00161 #ifdef DEBUG_FILE_SOURCE 00162 cerr << "FileSource::FileSource(string) exiting" << endl; 00163 #endif 00164 } 00165 00166 FileSource::FileSource(QUrl url, ProgressReporter *reporter) : 00167 m_url(url), 00168 m_localFile(0), 00169 m_reply(0), 00170 m_ok(false), 00171 m_cancelled(false), 00172 m_lastStatus(0), 00173 m_resource(false), 00174 m_remote(isRemote(url.toString())), 00175 m_done(false), 00176 m_leaveLocalFile(false), 00177 m_reporter(reporter), 00178 m_refCounted(false) 00179 { 00180 #ifdef DEBUG_FILE_SOURCE 00181 cerr << "FileSource::FileSource(" << url.toString() << ") [as url]" << endl; 00182 incCount(m_url.toString()); 00183 #endif 00184 00185 if (!canHandleScheme(m_url)) { 00186 cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString() << "\"" << endl; 00187 m_errorString = tr("Unsupported scheme in URL"); 00188 return; 00189 } 00190 00191 init(); 00192 00193 #ifdef DEBUG_FILE_SOURCE 00194 cerr << "FileSource::FileSource(url) exiting" << endl; 00195 #endif 00196 } 00197 00198 FileSource::FileSource(const FileSource &rf) : 00199 QObject(), 00200 m_url(rf.m_url), 00201 m_localFile(0), 00202 m_reply(0), 00203 m_ok(rf.m_ok), 00204 m_cancelled(rf.m_cancelled), 00205 m_lastStatus(rf.m_lastStatus), 00206 m_resource(rf.m_resource), 00207 m_remote(rf.m_remote), 00208 m_done(false), 00209 m_leaveLocalFile(false), 00210 m_reporter(rf.m_reporter), 00211 m_refCounted(false) 00212 { 00213 #ifdef DEBUG_FILE_SOURCE 00214 cerr << "FileSource::FileSource(" << m_url.toString() << ") [copy ctor]" << endl; 00215 incCount(m_url.toString()); 00216 #endif 00217 00218 if (!canHandleScheme(m_url)) { 00219 cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString() << "\"" << endl; 00220 m_errorString = tr("Unsupported scheme in URL"); 00221 return; 00222 } 00223 00224 if (!isRemote()) { 00225 m_localFilename = rf.m_localFilename; 00226 } else { 00227 QMutexLocker locker(&m_mapMutex); 00228 #ifdef DEBUG_FILE_SOURCE 00229 cerr << "FileSource::FileSource(copy ctor): ref count is " 00230 << m_refCountMap[m_url] << endl; 00231 #endif 00232 if (m_refCountMap[m_url] > 0) { 00233 m_refCountMap[m_url]++; 00234 #ifdef DEBUG_FILE_SOURCE 00235 cerr << "raised it to " << m_refCountMap[m_url] << endl; 00236 #endif 00237 m_localFilename = m_remoteLocalMap[m_url]; 00238 m_refCounted = true; 00239 } else { 00240 m_ok = false; 00241 m_lastStatus = 404; 00242 } 00243 } 00244 00245 m_done = true; 00246 00247 #ifdef DEBUG_FILE_SOURCE 00248 cerr << "FileSource::FileSource(" << m_url.toString() << ") [copy ctor]: note: local filename is \"" << m_localFilename << "\"" << endl; 00249 #endif 00250 00251 #ifdef DEBUG_FILE_SOURCE 00252 cerr << "FileSource::FileSource(copy ctor) exiting" << endl; 00253 #endif 00254 } 00255 00256 FileSource::~FileSource() 00257 { 00258 #ifdef DEBUG_FILE_SOURCE 00259 cerr << "FileSource(" << m_url.toString() << ")::~FileSource" << endl; 00260 decCount(m_url.toString()); 00261 #endif 00262 00263 cleanup(); 00264 00265 if (isRemote() && !m_leaveLocalFile) deleteCacheFile(); 00266 } 00267 00268 void 00269 FileSource::init() 00270 { 00271 { // check we have a QNetworkAccessManager 00272 QMutexLocker locker(&m_mapMutex); 00273 if (!nms.hasLocalData()) { 00274 nms.setLocalData(new QNetworkAccessManager()); 00275 } 00276 } 00277 00278 if (isResource()) { 00279 #ifdef DEBUG_FILE_SOURCE 00280 cerr << "FileSource::init: Is a resource" << endl; 00281 #endif 00282 QString resourceFile = m_url.toString(); 00283 resourceFile.replace(QRegExp("^qrc:"), ":"); 00284 00285 if (!QFileInfo(resourceFile).exists()) { 00286 #ifdef DEBUG_FILE_SOURCE 00287 cerr << "FileSource::init: Resource file of this name does not exist, switching to non-resource URL" << endl; 00288 #endif 00289 m_url = resourceFile; 00290 m_resource = false; 00291 } 00292 } 00293 00294 if (!isRemote() && !isResource()) { 00295 #ifdef DEBUG_FILE_SOURCE 00296 cerr << "FileSource::init: Not a remote URL" << endl; 00297 #endif 00298 bool literal = false; 00299 m_localFilename = m_url.toLocalFile(); 00300 00301 if (m_localFilename == "") { 00302 // QUrl may have mishandled the scheme (e.g. in a DOS path) 00303 m_localFilename = m_rawFileOrUrl; 00304 #ifdef DEBUG_FILE_SOURCE 00305 cerr << "FileSource::init: Trying literal local filename \"" 00306 << m_localFilename << "\"" << endl; 00307 #endif 00308 literal = true; 00309 } 00310 m_localFilename = QFileInfo(m_localFilename).absoluteFilePath(); 00311 00312 #ifdef DEBUG_FILE_SOURCE 00313 cerr << "FileSource::init: URL translates to absolute filename \"" 00314 << m_localFilename << "\" (with literal=" << literal << ")" 00315 << endl; 00316 #endif 00317 m_ok = true; 00318 m_lastStatus = 200; 00319 00320 if (!QFileInfo(m_localFilename).exists()) { 00321 if (literal) { 00322 m_lastStatus = 404; 00323 } else { 00324 #ifdef DEBUG_FILE_SOURCE 00325 cerr << "FileSource::init: Local file of this name does not exist, trying URL as a literal filename" << endl; 00326 #endif 00327 // Again, QUrl may have been mistreating us -- 00328 // e.g. dropping a part that looks like query data 00329 m_localFilename = m_rawFileOrUrl; 00330 literal = true; 00331 if (!QFileInfo(m_localFilename).exists()) { 00332 m_lastStatus = 404; 00333 } 00334 } 00335 } 00336 00337 m_done = true; 00338 return; 00339 } 00340 00341 if (createCacheFile()) { 00342 #ifdef DEBUG_FILE_SOURCE 00343 cerr << "FileSource::init: Already have this one" << endl; 00344 #endif 00345 m_ok = true; 00346 if (!QFileInfo(m_localFilename).exists()) { 00347 m_lastStatus = 404; 00348 } else { 00349 m_lastStatus = 200; 00350 } 00351 m_done = true; 00352 return; 00353 } 00354 00355 if (m_localFilename == "") return; 00356 00357 m_localFile = new QFile(m_localFilename); 00358 m_localFile->open(QFile::WriteOnly); 00359 00360 if (isResource()) { 00361 00362 // Absent resource file case was dealt with at the top -- this 00363 // is the successful case 00364 00365 QString resourceFileName = m_url.toString(); 00366 resourceFileName.replace(QRegExp("^qrc:"), ":"); 00367 QFile resourceFile(resourceFileName); 00368 resourceFile.open(QFile::ReadOnly); 00369 QByteArray ba(resourceFile.readAll()); 00370 00371 #ifdef DEBUG_FILE_SOURCE 00372 cerr << "Copying " << ba.size() << " bytes from resource file to cache file" << endl; 00373 #endif 00374 00375 qint64 written = m_localFile->write(ba); 00376 m_localFile->close(); 00377 delete m_localFile; 00378 m_localFile = 0; 00379 00380 if (written != ba.size()) { 00381 #ifdef DEBUG_FILE_SOURCE 00382 cerr << "Copy failed (wrote " << written << " bytes)" << endl; 00383 #endif 00384 m_ok = false; 00385 return; 00386 } else { 00387 m_ok = true; 00388 m_lastStatus = 200; 00389 m_done = true; 00390 } 00391 00392 } else { 00393 00394 QString scheme = m_url.scheme().toLower(); 00395 00396 #ifdef DEBUG_FILE_SOURCE 00397 cerr << "FileSource::init: Don't have local copy of \"" 00398 << m_url.toString() << "\", retrieving" << endl; 00399 #endif 00400 00401 if (scheme == "http" || scheme == "https" || scheme == "ftp") { 00402 initRemote(); 00403 #ifdef DEBUG_FILE_SOURCE 00404 cerr << "FileSource: initRemote returned" << endl; 00405 #endif 00406 } else { 00407 m_remote = false; 00408 m_ok = false; 00409 } 00410 } 00411 00412 if (m_ok) { 00413 00414 QMutexLocker locker(&m_mapMutex); 00415 00416 if (m_refCountMap[m_url] > 0) { 00417 // someone else has been doing the same thing at the same time, 00418 // but has got there first 00419 cleanup(); 00420 m_refCountMap[m_url]++; 00421 #ifdef DEBUG_FILE_SOURCE 00422 cerr << "FileSource::init: Another FileSource has got there first, abandoning our download and using theirs" << endl; 00423 #endif 00424 m_localFilename = m_remoteLocalMap[m_url]; 00425 m_refCounted = true; 00426 m_ok = true; 00427 if (!QFileInfo(m_localFilename).exists()) { 00428 m_lastStatus = 404; 00429 } 00430 m_done = true; 00431 return; 00432 } 00433 00434 m_remoteLocalMap[m_url] = m_localFilename; 00435 m_refCountMap[m_url]++; 00436 m_refCounted = true; 00437 00438 if (m_reporter && !m_done) { 00439 m_reporter->setMessage 00440 (tr("Downloading %1...").arg(m_url.toString())); 00441 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); 00442 connect(this, SIGNAL(progress(int)), 00443 m_reporter, SLOT(setProgress(int))); 00444 } 00445 } 00446 } 00447 00448 void 00449 FileSource::initRemote() 00450 { 00451 m_ok = true; 00452 00453 QNetworkRequest req; 00454 req.setUrl(m_url); 00455 00456 if (m_preferredContentType != "") { 00457 #ifdef DEBUG_FILE_SOURCE 00458 cerr << "FileSource: indicating preferred content type of \"" 00459 << m_preferredContentType << "\"" << endl; 00460 #endif 00461 req.setRawHeader 00462 ("Accept", 00463 QString("%1, */*").arg(m_preferredContentType).toLatin1()); 00464 } 00465 00466 m_reply = nms.localData()->get(req); 00467 00468 connect(m_reply, SIGNAL(readyRead()), 00469 this, SLOT(readyRead())); 00470 connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError)), 00471 this, SLOT(replyFailed(QNetworkReply::NetworkError))); 00472 connect(m_reply, SIGNAL(finished()), 00473 this, SLOT(replyFinished())); 00474 connect(m_reply, SIGNAL(metaDataChanged()), 00475 this, SLOT(metaDataChanged())); 00476 connect(m_reply, SIGNAL(downloadProgress(qint64, qint64)), 00477 this, SLOT(downloadProgress(qint64, qint64))); 00478 } 00479 00480 void 00481 FileSource::cleanup() 00482 { 00483 if (m_done) { 00484 delete m_localFile; // does not actually delete the file 00485 m_localFile = 0; 00486 } 00487 m_done = true; 00488 if (m_reply) { 00489 QNetworkReply *r = m_reply; 00490 m_reply = 0; 00491 // Can only call abort() when there are no errors. 00492 if (r->error() == QNetworkReply::NoError) { 00493 r->abort(); 00494 } 00495 r->deleteLater(); 00496 } 00497 if (m_localFile) { 00498 delete m_localFile; // does not actually delete the file 00499 m_localFile = 0; 00500 } 00501 } 00502 00503 bool 00504 FileSource::isRemote(QString fileOrUrl) 00505 { 00506 // Note that a "scheme" with length 1 is probably a DOS drive letter 00507 QString scheme = QUrl(fileOrUrl).scheme().toLower(); 00508 if (scheme == "" || scheme == "file" || scheme.length() == 1) return false; 00509 return true; 00510 } 00511 00512 bool 00513 FileSource::canHandleScheme(QUrl url) 00514 { 00515 // Note that a "scheme" with length 1 is probably a DOS drive letter 00516 QString scheme = url.scheme().toLower(); 00517 return (scheme == "http" || scheme == "https" || 00518 scheme == "ftp" || scheme == "file" || scheme == "qrc" || 00519 scheme == "" || scheme.length() == 1); 00520 } 00521 00522 bool 00523 FileSource::isAvailable() 00524 { 00525 waitForStatus(); 00526 bool available = true; 00527 if (!m_ok) { 00528 available = false; 00529 } else { 00530 // http 2xx status codes mean success 00531 available = (m_lastStatus / 100 == 2); 00532 } 00533 #ifdef DEBUG_FILE_SOURCE 00534 cerr << "FileSource::isAvailable: " << (available ? "yes" : "no") << endl; 00535 #endif 00536 return available; 00537 } 00538 00539 void 00540 FileSource::waitForStatus() 00541 { 00542 while (m_ok && (!m_done && m_lastStatus == 0)) { 00543 // cerr << "waitForStatus: processing (last status " << m_lastStatus << ")" << endl; 00544 QCoreApplication::processEvents(); 00545 } 00546 } 00547 00548 void 00549 FileSource::waitForData() 00550 { 00551 while (m_ok && !m_done) { 00552 // cerr << "FileSource::waitForData: calling QApplication::processEvents" << endl; 00553 QCoreApplication::processEvents(); 00554 usleep(10000); 00555 } 00556 } 00557 00558 void 00559 FileSource::setLeaveLocalFile(bool leave) 00560 { 00561 m_leaveLocalFile = leave; 00562 } 00563 00564 bool 00565 FileSource::isOK() const 00566 { 00567 return m_ok; 00568 } 00569 00570 bool 00571 FileSource::isDone() const 00572 { 00573 return m_done; 00574 } 00575 00576 bool 00577 FileSource::wasCancelled() const 00578 { 00579 return m_cancelled; 00580 } 00581 00582 bool 00583 FileSource::isResource() const 00584 { 00585 return m_resource; 00586 } 00587 00588 bool 00589 FileSource::isRemote() const 00590 { 00591 return m_remote; 00592 } 00593 00594 QString 00595 FileSource::getLocation() const 00596 { 00597 return m_url.toString(); 00598 } 00599 00600 QString 00601 FileSource::getLocalFilename() const 00602 { 00603 return m_localFilename; 00604 } 00605 00606 QString 00607 FileSource::getBasename() const 00608 { 00609 return QFileInfo(m_localFilename).fileName(); 00610 } 00611 00612 QString 00613 FileSource::getContentType() const 00614 { 00615 return m_contentType; 00616 } 00617 00618 QString 00619 FileSource::getExtension() const 00620 { 00621 if (m_localFilename != "") { 00622 return QFileInfo(m_localFilename).suffix().toLower(); 00623 } else { 00624 return QFileInfo(m_url.toLocalFile()).suffix().toLower(); 00625 } 00626 } 00627 00628 QString 00629 FileSource::getErrorString() const 00630 { 00631 return m_errorString; 00632 } 00633 00634 void 00635 FileSource::readyRead() 00636 { 00637 m_localFile->write(m_reply->readAll()); 00638 } 00639 00640 void 00641 FileSource::metaDataChanged() 00642 { 00643 #ifdef DEBUG_FILE_SOURCE 00644 cerr << "FileSource::metaDataChanged" << endl; 00645 #endif 00646 00647 if (!m_reply) { 00648 cerr << "WARNING: FileSource::metaDataChanged() called without a reply object being known to us" << endl; 00649 return; 00650 } 00651 00652 // Handle http transfer status codes. 00653 00654 int status = 00655 m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); 00656 00657 // If this is a redirection (3xx) code, do the redirect 00658 if (status / 100 == 3) { 00659 QString location = m_reply->header 00660 (QNetworkRequest::LocationHeader).toString(); 00661 #ifdef DEBUG_FILE_SOURCE 00662 cerr << "FileSource::metaDataChanged: redirect to \"" 00663 << location << "\" received" << endl; 00664 #endif 00665 if (location != "") { 00666 QUrl newUrl(location); 00667 if (newUrl != m_url) { 00668 cleanup(); 00669 deleteCacheFile(); 00670 #ifdef DEBUG_FILE_SOURCE 00671 decCount(m_url.toString()); 00672 incCount(newUrl.toString()); 00673 #endif 00674 m_url = newUrl; 00675 m_localFile = 0; 00676 m_lastStatus = 0; 00677 m_done = false; 00678 m_refCounted = false; 00679 init(); 00680 return; 00681 } 00682 } 00683 } 00684 00685 m_lastStatus = status; 00686 00687 // 400 and up are failures, get the error string 00688 if (m_lastStatus / 100 >= 4) { 00689 m_errorString = QString("%1 %2") 00690 .arg(status) 00691 .arg(m_reply->attribute 00692 (QNetworkRequest::HttpReasonPhraseAttribute).toString()); 00693 #ifdef DEBUG_FILE_SOURCE 00694 cerr << "FileSource::metaDataChanged: " 00695 << m_errorString << endl; 00696 #endif 00697 } else { 00698 #ifdef DEBUG_FILE_SOURCE 00699 cerr << "FileSource::metaDataChanged: " 00700 << m_lastStatus << endl; 00701 #endif 00702 m_contentType = 00703 m_reply->header(QNetworkRequest::ContentTypeHeader).toString(); 00704 } 00705 emit statusAvailable(); 00706 } 00707 00708 void 00709 FileSource::downloadProgress(qint64 done, qint64 total) 00710 { 00711 int percent = int((double(done) / double(total)) * 100.0 - 0.1); 00712 emit progress(percent); 00713 } 00714 00715 void 00716 FileSource::cancelled() 00717 { 00718 m_done = true; 00719 cleanup(); 00720 00721 m_ok = false; 00722 m_cancelled = true; 00723 m_errorString = tr("Download cancelled"); 00724 } 00725 00726 void 00727 FileSource::replyFinished() 00728 { 00729 emit progress(100); 00730 00731 #ifdef DEBUG_FILE_SOURCE 00732 cerr << "FileSource::replyFinished()" << endl; 00733 #endif 00734 00735 if (m_done) return; 00736 00737 QString scheme = m_url.scheme().toLower(); 00738 // For ftp transfers, replyFinished() will be called on success. 00739 // metaDataChanged() is never called for ftp transfers. 00740 if (scheme == "ftp") { 00741 m_lastStatus = 200; // http ok 00742 } 00743 00744 bool error = (m_lastStatus / 100 >= 4); 00745 00746 cleanup(); 00747 00748 if (!error) { 00749 QFileInfo fi(m_localFilename); 00750 if (!fi.exists()) { 00751 m_errorString = tr("Failed to create local file %1").arg(m_localFilename); 00752 error = true; 00753 } else if (fi.size() == 0) { 00754 m_errorString = tr("File contains no data!"); 00755 error = true; 00756 } 00757 } 00758 00759 if (error) { 00760 #ifdef DEBUG_FILE_SOURCE 00761 cerr << "FileSource::done: error is " << error << ", deleting cache file" << endl; 00762 #endif 00763 deleteCacheFile(); 00764 } 00765 00766 m_ok = !error; 00767 if (m_localFile) m_localFile->flush(); 00768 m_done = true; 00769 emit ready(); 00770 } 00771 00772 void 00773 FileSource::replyFailed(QNetworkReply::NetworkError) 00774 { 00775 emit progress(100); 00776 if (!m_reply) { 00777 cerr << "WARNING: FileSource::replyFailed() called without a reply object being known to us" << endl; 00778 } else { 00779 m_errorString = m_reply->errorString(); 00780 } 00781 m_ok = false; 00782 m_done = true; 00783 cleanup(); 00784 emit ready(); 00785 } 00786 00787 void 00788 FileSource::deleteCacheFile() 00789 { 00790 #ifdef DEBUG_FILE_SOURCE 00791 cerr << "FileSource::deleteCacheFile(\"" << m_localFilename << "\")" << endl; 00792 #endif 00793 00794 cleanup(); 00795 00796 if (m_localFilename == "") { 00797 return; 00798 } 00799 00800 if (!isRemote()) { 00801 #ifdef DEBUG_FILE_SOURCE 00802 cerr << "not a cache file" << endl; 00803 #endif 00804 return; 00805 } 00806 00807 if (m_refCounted) { 00808 00809 QMutexLocker locker(&m_mapMutex); 00810 m_refCounted = false; 00811 00812 if (m_refCountMap[m_url] > 0) { 00813 m_refCountMap[m_url]--; 00814 #ifdef DEBUG_FILE_SOURCE 00815 cerr << "reduced ref count to " << m_refCountMap[m_url] << endl; 00816 #endif 00817 if (m_refCountMap[m_url] > 0) { 00818 m_done = true; 00819 return; 00820 } 00821 } 00822 } 00823 00824 m_fileCreationMutex.lock(); 00825 00826 if (!QFile(m_localFilename).remove()) { 00827 #ifdef DEBUG_FILE_SOURCE 00828 cerr << "FileSource::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename << "\"" << endl; 00829 #endif 00830 } else { 00831 #ifdef DEBUG_FILE_SOURCE 00832 cerr << "FileSource::deleteCacheFile: Deleted cache file \"" << m_localFilename << "\"" << endl; 00833 #endif 00834 m_localFilename = ""; 00835 } 00836 00837 m_fileCreationMutex.unlock(); 00838 00839 m_done = true; 00840 } 00841 00842 bool 00843 FileSource::createCacheFile() 00844 { 00845 { 00846 QMutexLocker locker(&m_mapMutex); 00847 00848 #ifdef DEBUG_FILE_SOURCE 00849 cerr << "FileSource::createCacheFile: refcount is " << m_refCountMap[m_url] << endl; 00850 #endif 00851 00852 if (m_refCountMap[m_url] > 0) { 00853 m_refCountMap[m_url]++; 00854 m_localFilename = m_remoteLocalMap[m_url]; 00855 #ifdef DEBUG_FILE_SOURCE 00856 cerr << "raised it to " << m_refCountMap[m_url] << endl; 00857 #endif 00858 m_refCounted = true; 00859 return true; 00860 } 00861 } 00862 00863 QDir dir; 00864 try { 00865 dir = TempDirectory::getInstance()->getSubDirectoryPath("download"); 00866 } catch (DirectoryCreationFailed f) { 00867 #ifdef DEBUG_FILE_SOURCE 00868 cerr << "FileSource::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << endl; 00869 #endif 00870 return false; 00871 } 00872 00873 QString filepart = m_url.path().section('/', -1, -1, 00874 QString::SectionSkipEmpty); 00875 00876 QString extension = ""; 00877 if (filepart.contains('.')) extension = filepart.section('.', -1); 00878 00879 QString base = filepart; 00880 if (extension != "") { 00881 base = base.left(base.length() - extension.length() - 1); 00882 } 00883 if (base == "") base = "remote"; 00884 00885 QString filename; 00886 00887 if (extension == "") { 00888 filename = base; 00889 } else { 00890 filename = QString("%1.%2").arg(base).arg(extension); 00891 } 00892 00893 QString filepath(dir.filePath(filename)); 00894 00895 #ifdef DEBUG_FILE_SOURCE 00896 cerr << "FileSource::createCacheFile: URL is \"" << m_url.toString() << "\", dir is \"" << dir.path() << "\", base \"" << base << "\", extension \"" << extension << "\", filebase \"" << filename << "\", filename \"" << filepath << "\"" << endl; 00897 #endif 00898 00899 QMutexLocker fcLocker(&m_fileCreationMutex); 00900 00901 ++m_count; 00902 00903 if (QFileInfo(filepath).exists() || 00904 !QFile(filepath).open(QFile::WriteOnly)) { 00905 00906 #ifdef DEBUG_FILE_SOURCE 00907 cerr << "FileSource::createCacheFile: Failed to create local file \"" 00908 << filepath << "\" for URL \"" 00909 << m_url.toString() << "\" (or file already exists): appending suffix instead" << endl; 00910 #endif 00911 00912 if (extension == "") { 00913 filename = QString("%1_%2").arg(base).arg(m_count); 00914 } else { 00915 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension); 00916 } 00917 filepath = dir.filePath(filename); 00918 00919 if (QFileInfo(filepath).exists() || 00920 !QFile(filepath).open(QFile::WriteOnly)) { 00921 00922 #ifdef DEBUG_FILE_SOURCE 00923 cerr << "FileSource::createCacheFile: ERROR: Failed to create local file \"" 00924 << filepath << "\" for URL \"" 00925 << m_url.toString() << "\" (or file already exists)" << endl; 00926 #endif 00927 00928 return false; 00929 } 00930 } 00931 00932 #ifdef DEBUG_FILE_SOURCE 00933 cerr << "FileSource::createCacheFile: url " 00934 << m_url.toString() << " -> local filename " 00935 << filepath << endl; 00936 #endif 00937 00938 m_localFilename = filepath; 00939 00940 return false; 00941 } 00942