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"