annotate data/fileio/FileSource.cpp @ 321:1dc99b430d2a

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