annotate data/fileio/RemoteFile.cpp @ 316:3a6725f285d6

* Make RemoteFile far more pervasive, and use it for local files as well so that we can handle both transparently. Make it shallow copy with reference counting, so it can be used by value without having to worry about the cache file lifetime. Use RemoteFile for MainWindow file-open functions, etc
author Chris Cannam
date Thu, 18 Oct 2007 15:31:20 +0000
parents 96ef9746c560
children
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@208 16 #include "RemoteFile.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@208 31 RemoteFile::m_count = 0;
Chris@208 32
Chris@208 33 QMutex
Chris@208 34 RemoteFile::m_fileCreationMutex;
Chris@208 35
Chris@304 36 RemoteFile::RemoteRefCountMap
Chris@304 37 RemoteFile::m_refCountMap;
Chris@304 38
Chris@304 39 RemoteFile::RemoteLocalMap
Chris@304 40 RemoteFile::m_remoteLocalMap;
Chris@304 41
Chris@304 42 QMutex
Chris@304 43 RemoteFile::m_mapMutex;
Chris@304 44
Chris@316 45 RemoteFile::RemoteFile(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@316 59 std::cerr << "RemoteFile::RemoteFile(" << fileOrUrl.toStdString() << ")" << std::endl;
Chris@316 60
Chris@316 61 if (!canHandleScheme(m_url)) {
Chris@316 62 std::cerr << "RemoteFile::RemoteFile: 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@316 79 std::cerr << "RemoteFile::RemoteFile: 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@316 90 RemoteFile::RemoteFile(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@316 104 std::cerr << "RemoteFile::RemoteFile(" << url.toString().toStdString() << ") [as url]" << std::endl;
Chris@316 105
Chris@316 106 if (!canHandleScheme(m_url)) {
Chris@316 107 std::cerr << "RemoteFile::RemoteFile: 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@316 115 RemoteFile::RemoteFile(const RemoteFile &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@316 130 std::cerr << "RemoteFile::RemoteFile(" << m_url.toString().toStdString() << ") [copy ctor]" << std::endl;
Chris@304 131
Chris@316 132 if (!canHandleScheme(m_url)) {
Chris@316 133 std::cerr << "RemoteFile::RemoteFile: 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@316 142 std::cerr << "RemoteFile::RemoteFile(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@316 158 RemoteFile::~RemoteFile()
Chris@316 159 {
Chris@316 160 std::cerr << "RemoteFile(" << m_url.toString().toStdString() << ")::~RemoteFile" << 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@316 168 RemoteFile::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@316 183 std::cerr << "RemoteFile::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@316 200 std::cerr << "RemoteFile::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@316 221 std::cerr << "RemoteFile::init: Another RemoteFile 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@316 249 RemoteFile::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@316 303 std::cerr << "RemoteFile: 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@316 310 RemoteFile::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@211 342 RemoteFile::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@304 364 RemoteFile::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@208 371 RemoteFile::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@210 379 RemoteFile::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@211 385 std::cerr << "RemoteFile::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@316 391 RemoteFile::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@316 400 RemoteFile::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@316 408 RemoteFile::setLeaveLocalFile(bool leave)
Chris@316 409 {
Chris@316 410 m_leaveLocalFile = leave;
Chris@316 411 }
Chris@316 412
Chris@208 413 bool
Chris@208 414 RemoteFile::isOK() const
Chris@208 415 {
Chris@208 416 return m_ok;
Chris@208 417 }
Chris@208 418
Chris@208 419 bool
Chris@208 420 RemoteFile::isDone() const
Chris@208 421 {
Chris@208 422 return m_done;
Chris@208 423 }
Chris@208 424
Chris@316 425 bool
Chris@316 426 RemoteFile::isRemote() const
Chris@316 427 {
Chris@316 428 return m_remote;
Chris@316 429 }
Chris@316 430
Chris@316 431 QString
Chris@316 432 RemoteFile::getLocation() const
Chris@316 433 {
Chris@316 434 return m_url.toString();
Chris@316 435 }
Chris@316 436
Chris@208 437 QString
Chris@208 438 RemoteFile::getLocalFilename() const
Chris@208 439 {
Chris@208 440 return m_localFilename;
Chris@208 441 }
Chris@208 442
Chris@208 443 QString
Chris@316 444 RemoteFile::getContentType() const
Chris@316 445 {
Chris@316 446 return m_contentType;
Chris@316 447 }
Chris@316 448
Chris@316 449 QString
Chris@316 450 RemoteFile::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@208 460 RemoteFile::getErrorString() const
Chris@208 461 {
Chris@208 462 return m_errorString;
Chris@208 463 }
Chris@208 464
Chris@208 465 void
Chris@208 466 RemoteFile::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@214 472 RemoteFile::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@211 478 std::cerr << "RemoteFile::responseHeaderReceived: "
Chris@211 479 << m_errorString.toStdString() << std::endl;
Chris@211 480 } else {
Chris@211 481 std::cerr << "RemoteFile::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@214 488 RemoteFile::ftpCommandFinished(int id, bool error)
Chris@214 489 {
Chris@214 490 std::cerr << "RemoteFile::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@214 497 std::cerr << "RemoteFile::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@208 516 RemoteFile::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@210 530 RemoteFile::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@208 540 RemoteFile::done(bool error)
Chris@208 541 {
Chris@214 542 std::cerr << "RemoteFile::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@316 574 std::cerr << "RemoteFile::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@316 584 RemoteFile::deleteCacheFile()
Chris@211 585 {
Chris@316 586 std::cerr << "RemoteFile::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@316 617 std::cerr << "RemoteFile::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
Chris@211 618 } else {
Chris@316 619 std::cerr << "RemoteFile::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@210 629 RemoteFile::showProgressDialog()
Chris@210 630 {
Chris@210 631 if (m_progressDialog) m_progressDialog->show();
Chris@210 632 }
Chris@210 633
Chris@316 634 bool
Chris@316 635 RemoteFile::createCacheFile()
Chris@208 636 {
Chris@316 637 {
Chris@316 638 QMutexLocker locker(&m_mapMutex);
Chris@316 639
Chris@316 640 std::cerr << "RemoteFile::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@316 655 std::cerr << "RemoteFile::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@316 679 std::cerr << "RemoteFile::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@316 688 std::cerr << "RemoteFile::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@316 703 std::cerr << "RemoteFile::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@316 711 std::cerr << "RemoteFile::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 }