annotate data/fileio/FileSource.cpp @ 493:3931711b5671

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