File indexing completed on 2024-04-28 04:57:31

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2004 Dario Massarin <nekkar@libero.it>
0004    Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net>
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 "transferKio.h"
0013 #include "core/signature.h"
0014 #include "core/verifier.h"
0015 #include "settings.h"
0016 
0017 #ifdef Q_OS_WIN
0018 #include <sys/utime.h>
0019 #else
0020 #include <utime.h>
0021 #endif
0022 
0023 #include "kget_debug.h"
0024 #include <QDebug>
0025 
0026 #include <KIO/CopyJob>
0027 #include <KIO/DeleteJob>
0028 #include <KIO/StatJob>
0029 #include <KIO/UDSEntry>
0030 #include <KLocalizedString>
0031 #include <KMessageBox>
0032 
0033 #include <QDomElement>
0034 #include <QFile>
0035 
0036 TransferKio::TransferKio(TransferGroup *parent, TransferFactory *factory, Scheduler *scheduler, const QUrl &source, const QUrl &dest, const QDomElement *e)
0037     : Transfer(parent, factory, scheduler, source, dest, e)
0038     , m_copyjob(nullptr)
0039     , m_movingFile(false)
0040     , m_verifier(nullptr)
0041     , m_signature(nullptr)
0042 {
0043     setCapabilities(Transfer::Cap_Moving | Transfer::Cap_Renaming | Transfer::Cap_Resuming); // TODO check if it really can resume
0044 }
0045 
0046 bool TransferKio::setDirectory(const QUrl &newDirectory)
0047 {
0048     QUrl newDest = newDirectory;
0049     newDest.setPath(newDest.adjusted(QUrl::RemoveFilename).toString() + m_dest.fileName());
0050     return setNewDestination(newDest);
0051 }
0052 
0053 bool TransferKio::setNewDestination(const QUrl &newDestination)
0054 {
0055     if (newDestination.isValid() && (newDestination != dest())) {
0056         QString oldPath = m_dest.toLocalFile() + ".part";
0057         if (QFile::exists(oldPath)) {
0058             m_movingFile = true;
0059             stop();
0060             setStatus(Job::Moving);
0061             setTransferChange(Tc_Status, true);
0062 
0063             m_dest = newDestination;
0064 
0065             if (m_verifier) {
0066                 m_verifier->setDestination(newDestination);
0067             }
0068             if (m_signature) {
0069                 m_signature->setDestination(newDestination);
0070             }
0071 
0072             KIO::FileCopyJob *move =
0073                 KIO::file_move(QUrl::fromLocalFile(oldPath), QUrl::fromLocalFile(newDestination.toLocalFile() + ".part"), -1, KIO::HideProgressInfo);
0074             connect(move, &KJob::result, this, &TransferKio::newDestResult);
0075             connect(move, &KJob::infoMessage, this, &TransferKio::slotInfoMessage);
0076             connect(move, &KJob::percentChanged, this, &TransferKio::slotPercent);
0077 
0078             return true;
0079         }
0080     }
0081     return false;
0082 }
0083 
0084 void TransferKio::newDestResult(KJob *result)
0085 {
0086     Q_UNUSED(result) // TODO handle errors etc.!
0087     m_movingFile = false;
0088     start();
0089     setTransferChange(Tc_FileName);
0090 }
0091 
0092 void TransferKio::start()
0093 {
0094     if (!m_movingFile && (status() != Finished)) {
0095         m_stopped = false;
0096         if (!m_copyjob)
0097             createJob();
0098 
0099         qCDebug(KGET_DEBUG) << "TransferKio::start";
0100         setStatus(Job::Running,
0101                   i18nc("transfer state: connecting", "Connecting...."),
0102                   "network-connect"); // should be "network-connecting", but that doesn't exist for KDE 4.0 yet
0103         setTransferChange(Tc_Status, true);
0104     }
0105 }
0106 
0107 void TransferKio::stop()
0108 {
0109     if ((status() == Stopped) || (status() == Finished)) {
0110         return;
0111     }
0112 
0113     m_stopped = true;
0114 
0115     if (m_copyjob) {
0116         m_copyjob->kill(KJob::EmitResult);
0117         m_copyjob = nullptr;
0118     }
0119 
0120     qCDebug(KGET_DEBUG) << "Stop";
0121     setStatus(Job::Stopped);
0122     m_downloadSpeed = 0;
0123     setTransferChange(Tc_Status | Tc_DownloadSpeed, true);
0124 }
0125 
0126 void TransferKio::deinit(Transfer::DeleteOptions options)
0127 {
0128     if (options & DeleteFiles) // if the transfer is not finished, we delete the *.part-file
0129     {
0130         KIO::Job *del = KIO::del(QUrl::fromLocalFile(m_dest.path() + ".part"), KIO::HideProgressInfo);
0131         if (!del->exec()) {
0132             qCDebug(KGET_DEBUG) << "Could not delete part " << QString(m_dest.path() + ".part");
0133         }
0134     } // TODO: Ask the user if he/she wants to delete the *.part-file? To discuss (boom1992)
0135 }
0136 
0137 // NOTE: INTERNAL METHODS
0138 
0139 void TransferKio::createJob()
0140 {
0141     if (!m_copyjob) {
0142         m_copyjob = KIO::file_copy(m_source, m_dest, -1, KIO::HideProgressInfo);
0143 
0144         connect(m_copyjob, &KJob::result, this, &TransferKio::slotResult);
0145         connect(m_copyjob, &KJob::infoMessage, this, &TransferKio::slotInfoMessage);
0146         connect(m_copyjob, &KJob::percentChanged, this, &TransferKio::slotPercent);
0147         connect(m_copyjob, &KJob::totalSize, this, &TransferKio::slotTotalSize);
0148         connect(m_copyjob, &KJob::processedSize, this, &TransferKio::slotProcessedSize);
0149         connect(m_copyjob, &KJob::speed, this, &TransferKio::slotSpeed);
0150     }
0151 }
0152 
0153 void TransferKio::slotResult(KJob *kioJob)
0154 {
0155     qCDebug(KGET_DEBUG) << "slotResult  (" << kioJob->error() << ")";
0156     switch (kioJob->error()) {
0157     case 0: // The download has finished
0158     case KIO::ERR_FILE_ALREADY_EXIST: // The file has already been downloaded.
0159         setStatus(Job::Finished);
0160         // "ok" icon should probably be "dialog-success", but we don't have that icon in KDE 4.0
0161         m_percent = 100;
0162         m_downloadSpeed = 0;
0163         m_downloadedSize = m_totalSize;
0164         setTransferChange(Tc_Percent | Tc_DownloadSpeed);
0165         break;
0166     default:
0167         // There has been an error
0168         qCDebug(KGET_DEBUG) << "--  E R R O R  (" << kioJob->error() << ")--";
0169         if (!m_stopped)
0170             setStatus(Job::Aborted);
0171         break;
0172     }
0173     // when slotResult gets called, the m_copyjob has already been deleted!
0174     m_copyjob = nullptr;
0175 
0176     // If it is an ftp file, there's still work to do
0177     Transfer::ChangesFlags flags = (m_source.scheme() != "ftp") ? Tc_Status : Tc_None;
0178     if (status() == Job::Finished) {
0179         if (!m_totalSize) {
0180             // downloaded elsewhere already, e.g. Konqueror
0181             if (!m_downloadedSize) {
0182                 QFile file(m_dest.toLocalFile() + ".part");
0183                 m_downloadedSize = file.size();
0184                 if (!m_downloadedSize) {
0185                     QFile file(m_dest.toLocalFile());
0186                     m_downloadedSize = file.size();
0187                 }
0188             }
0189             m_totalSize = m_downloadedSize;
0190             flags |= Tc_DownloadedSize;
0191         }
0192         if (m_verifier && Settings::checksumAutomaticVerification()) {
0193             m_verifier->verify();
0194         }
0195         if (m_signature && Settings::signatureAutomaticVerification()) {
0196             m_signature->verify();
0197         }
0198     }
0199 
0200     if (m_source.scheme() == "ftp") {
0201         KIO::StatJob *statJob = KIO::stat(m_source);
0202         connect(statJob, &KJob::result, this, &TransferKio::slotStatResult);
0203         statJob->start();
0204     }
0205 
0206     setTransferChange(flags, true);
0207 }
0208 
0209 void TransferKio::slotInfoMessage(KJob *kioJob, const QString &msg)
0210 {
0211     Q_UNUSED(kioJob)
0212     m_log.append(QString(msg));
0213 }
0214 
0215 void TransferKio::slotPercent(KJob *kioJob, unsigned long percent)
0216 {
0217     qCDebug(KGET_DEBUG) << "slotPercent";
0218     Q_UNUSED(kioJob)
0219     m_percent = percent;
0220     setTransferChange(Tc_Percent, true);
0221 }
0222 
0223 void TransferKio::slotTotalSize(KJob *kioJob, qulonglong size)
0224 {
0225     Q_UNUSED(kioJob)
0226 
0227     qCDebug(KGET_DEBUG) << "slotTotalSize";
0228 
0229     setStatus(Job::Running);
0230 
0231     m_totalSize = size;
0232     setTransferChange(Tc_Status | Tc_TotalSize, true);
0233 }
0234 
0235 void TransferKio::slotProcessedSize(KJob *kioJob, qulonglong size)
0236 {
0237     Q_UNUSED(kioJob)
0238 
0239     //     qCDebug(KGET_DEBUG) << "slotProcessedSize";
0240 
0241     if (status() != Job::Running) {
0242         setStatus(Job::Running);
0243         setTransferChange(Tc_Status);
0244     }
0245     m_downloadedSize = size;
0246     setTransferChange(Tc_DownloadedSize, true);
0247 }
0248 
0249 void TransferKio::slotSpeed(KJob *kioJob, unsigned long bytes_per_second)
0250 {
0251     Q_UNUSED(kioJob)
0252 
0253     //     qCDebug(KGET_DEBUG) << "slotSpeed";
0254 
0255     if (status() != Job::Running) {
0256         if (m_movingFile)
0257             setStatus(Job::Moving);
0258         else
0259             setStatus(Job::Running);
0260         setTransferChange(Tc_Status);
0261     }
0262 
0263     m_downloadSpeed = bytes_per_second;
0264     setTransferChange(Tc_DownloadSpeed, true);
0265 }
0266 
0267 void TransferKio::slotVerified(bool isVerified)
0268 {
0269     if (!isVerified) {
0270         QString text;
0271         KGuiItem action;
0272         if (verifier()->partialChunkLength()) {
0273             text = i18n("The download (%1) could not be verified. Do you want to repair it?", m_dest.fileName());
0274             action = KGuiItem(i18nc("@action:button", "Repair"));
0275         } else {
0276             text = i18n("The download (%1) could not be verified. Do you want to redownload it?", m_dest.fileName());
0277             action = KGuiItem(i18nc("@action:button", "Download Again"), QStringLiteral("document-save"));
0278         }
0279         if (KMessageBox::warningTwoActions(nullptr, text, i18n("Verification failed."), action, KGuiItem(i18n("Ignore"), QStringLiteral("dialog-cancel")))
0280             == KMessageBox::PrimaryAction) {
0281             repair();
0282         }
0283     }
0284 }
0285 
0286 void TransferKio::slotStatResult(KJob *kioJob)
0287 {
0288     auto *statJob = qobject_cast<KIO::StatJob *>(kioJob);
0289 
0290     if (!statJob->error()) {
0291         const KIO::UDSEntry entryResult = statJob->statResult();
0292         struct utimbuf time;
0293 
0294         time.modtime = entryResult.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME);
0295         time.actime = QDateTime::currentDateTime().toSecsSinceEpoch();
0296         utime(m_dest.toLocalFile().toUtf8().constData(), &time);
0297     }
0298 
0299     setStatus(Job::Finished);
0300     setTransferChange(Tc_Status, true);
0301 }
0302 
0303 bool TransferKio::repair(const QUrl &file)
0304 {
0305     Q_UNUSED(file)
0306 
0307     if (verifier()->status() == Verifier::NotVerified) {
0308         m_downloadedSize = 0;
0309         m_percent = 0;
0310         if (m_copyjob) {
0311             m_copyjob->kill(KJob::Quietly);
0312             m_copyjob = nullptr;
0313         }
0314         setTransferChange(Tc_DownloadedSize | Tc_Percent, true);
0315 
0316         start();
0317 
0318         return true;
0319     }
0320 
0321     return false;
0322 }
0323 
0324 Verifier *TransferKio::verifier(const QUrl &file)
0325 {
0326     Q_UNUSED(file)
0327 
0328     if (!m_verifier) {
0329         m_verifier = new Verifier(m_dest, this);
0330         connect(m_verifier, &Verifier::verified, this, &TransferKio::slotVerified);
0331     }
0332 
0333     return m_verifier;
0334 }
0335 
0336 Signature *TransferKio::signature(const QUrl &file)
0337 {
0338     Q_UNUSED(file)
0339 
0340     if (!m_signature) {
0341         m_signature = new Signature(m_dest, this);
0342     }
0343 
0344     return m_signature;
0345 }
0346 
0347 #include "moc_transferKio.cpp"