File indexing completed on 2024-04-28 04:57:32
0001 /* This file is part of the KDE project 0002 0003 Copyright (C) 2004 Dario Massarin <nekkar@libero.it> 0004 Copyright (C) 2007 Manolo Valdes <nolis71cu@gmail.com> 0005 Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net> 0006 0007 This program is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU General Public 0009 License as published by the Free Software Foundation; either 0010 version 2 of the License, or (at your option) any later version. 0011 */ 0012 0013 #include "metalink.h" 0014 #include "fileselectiondlg.h" 0015 #include "metalinksettings.h" 0016 0017 #include "core/download.h" 0018 #include "core/filemodel.h" 0019 #include "core/kget.h" 0020 #include "core/signature.h" 0021 #include "core/transferdatasource.h" 0022 #include "core/transfergroup.h" 0023 #include "core/urlchecker.h" 0024 #include "core/verifier.h" 0025 0026 #include <algorithm> 0027 0028 #include <KConfigGroup> 0029 #include <KIO/DeleteJob> 0030 #include <KIO/RenameDialog> 0031 #include <KLocalizedString> 0032 #include <KMessageBox> 0033 #include <kwidgetsaddons_version.h> 0034 0035 #include <QDebug> 0036 #include <QDialog> 0037 #include <QDomElement> 0038 #include <QFile> 0039 #include <QStandardPaths> 0040 0041 Metalink::Metalink(TransferGroup *parent, TransferFactory *factory, Scheduler *scheduler, const QUrl &source, const QUrl &dest, const QDomElement *e) 0042 : Transfer(parent, factory, scheduler, source, dest, e) 0043 , m_fileModel(nullptr) 0044 , m_currentFiles(0) 0045 , m_metalinkJustDownloaded(false) 0046 , m_ready(false) 0047 , m_speedCount(0) 0048 , m_tempAverageSpeed(0) 0049 , m_averageSpeed(0) 0050 { 0051 } 0052 0053 Metalink::~Metalink() 0054 { 0055 } 0056 0057 void Metalink::start() 0058 { 0059 qCDebug(KGET_DEBUG) << "metalink::start"; 0060 0061 if (!m_ready) { 0062 if (m_localMetalinkLocation.isValid() && metalinkInit()) { 0063 startMetalink(); 0064 } else { 0065 downloadMetalink(); 0066 } 0067 } else { 0068 startMetalink(); 0069 } 0070 } 0071 0072 void Metalink::downloadMetalink() 0073 { 0074 m_metalinkJustDownloaded = true; 0075 0076 setStatus(Job::Stopped, i18n("Downloading Metalink File...."), "document-save"); 0077 setTransferChange(Tc_Status, true); 0078 // make sure that the DataLocation directory exists (earlier this used to be handled by KStandardDirs) 0079 if (!QFileInfo::exists(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation))) { 0080 QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); 0081 } 0082 Download *download = 0083 new Download(m_source, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/metalinks/") + m_source.fileName()); 0084 connect(download, SIGNAL(finishedSuccessfully(QUrl, QByteArray)), SLOT(metalinkInit(QUrl, QByteArray))); 0085 } 0086 0087 bool Metalink::metalinkInit(const QUrl &src, const QByteArray &data) 0088 { 0089 qCDebug(KGET_DEBUG); 0090 0091 if (!src.isEmpty()) { 0092 m_localMetalinkLocation = src; 0093 } 0094 0095 // use the downloaded metalink-file data directly if possible 0096 if (!data.isEmpty()) { 0097 KGetMetalink::HandleMetalink::load(data, &m_metalink); 0098 } 0099 0100 // try to parse the locally stored metalink-file 0101 if (!m_metalink.isValid() && m_localMetalinkLocation.isValid()) { 0102 KGetMetalink::HandleMetalink::load(m_localMetalinkLocation.toLocalFile(), &m_metalink); 0103 } 0104 0105 if (!m_metalink.isValid()) { 0106 qCCritical(KGET_DEBUG) << "Unknown error when trying to load the .metalink-file. Metalink is not valid."; 0107 setStatus(Job::Aborted); 0108 setTransferChange(Tc_Status, true); 0109 return false; 0110 } 0111 0112 // offers a dialog to download the newest version of a dynamic metalink 0113 if ((m_source.isLocalFile() || !m_metalinkJustDownloaded) && m_metalink.dynamic && (UrlChecker::checkSource(m_metalink.origin) == UrlChecker::NoError)) { 0114 if (KMessageBox::questionTwoActions(nullptr, 0115 i18n("A newer version of this Metalink might exist, do you want to download it?"), 0116 i18n("Redownload Metalink"), 0117 KGuiItem(i18nc("@action:button", "Download Again"), QStringLiteral("view-refresh")), 0118 KGuiItem(i18nc("@action:button", "Ignore"), QStringLiteral("dialog-cancel"))) 0119 == KMessageBox::PrimaryAction) { 0120 m_localMetalinkLocation.clear(); 0121 m_source = m_metalink.origin; 0122 downloadMetalink(); 0123 return false; 0124 } 0125 } 0126 0127 QList<KGetMetalink::File>::const_iterator it; 0128 QList<KGetMetalink::File>::const_iterator itEnd = m_metalink.files.files.constEnd(); 0129 m_totalSize = 0; 0130 KIO::fileoffset_t segSize = 500 * 1024; // TODO use config here! 0131 const QUrl tempDest = QUrl(m_dest.directory()); 0132 QUrl dest; 0133 for (it = m_metalink.files.files.constBegin(); it != itEnd; ++it) { 0134 dest = tempDest; 0135 dest.addPath((*it).name); 0136 0137 QList<KGetMetalink::Url> urlList = (*it).resources.urls; 0138 // sort the urls according to their priority (highest first) 0139 std::sort(urlList.begin(), urlList.end(), [](const KGetMetalink::Url &a, const KGetMetalink::Url &b) { 0140 return b < a; 0141 }); 0142 0143 KIO::filesize_t fileSize = (*it).size; 0144 m_totalSize += fileSize; 0145 0146 // create a DataSourceFactory for each separate file 0147 DataSourceFactory *dataFactory = new DataSourceFactory(this, dest, fileSize, segSize); 0148 dataFactory->setMaxMirrorsUsed(MetalinkSettings::mirrorsPerFile()); 0149 0150 // TODO compare available file size (<size>) with the sizes of the server while downloading? 0151 0152 connect(dataFactory, SIGNAL(capabilitiesChanged()), this, SLOT(slotUpdateCapabilities())); 0153 connect(dataFactory, SIGNAL(dataSourceFactoryChange(Transfer::ChangesFlags)), this, SLOT(slotDataSourceFactoryChange(Transfer::ChangesFlags))); 0154 connect(dataFactory->verifier(), SIGNAL(verified(bool)), this, SLOT(slotVerified(bool))); 0155 connect(dataFactory->signature(), SIGNAL(verified(int)), this, SLOT(slotSignatureVerified())); 0156 connect(dataFactory, SIGNAL(log(QString, Transfer::LogLevel)), this, SLOT(setLog(QString, Transfer::LogLevel))); 0157 0158 // add the DataSources 0159 for (int i = 0; i < urlList.size(); ++i) { 0160 const QUrl url = urlList[i].url; 0161 if (url.isValid()) { 0162 dataFactory->addMirror(url, MetalinkSettings::connectionsPerUrl()); 0163 } 0164 } 0165 // no datasource has been created, so remove the datasource factory 0166 if (dataFactory->mirrors().isEmpty()) { 0167 delete dataFactory; 0168 } else { 0169 dataFactory->verifier()->addChecksums((*it).verification.hashes); 0170 foreach (const KGetMetalink::Pieces &pieces, (*it).verification.pieces) { 0171 dataFactory->verifier()->addPartialChecksums(pieces.type, pieces.length, pieces.hashes); 0172 } 0173 0174 const QHash<QString, QString> signatures = (*it).verification.signatures; 0175 QHash<QString, QString>::const_iterator it; 0176 QHash<QString, QString>::const_iterator itEnd = signatures.constEnd(); 0177 for (it = signatures.constBegin(); it != itEnd; ++it) { 0178 if (it.key().toLower() == "pgp") { 0179 dataFactory->signature()->setAsciiDetachedSignature(*it); 0180 } 0181 } 0182 0183 m_dataSourceFactory[dataFactory->dest()] = dataFactory; 0184 } 0185 } 0186 0187 if ((m_metalink.files.files.size() == 1) && m_dataSourceFactory.size()) { 0188 m_dest = dest; 0189 } 0190 0191 if (!m_dataSourceFactory.size()) { 0192 // TODO make this via log in the future + do not display the KMessageBox 0193 qCWarning(KGET_DEBUG) << "Download of" << m_source << "failed, no working URLs were found."; 0194 KMessageBox::error(nullptr, i18n("Download failed, no working URLs were found."), i18n("Error")); 0195 setStatus(Job::Aborted); 0196 setTransferChange(Tc_Status, true); 0197 return false; 0198 } 0199 0200 m_ready = !m_dataSourceFactory.isEmpty(); 0201 slotUpdateCapabilities(); 0202 0203 // the metalink-file has just been downloaded, so ask the user to choose the files that 0204 // should be downloaded 0205 if (m_metalinkJustDownloaded) { 0206 QDialog *dialog = new FileSelectionDlg(fileModel()); 0207 dialog->setAttribute(Qt::WA_DeleteOnClose); 0208 connect(dialog, SIGNAL(finished(int)), this, SLOT(fileDlgFinished(int))); 0209 0210 dialog->show(); 0211 } 0212 0213 return true; 0214 } 0215 0216 void Metalink::untickAllFiles() 0217 { 0218 for (int row = 0; row < fileModel()->rowCount(); ++row) { 0219 QModelIndex index = fileModel()->index(row, FileItem::File); 0220 if (index.isValid()) { 0221 fileModel()->setData(index, Qt::Unchecked, Qt::CheckStateRole); 0222 } 0223 } 0224 } 0225 0226 void Metalink::fileDlgFinished(int result) 0227 { 0228 // the dialog was not accepted untick every file, this ensures that the user does not 0229 // press start by accident without first selecting the desired files 0230 if (result != QDialog::Accepted) { 0231 untickAllFiles(); 0232 } 0233 0234 filesSelected(); 0235 0236 // no files selected to download or dialog rejected, stop the download 0237 if (!m_numFilesSelected || (result != QDialog::Accepted)) { 0238 setStatus(Job::Stopped); 0239 setTransferChange(Tc_Status, true); 0240 return; 0241 } 0242 0243 startMetalink(); 0244 } 0245 0246 void Metalink::startMetalink() 0247 { 0248 if (m_ready) { 0249 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0250 // specified number of files is downloaded simultaneously 0251 if (m_currentFiles < MetalinkSettings::simultaneousFiles()) { 0252 const int status = factory->status(); 0253 // only start factories that should be downloaded 0254 if (factory->doDownload() && (status != Job::Finished) && (status != Job::FinishedKeepAlive) && (status != Job::Running)) { 0255 ++m_currentFiles; 0256 factory->start(); 0257 } 0258 } else { 0259 break; 0260 } 0261 } 0262 } 0263 } 0264 0265 void Metalink::deinit(Transfer::DeleteOptions options) 0266 { 0267 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0268 if (options & Transfer::DeleteFiles) { 0269 factory->deinit(); 0270 } 0271 } // TODO: Ask the user if he/she wants to delete the *.part-file? To discuss (boom1992) 0272 0273 // FIXME does that mean, that the metalink file is always removed, even if 0274 // downloaded by the user? 0275 if ((options & Transfer::DeleteTemporaryFiles) && m_localMetalinkLocation.isLocalFile()) { 0276 KIO::Job *del = KIO::del(m_localMetalinkLocation, KIO::HideProgressInfo); 0277 if (!del->exec()) { 0278 qCDebug(KGET_DEBUG) << "Could not delete " << m_localMetalinkLocation.path(); 0279 } 0280 } 0281 } 0282 0283 void Metalink::stop() 0284 { 0285 qCDebug(KGET_DEBUG) << "metalink::Stop"; 0286 if (m_ready && status() != Stopped) { 0287 m_currentFiles = 0; 0288 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0289 factory->stop(); 0290 } 0291 } 0292 } 0293 0294 void Metalink::slotDataSourceFactoryChange(Transfer::ChangesFlags change) 0295 { 0296 if ((change & Tc_Status) | (change & Tc_TotalSize)) { 0297 DataSourceFactory *factory = qobject_cast<DataSourceFactory *>(sender()); 0298 if (change & Tc_Status) { 0299 bool changeStatus; 0300 updateStatus(factory, &changeStatus); 0301 if (!changeStatus) { 0302 change &= ~Tc_Status; 0303 } 0304 } 0305 if (change & Tc_TotalSize) { 0306 recalculateTotalSize(factory); 0307 } 0308 } 0309 if (change & Tc_DownloadedSize) { 0310 recalculateProcessedSize(); 0311 change |= Tc_Percent; 0312 } 0313 if (change & Tc_DownloadSpeed) { 0314 recalculateSpeed(); 0315 } 0316 0317 setTransferChange(change, true); 0318 } 0319 0320 void Metalink::recalculateTotalSize(DataSourceFactory *sender) 0321 { 0322 m_totalSize = 0; 0323 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0324 if (factory->doDownload()) { 0325 m_totalSize += factory->size(); 0326 } 0327 } 0328 0329 if (m_fileModel) { 0330 if (sender) { 0331 QModelIndex sizeIndex = m_fileModel->index(sender->dest(), FileItem::Size); 0332 m_fileModel->setData(sizeIndex, static_cast<qlonglong>(sender->size())); 0333 } 0334 } 0335 } 0336 0337 void Metalink::recalculateProcessedSize() 0338 { 0339 m_downloadedSize = 0; 0340 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0341 if (factory->doDownload()) { 0342 m_downloadedSize += factory->downloadedSize(); 0343 } 0344 } 0345 0346 if (m_totalSize) { 0347 m_percent = (m_downloadedSize * 100) / m_totalSize; 0348 } else { 0349 m_percent = 0; 0350 } 0351 } 0352 0353 void Metalink::recalculateSpeed() 0354 { 0355 m_downloadSpeed = 0; 0356 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0357 if (factory->doDownload()) { 0358 m_downloadSpeed += factory->currentSpeed(); 0359 } 0360 } 0361 0362 // calculate the average of the last three speeds 0363 m_tempAverageSpeed += m_downloadSpeed; 0364 ++m_speedCount; 0365 if (m_speedCount == 3) { 0366 m_averageSpeed = m_tempAverageSpeed / 3; 0367 m_speedCount = 0; 0368 m_tempAverageSpeed = 0; 0369 } 0370 } 0371 0372 int Metalink::remainingTime() const 0373 { 0374 if (!m_averageSpeed) { 0375 m_averageSpeed = m_downloadSpeed; 0376 } 0377 return KIO::calculateRemainingSeconds(m_totalSize, m_downloadedSize, m_averageSpeed); 0378 } 0379 0380 void Metalink::updateStatus(DataSourceFactory *sender, bool *changeStatus) 0381 { 0382 Job::Status status = (sender ? sender->status() : Job::Stopped); 0383 *changeStatus = true; 0384 switch (status) { 0385 case Job::Aborted: 0386 case Job::Stopped: { 0387 m_currentFiles = 0; 0388 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0389 // one factory is still running, do not change the status 0390 if (factory->doDownload() && (factory->status() == Job::Running)) { 0391 *changeStatus = false; 0392 ++m_currentFiles; 0393 } 0394 } 0395 0396 if (*changeStatus) { 0397 setStatus(status); 0398 } 0399 break; 0400 } 0401 case Job::Finished: 0402 // one file that has been downloaded now is finished//FIXME ignore downloads that were finished in the previous download!!!! 0403 if (m_currentFiles) { 0404 --m_currentFiles; 0405 startMetalink(); 0406 } 0407 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0408 // one factory is not finished, do not change the status 0409 if (factory->doDownload() && (factory->status() != Job::Finished)) { 0410 *changeStatus = false; 0411 break; 0412 } 0413 } 0414 0415 if (*changeStatus) { 0416 setStatus(Job::Finished); 0417 } 0418 break; 0419 0420 default: 0421 setStatus(status); 0422 break; 0423 } 0424 0425 if (m_fileModel) { 0426 if (sender) { 0427 QModelIndex statusIndex = m_fileModel->index(sender->dest(), FileItem::Status); 0428 m_fileModel->setData(statusIndex, status); 0429 } 0430 } 0431 } 0432 0433 void Metalink::slotVerified(bool isVerified) 0434 { 0435 Q_UNUSED(isVerified) 0436 0437 if (status() == Job::Finished) { 0438 // see if some files are NotVerified 0439 QStringList brokenFiles; 0440 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0441 if (m_fileModel) { 0442 QModelIndex checksumVerified = m_fileModel->index(factory->dest(), FileItem::ChecksumVerified); 0443 m_fileModel->setData(checksumVerified, factory->verifier()->status()); 0444 } 0445 if (factory->doDownload() && (factory->verifier()->status() == Verifier::NotVerified)) { 0446 brokenFiles.append(factory->dest().pathOrUrl()); 0447 } 0448 } 0449 0450 if (brokenFiles.count()) { 0451 if (KMessageBox::warningTwoActionsList( 0452 nullptr, 0453 i18n("The download could not be verified, do you want to repair (if repairing does not work the download would be restarted) it?"), 0454 brokenFiles, 0455 QString(), 0456 KGuiItem(i18nc("@action:button", "Repair")), 0457 KGuiItem(i18nc("@action:button", "Ignore"), QStringLiteral("dialog-cancel"))) 0458 == KMessageBox::PrimaryAction) { 0459 if (repair()) { 0460 return; 0461 } 0462 } 0463 } 0464 } 0465 } 0466 0467 void Metalink::slotSignatureVerified() 0468 { 0469 if (status() == Job::Finished) { 0470 // see if some files are NotVerified 0471 QStringList brokenFiles; 0472 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0473 if (m_fileModel) { 0474 QModelIndex signatureVerified = m_fileModel->index(factory->dest(), FileItem::SignatureVerified); 0475 m_fileModel->setData(signatureVerified, factory->signature()->status()); 0476 } 0477 if (factory->doDownload() && (factory->verifier()->status() == Verifier::NotVerified)) { 0478 brokenFiles.append(factory->dest().pathOrUrl()); 0479 } 0480 } 0481 /* 0482 if (brokenFiles.count())//TODO 0483 { 0484 if (KMessageBox::warningYesNoCancelList(nullptr, 0485 i18n("The download could not be verified, try to repair it?"), 0486 brokenFiles) == KMessageBox::Yes) 0487 { 0488 if (repair()) 0489 { 0490 return; 0491 } 0492 } 0493 }*/ 0494 } 0495 } 0496 0497 bool Metalink::repair(const QUrl &file) 0498 { 0499 if (file.isValid()) { 0500 if (m_dataSourceFactory.contains(file)) { 0501 DataSourceFactory *broken = m_dataSourceFactory[file]; 0502 if (broken->verifier()->status() == Verifier::NotVerified) { 0503 broken->repair(); 0504 return true; 0505 } 0506 } 0507 } else { 0508 QList<DataSourceFactory *> broken; 0509 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0510 if (factory->doDownload() && (factory->verifier()->status() == Verifier::NotVerified)) { 0511 broken.append(factory); 0512 } 0513 } 0514 if (broken.count()) { 0515 foreach (DataSourceFactory *factory, broken) { 0516 factory->repair(); 0517 } 0518 return true; 0519 } 0520 } 0521 0522 return false; 0523 } 0524 0525 void Metalink::load(const QDomElement *element) 0526 { 0527 Transfer::load(element); 0528 0529 if (!element) { 0530 return; 0531 } 0532 0533 const QDomElement e = *element; 0534 m_localMetalinkLocation = QUrl(e.attribute("LocalMetalinkLocation")); 0535 QDomNodeList factories = e.firstChildElement("factories").elementsByTagName("factory"); 0536 0537 // no stored information found, stop right here 0538 if (!factories.count()) { 0539 return; 0540 } 0541 0542 while (factories.count()) { 0543 QDomDocument doc; 0544 QDomElement factory = doc.createElement("factories"); 0545 factory.appendChild(factories.item(0).toElement()); 0546 doc.appendChild(factory); 0547 0548 DataSourceFactory *file = new DataSourceFactory(this); 0549 file->load(&factory); 0550 connect(file, SIGNAL(capabilitiesChanged()), this, SLOT(slotUpdateCapabilities())); 0551 connect(file, SIGNAL(dataSourceFactoryChange(Transfer::ChangesFlags)), this, SLOT(slotDataSourceFactoryChange(Transfer::ChangesFlags))); 0552 m_dataSourceFactory[file->dest()] = file; 0553 connect(file->verifier(), SIGNAL(verified(bool)), this, SLOT(slotVerified(bool))); 0554 connect(file->signature(), SIGNAL(verified(int)), this, SLOT(slotSignatureVerified())); 0555 connect(file, SIGNAL(log(QString, Transfer::LogLevel)), this, SLOT(setLog(QString, Transfer::LogLevel))); 0556 0557 // start the DataSourceFactories that were Started when KGet was closed 0558 if (file->status() == Job::Running) { 0559 if (m_currentFiles < MetalinkSettings::simultaneousFiles()) { 0560 ++m_currentFiles; 0561 file->start(); 0562 } else { 0563 // enough simultaneous files already, so increase the number and set file to stop --> that will decrease the number again 0564 file->stop(); 0565 } 0566 } 0567 } 0568 m_ready = !m_dataSourceFactory.isEmpty(); 0569 slotUpdateCapabilities(); 0570 } 0571 0572 void Metalink::save(const QDomElement &element) 0573 { 0574 Transfer::save(element); 0575 0576 QDomElement e = element; 0577 e.setAttribute("LocalMetalinkLocation", m_localMetalinkLocation.url()); 0578 0579 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0580 factory->save(e); 0581 } 0582 } 0583 0584 Verifier *Metalink::verifier(const QUrl &file) 0585 { 0586 if (!m_dataSourceFactory.contains(file)) { 0587 return nullptr; 0588 } 0589 0590 return m_dataSourceFactory[file]->verifier(); 0591 } 0592 0593 Signature *Metalink::signature(const QUrl &file) 0594 { 0595 if (!m_dataSourceFactory.contains(file)) { 0596 return nullptr; 0597 } 0598 0599 return m_dataSourceFactory[file]->signature(); 0600 } 0601 0602 QList<QUrl> Metalink::files() const 0603 { 0604 return m_dataSourceFactory.keys(); 0605 } 0606 0607 FileModel *Metalink::fileModel() 0608 { 0609 if (!m_fileModel) { 0610 m_fileModel = new FileModel(files(), directory(), this); 0611 connect(m_fileModel, SIGNAL(rename(QUrl, QUrl)), this, SLOT(slotRename(QUrl, QUrl))); 0612 connect(m_fileModel, SIGNAL(checkStateChanged()), this, SLOT(filesSelected())); 0613 0614 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0615 const QUrl dest = factory->dest(); 0616 QModelIndex size = m_fileModel->index(dest, FileItem::Size); 0617 m_fileModel->setData(size, static_cast<qlonglong>(factory->size())); 0618 QModelIndex status = m_fileModel->index(dest, FileItem::Status); 0619 m_fileModel->setData(status, factory->status()); 0620 QModelIndex checksumVerified = m_fileModel->index(dest, FileItem::ChecksumVerified); 0621 m_fileModel->setData(checksumVerified, factory->verifier()->status()); 0622 QModelIndex signatureVerified = m_fileModel->index(dest, FileItem::SignatureVerified); 0623 m_fileModel->setData(signatureVerified, factory->signature()->status()); 0624 if (!factory->doDownload()) { 0625 QModelIndex index = m_fileModel->index(factory->dest(), FileItem::File); 0626 m_fileModel->setData(index, Qt::Unchecked, Qt::CheckStateRole); 0627 } 0628 } 0629 } 0630 0631 return m_fileModel; 0632 } 0633 0634 void Metalink::filesSelected() 0635 { 0636 bool overwriteAll = false; 0637 bool autoSkip = false; 0638 bool cancel = false; 0639 QModelIndexList files = fileModel()->fileIndexes(FileItem::File); 0640 m_numFilesSelected = 0; 0641 0642 // sets the CheckState of the fileModel to the according DataSourceFactories 0643 // and asks the user if there are existing files already 0644 foreach (const QModelIndex &index, files) { 0645 const QUrl dest = fileModel()->getUrl(index); 0646 bool doDownload = index.data(Qt::CheckStateRole).toBool(); 0647 if (m_dataSourceFactory.contains(dest)) { 0648 DataSourceFactory *factory = m_dataSourceFactory[dest]; 0649 // ignore finished transfers 0650 if ((factory->status() == Job::Finished) || (factory->status() == Job::FinishedKeepAlive)) { 0651 continue; 0652 } 0653 0654 // check if the file at dest exists already and ask the user what to do in this case, ignore already running transfers 0655 if (doDownload && (factory->status() != Job::Running) && QFile::exists(dest.toLocalFile())) { 0656 // user has chosen to skip all files that exist already before 0657 if (autoSkip) { 0658 fileModel()->setData(index, Qt::Unchecked, Qt::CheckStateRole); 0659 doDownload = false; 0660 // ask the user, unless he has chosen overwriteAll before 0661 } else if (!overwriteAll) { 0662 KIO::RenameDialog dlg(nullptr, 0663 i18n("File already exists"), 0664 index.data().toString(), 0665 dest, 0666 KIO::RenameDialog_Options(KIO::RenameDialog_MultipleItems | KIO::RenameDialog_Overwrite | KIO::RenameDialog_Skip)); 0667 const int result = dlg.exec(); 0668 0669 if (result == KIO::Result_Rename) { 0670 // no reason to use FileModel::rename() since the file does not exist yet, so simply skip it 0671 // avoids having to deal with signals 0672 const QUrl newDest = dlg.newDestUrl(); 0673 factory->setDoDownload(doDownload); 0674 factory->setNewDestination(newDest); 0675 fileModel()->setData(index, newDest.fileName(), FileItem::File); 0676 ++m_numFilesSelected; 0677 0678 m_dataSourceFactory.remove(dest); 0679 m_dataSourceFactory[newDest] = factory; 0680 continue; 0681 } else if (result == KIO::Result_Skip) { 0682 fileModel()->setData(index, Qt::Unchecked, Qt::CheckStateRole); 0683 doDownload = false; 0684 } else if (result == KIO::Result_Cancel) { 0685 cancel = true; 0686 break; 0687 } else if (result == KIO:Result_AutoSkip:) { 0688 autoSkip = true; 0689 fileModel()->setData(index, Qt::Unchecked, Qt::CheckStateRole); 0690 doDownload = false; 0691 } else if (result == KIO::Result_OverwriteAll) { 0692 overwriteAll = true; 0693 } 0694 } 0695 } 0696 0697 factory->setDoDownload(doDownload); 0698 if (doDownload && (factory->status() != Finished) && (factory->status() != FinishedKeepAlive)) { 0699 ++m_numFilesSelected; 0700 } 0701 } 0702 } 0703 0704 // the user decided to cancel, so untick all files 0705 if (cancel) { 0706 m_numFilesSelected = 0; 0707 untickAllFiles(); 0708 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0709 factory->setDoDownload(false); 0710 } 0711 } 0712 0713 Transfer::ChangesFlags change = (Tc_TotalSize | Tc_DownloadSpeed); 0714 // some files have been selected that are not finished yet, set them to stop if the transfer is not running (checked in slotStatus) 0715 if (m_numFilesSelected) { 0716 change |= Tc_Status; 0717 } 0718 slotDataSourceFactoryChange(change); 0719 } 0720 0721 void Metalink::slotRename(const QUrl &oldUrl, const QUrl &newUrl) 0722 { 0723 if (!m_dataSourceFactory.contains(oldUrl)) { 0724 return; 0725 } 0726 0727 m_dataSourceFactory[newUrl] = m_dataSourceFactory[oldUrl]; 0728 m_dataSourceFactory.remove(oldUrl); 0729 m_dataSourceFactory[newUrl]->setNewDestination(newUrl); 0730 0731 setTransferChange(Tc_FileName); 0732 } 0733 0734 bool Metalink::setDirectory(const QUrl &new_directory) 0735 { 0736 if (new_directory == directory()) { 0737 return false; 0738 } 0739 0740 if (m_fileModel) { 0741 m_fileModel->setDirectory(new_directory); 0742 } 0743 0744 const QString oldDirectory = directory().pathOrUrl(QUrl::AddTrailingSlash); 0745 const QString newDirectory = new_directory.pathOrUrl(QUrl::AddTrailingSlash); 0746 const QString fileName = m_dest.fileName(); 0747 m_dest = new_directory; 0748 m_dest.addPath(fileName); 0749 0750 QHash<QUrl, DataSourceFactory *> newStorage; 0751 foreach (DataSourceFactory *factory, m_dataSourceFactory) { 0752 const QUrl oldUrl = factory->dest(); 0753 const QUrl newUrl = QUrl(oldUrl.pathOrUrl().replace(oldDirectory, newDirectory)); 0754 factory->setNewDestination(newUrl); 0755 newStorage[newUrl] = factory; 0756 } 0757 m_dataSourceFactory = newStorage; 0758 0759 setTransferChange(Tc_FileName); 0760 return true; 0761 } 0762 0763 QHash<QUrl, QPair<bool, int>> Metalink::availableMirrors(const QUrl &file) const 0764 { 0765 QHash<QUrl, QPair<bool, int>> urls; 0766 0767 if (m_dataSourceFactory.contains(file)) { 0768 urls = m_dataSourceFactory[file]->mirrors(); 0769 } 0770 0771 return urls; 0772 } 0773 0774 void Metalink::setAvailableMirrors(const QUrl &file, const QHash<QUrl, QPair<bool, int>> &mirrors) 0775 { 0776 if (!m_dataSourceFactory.contains(file)) { 0777 return; 0778 } 0779 0780 m_dataSourceFactory[file]->setMirrors(mirrors); 0781 } 0782 0783 void Metalink::slotUpdateCapabilities() 0784 { 0785 Capabilities oldCap = capabilities(); 0786 Capabilities newCap = 0; 0787 foreach (DataSourceFactory *file, m_dataSourceFactory) { 0788 if (file->doDownload()) { // FIXME when a download did not start yet it should be moveable!!//FIXME why not working, when only two connections? 0789 if (newCap) { 0790 newCap &= file->capabilities(); 0791 } else { 0792 newCap = file->capabilities(); 0793 } 0794 } 0795 } 0796 0797 if (newCap != oldCap) { 0798 setCapabilities(newCap); 0799 } 0800 } 0801 0802 #include "moc_metalink.cpp"