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"