annotate data/fileio/FileSource.cpp @ 503:3176aade1a03

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