File indexing completed on 2024-05-19 05:00:09
0001 /* This file is part of the KDE project 0002 0003 Copyright (C) 2004 Dario Massarin <nekkar@libero.it> 0004 Copyright (C) 2006 Manolo Valdes <nolis71cu@gmail.com> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public 0008 License as published by the Free Software Foundation; either 0009 version 2 of the License, or (at your option) any later version. 0010 */ 0011 0012 #include "transfermultisegkio.h" 0013 0014 #include "core/kget.h" 0015 #include "core/transferdatasource.h" 0016 #include "multisegkiosettings.h" 0017 // #include "mirrors.h" 0018 #include "core/filemodel.h" 0019 #include "core/signature.h" 0020 #include "core/verifier.h" 0021 0022 #ifdef Q_OS_WIN 0023 #include <sys/utime.h> 0024 #else 0025 #include <utime.h> 0026 #endif 0027 0028 #include "kget_debug.h" 0029 0030 #include <KIO/CopyJob> 0031 #include <KIO/StatJob> 0032 #include <KIO/UDSEntry> 0033 #include <KLocalizedString> 0034 #include <KMessageBox> 0035 0036 #include <QDebug> 0037 #include <QDomElement> 0038 #include <QFile> 0039 0040 TransferMultiSegKio::TransferMultiSegKio(TransferGroup *parent, 0041 TransferFactory *factory, 0042 Scheduler *scheduler, 0043 const QUrl &source, 0044 const QUrl &dest, 0045 const QDomElement *e) 0046 : Transfer(parent, factory, scheduler, source, dest, e) 0047 , m_movingFile(false) 0048 , m_searchStarted(false) 0049 , m_verificationSearch(false) 0050 , m_dataSourceFactory(nullptr) 0051 , m_fileModel(nullptr) 0052 { 0053 } 0054 0055 void TransferMultiSegKio::init() 0056 { 0057 Transfer::init(); 0058 0059 if (!m_dataSourceFactory) { 0060 m_dataSourceFactory = new DataSourceFactory(this, m_dest); 0061 connect(m_dataSourceFactory, &DataSourceFactory::capabilitiesChanged, this, &TransferMultiSegKio::slotUpdateCapabilities); 0062 connect(m_dataSourceFactory, &DataSourceFactory::dataSourceFactoryChange, this, &TransferMultiSegKio::slotDataSourceFactoryChange); 0063 connect(m_dataSourceFactory->verifier(), &Verifier::verified, this, &TransferMultiSegKio::slotVerified); 0064 connect(m_dataSourceFactory, &DataSourceFactory::log, this, &Transfer::setLog); 0065 0066 m_dataSourceFactory->addMirror(m_source, MultiSegKioSettings::segments()); 0067 0068 slotUpdateCapabilities(); 0069 } 0070 } 0071 0072 void TransferMultiSegKio::deinit(Transfer::DeleteOptions options) 0073 { 0074 if (options & Transfer::DeleteFiles) // if the transfer is not finished, we delete the *.part-file 0075 { 0076 m_dataSourceFactory->deinit(); 0077 } // TODO: Ask the user if he/she wants to delete the *.part-file? To discuss (boom1992) 0078 } 0079 0080 void TransferMultiSegKio::start() 0081 { 0082 qCDebug(KGET_DEBUG) << "Start TransferMultiSegKio"; 0083 if (status() == Running) { 0084 return; 0085 } 0086 0087 m_dataSourceFactory->start(); 0088 0089 if (MultiSegKioSettings::useSearchEngines() && !m_searchStarted) { 0090 m_searchStarted = true; 0091 QDomDocument doc; 0092 QDomElement element = doc.createElement("TransferDataSource"); 0093 element.setAttribute("type", "search"); 0094 doc.appendChild(element); 0095 0096 TransferDataSource *mirrorSearch = KGet::createTransferDataSource(m_source, element, this); 0097 if (mirrorSearch) { 0098 connect(mirrorSearch, SIGNAL(data(QList<QUrl>)), this, SLOT(slotSearchUrls(QList<QUrl>))); 0099 mirrorSearch->start(); 0100 } 0101 } 0102 } 0103 0104 void TransferMultiSegKio::stop() 0105 { 0106 qCDebug(KGET_DEBUG); 0107 0108 if ((status() == Stopped) || (status() == Finished)) { 0109 return; 0110 } 0111 0112 if (m_dataSourceFactory) { 0113 m_dataSourceFactory->stop(); 0114 } 0115 } 0116 0117 bool TransferMultiSegKio::repair(const QUrl &file) 0118 { 0119 if (!file.isValid() || (m_dest == file)) { 0120 if (m_dataSourceFactory && (m_dataSourceFactory->verifier()->status() == Verifier::NotVerified)) { 0121 m_dataSourceFactory->repair(); 0122 return true; 0123 } 0124 } 0125 0126 return false; 0127 } 0128 0129 bool TransferMultiSegKio::setDirectory(const QUrl &newDirectory) 0130 { 0131 QUrl newDest = newDirectory; 0132 newDest.setPath(newDest.path() + "/" + m_dest.fileName()); 0133 return setNewDestination(newDest); 0134 } 0135 0136 bool TransferMultiSegKio::setNewDestination(const QUrl &newDestination) 0137 { 0138 qCDebug(KGET_DEBUG) << "New destination: " << newDestination; 0139 if (newDestination.isValid() && (newDestination != dest()) && m_dataSourceFactory) { 0140 m_movingFile = true; 0141 stop(); 0142 m_dataSourceFactory->setNewDestination(newDestination); 0143 0144 m_dest = newDestination; 0145 0146 if (m_fileModel) { 0147 m_fileModel->setDirectory(directory()); 0148 } 0149 0150 setTransferChange(Tc_FileName); 0151 return true; 0152 } 0153 return false; 0154 } 0155 0156 void TransferMultiSegKio::load(const QDomElement *element) 0157 { 0158 qCDebug(KGET_DEBUG); 0159 0160 Transfer::load(element); 0161 m_dataSourceFactory->load(element); 0162 } 0163 0164 void TransferMultiSegKio::save(const QDomElement &element) 0165 { 0166 qCDebug(KGET_DEBUG); 0167 Transfer::save(element); 0168 m_dataSourceFactory->save(element); 0169 } 0170 0171 void TransferMultiSegKio::slotDataSourceFactoryChange(Transfer::ChangesFlags change) 0172 { 0173 if (change & Tc_FileName) { 0174 QList<QUrl> urls = m_dataSourceFactory->mirrors().keys(); 0175 QString filename = urls.first().fileName(); 0176 if (filename.isEmpty()) 0177 return; 0178 foreach (const QUrl url, urls) { 0179 if (filename != url.fileName()) 0180 return; 0181 } 0182 QUrl path = m_dest.adjusted(QUrl::RemoveFilename); 0183 path.setPath(path.path() + filename); 0184 setNewDestination(path); 0185 } 0186 if (change & Tc_Source) { 0187 m_source = QUrl(); 0188 QHash<QUrl, QPair<bool, int>> mirrors = m_dataSourceFactory->mirrors(); 0189 QHash<QUrl, QPair<bool, int>>::const_iterator it = mirrors.constBegin(); 0190 QHash<QUrl, QPair<bool, int>>::const_iterator end = mirrors.constEnd(); 0191 for (; it != end; it++) { 0192 if (it.value().first) { 0193 m_source = it.key(); 0194 break; 0195 } 0196 } 0197 } 0198 if (change & Tc_Status) { 0199 if ((m_dataSourceFactory->status() == Job::Finished) && m_source.scheme() == "ftp") { 0200 KIO::StatJob *statJob = KIO::stat(m_source); 0201 connect(statJob, &KJob::result, this, &TransferMultiSegKio::slotStatResult); 0202 statJob->start(); 0203 } else { 0204 setStatus(m_dataSourceFactory->status()); 0205 } 0206 0207 if (m_fileModel) { 0208 QModelIndex statusIndex = m_fileModel->index(m_dest, FileItem::Status); 0209 m_fileModel->setData(statusIndex, status()); 0210 } 0211 } 0212 if (change & Tc_TotalSize) { 0213 m_totalSize = m_dataSourceFactory->size(); 0214 if (m_fileModel) { 0215 QModelIndex sizeIndex = m_fileModel->index(m_dest, FileItem::Size); 0216 m_fileModel->setData(sizeIndex, static_cast<qlonglong>(m_totalSize)); 0217 } 0218 } 0219 if (change & Tc_DownloadedSize) { 0220 KIO::filesize_t processedSize = m_dataSourceFactory->downloadedSize(); 0221 // only start the verification search _after_ data has come in, that way only connections 0222 // are requested if there is already a successful one 0223 if ((processedSize != m_downloadedSize) && !m_verificationSearch && MultiSegKioSettings::useSearchVerification()) { 0224 m_verificationSearch = true; 0225 QDomDocument doc; 0226 QDomElement element = doc.createElement("TransferDataSource"); 0227 element.setAttribute("type", "checksumsearch"); 0228 doc.appendChild(element); 0229 0230 TransferDataSource *checksumSearch = KGet::createTransferDataSource(m_source, element, this); 0231 if (checksumSearch) { 0232 connect(checksumSearch, SIGNAL(data(QString, QString)), this, SLOT(slotChecksumFound(QString, QString))); 0233 checksumSearch->start(); 0234 } 0235 } 0236 m_downloadedSize = m_dataSourceFactory->downloadedSize(); 0237 } 0238 if (change & Tc_Percent) { 0239 m_percent = m_dataSourceFactory->percent(); 0240 } 0241 if (change & Tc_DownloadSpeed) { 0242 qCDebug(KGET_DEBUG) << "speed:" << m_downloadSpeed; 0243 m_downloadSpeed = m_dataSourceFactory->currentSpeed(); 0244 } 0245 0246 setTransferChange(change, true); 0247 } 0248 0249 void TransferMultiSegKio::slotVerified(bool isVerified) 0250 { 0251 if (m_fileModel) { 0252 QModelIndex checksumVerified = m_fileModel->index(m_dest, FileItem::ChecksumVerified); 0253 m_fileModel->setData(checksumVerified, verifier()->status()); 0254 } 0255 0256 if (!isVerified) { 0257 QString text; 0258 KGuiItem action; 0259 if (verifier()->partialChunkLength()) { 0260 text = i18n("The download (%1) could not be verified. Do you want to repair it?", m_dest.fileName()); 0261 action = KGuiItem(i18nc("@action:button", "Repair")); 0262 } else { 0263 text = i18n("The download (%1) could not be verified. Do you want to redownload it?", m_dest.fileName()); 0264 action = KGuiItem(i18nc("@action:button", "Download Again"), QStringLiteral("document-save")); 0265 } 0266 if (KMessageBox::warningTwoActions(nullptr, text, i18n("Verification failed."), action, KGuiItem(i18n("Ignore"), QStringLiteral("dialog-cancel"))) 0267 == KMessageBox::PrimaryAction) { 0268 repair(); 0269 } 0270 } 0271 } 0272 0273 void TransferMultiSegKio::slotStatResult(KJob *kioJob) 0274 { 0275 auto *statJob = qobject_cast<KIO::StatJob *>(kioJob); 0276 0277 if (!statJob->error()) { 0278 const KIO::UDSEntry entryResult = statJob->statResult(); 0279 struct utimbuf time; 0280 0281 time.modtime = entryResult.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME); 0282 time.actime = QDateTime::currentDateTime().toSecsSinceEpoch(); 0283 utime(m_dest.toLocalFile().toUtf8().constData(), &time); 0284 } 0285 0286 setStatus(Job::Finished); 0287 setTransferChange(Tc_Status, true); 0288 } 0289 0290 void TransferMultiSegKio::slotSearchUrls(const QList<QUrl> &urls) 0291 { 0292 qCDebug(KGET_DEBUG) << "Found " << urls.size() << " urls."; 0293 0294 foreach (const QUrl &url, urls) { 0295 m_dataSourceFactory->addMirror(url, MultiSegKioSettings::segments()); 0296 } 0297 } 0298 0299 void TransferMultiSegKio::slotChecksumFound(QString type, QString checksum) 0300 { 0301 m_dataSourceFactory->verifier()->addChecksum(type, checksum); 0302 } 0303 0304 QHash<QUrl, QPair<bool, int>> TransferMultiSegKio::availableMirrors(const QUrl &file) const 0305 { 0306 Q_UNUSED(file) 0307 0308 return m_dataSourceFactory->mirrors(); 0309 } 0310 0311 void TransferMultiSegKio::setAvailableMirrors(const QUrl &file, const QHash<QUrl, QPair<bool, int>> &mirrors) 0312 { 0313 Q_UNUSED(file) 0314 0315 m_dataSourceFactory->setMirrors(mirrors); 0316 0317 m_source = QUrl(); 0318 QHash<QUrl, QPair<bool, int>>::const_iterator it = mirrors.begin(); 0319 QHash<QUrl, QPair<bool, int>>::const_iterator end = mirrors.end(); 0320 for (; it != end; it++) { 0321 if (it.value().first) { 0322 m_source = it.key(); 0323 break; 0324 } 0325 } 0326 setTransferChange(Tc_Source, true); 0327 } 0328 0329 Verifier *TransferMultiSegKio::verifier(const QUrl &file) 0330 { 0331 Q_UNUSED(file) 0332 0333 return m_dataSourceFactory->verifier(); 0334 } 0335 0336 Signature *TransferMultiSegKio::signature(const QUrl &file) 0337 { 0338 Q_UNUSED(file) 0339 0340 return m_dataSourceFactory->signature(); 0341 } 0342 0343 FileModel *TransferMultiSegKio::fileModel() 0344 { 0345 if (!m_fileModel) { 0346 m_fileModel = new FileModel(QList<QUrl>() << m_dest, m_dest.adjusted(QUrl::RemoveFilename), this); 0347 connect(m_fileModel, SIGNAL(rename(QUrl, QUrl)), this, SLOT(slotRename(QUrl, QUrl))); 0348 0349 QModelIndex statusIndex = m_fileModel->index(m_dest, FileItem::Status); 0350 m_fileModel->setData(statusIndex, m_dataSourceFactory->status()); 0351 QModelIndex sizeIndex = m_fileModel->index(m_dest, FileItem::Size); 0352 m_fileModel->setData(sizeIndex, static_cast<qlonglong>(m_dataSourceFactory->size())); 0353 QModelIndex checksumVerified = m_fileModel->index(m_dest, FileItem::ChecksumVerified); 0354 m_fileModel->setData(checksumVerified, verifier()->status()); 0355 QModelIndex signatureVerified = m_fileModel->index(m_dest, FileItem::SignatureVerified); 0356 m_fileModel->setData(signatureVerified, signature()->status()); 0357 } 0358 0359 return m_fileModel; 0360 } 0361 0362 void TransferMultiSegKio::slotRename(const QUrl &oldUrl, const QUrl &newUrl) 0363 { 0364 Q_UNUSED(oldUrl) 0365 0366 if (newUrl.isValid() && (newUrl != dest()) && m_dataSourceFactory) { 0367 m_movingFile = true; 0368 stop(); 0369 m_dataSourceFactory->setNewDestination(newUrl); 0370 0371 m_dest = newUrl; 0372 0373 setTransferChange(Tc_FileName); 0374 } 0375 } 0376 0377 void TransferMultiSegKio::slotUpdateCapabilities() 0378 { 0379 setCapabilities(m_dataSourceFactory->capabilities()); 0380 } 0381 0382 #include "moc_transfermultisegkio.cpp"