annotate data/fileio/FileSource.cpp @ 327:1d656dcda8ef

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