annotate data/fileio/FileSource.cpp @ 325:82a2d3161e14

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