File indexing completed on 2024-09-29 12:07:37
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org> 0004 SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "filecopyjob.h" 0010 #include "askuseractioninterface.h" 0011 #include "job_p.h" 0012 #include "kprotocolmanager.h" 0013 #include "scheduler.h" 0014 #include "slave.h" 0015 #include <kio/jobuidelegatefactory.h> 0016 0017 #include <KLocalizedString> 0018 0019 #include <QFile> 0020 #include <QTimer> 0021 0022 using namespace KIO; 0023 0024 static inline Slave *jobSlave(SimpleJob *job) 0025 { 0026 return SimpleJobPrivate::get(job)->m_slave; 0027 } 0028 0029 /** @internal */ 0030 class KIO::FileCopyJobPrivate : public KIO::JobPrivate 0031 { 0032 public: 0033 FileCopyJobPrivate(const QUrl &src, const QUrl &dest, int permissions, bool move, JobFlags flags) 0034 : m_sourceSize(filesize_t(-1)) 0035 , m_src(src) 0036 , m_dest(dest) 0037 , m_moveJob(nullptr) 0038 , m_copyJob(nullptr) 0039 , m_delJob(nullptr) 0040 , m_chmodJob(nullptr) 0041 , m_getJob(nullptr) 0042 , m_putJob(nullptr) 0043 , m_permissions(permissions) 0044 , m_move(move) 0045 , m_mustChmod(0) 0046 , m_bFileCopyInProgress(false) 0047 , m_flags(flags) 0048 { 0049 } 0050 KIO::filesize_t m_sourceSize; 0051 QDateTime m_modificationTime; 0052 QUrl m_src; 0053 QUrl m_dest; 0054 QByteArray m_buffer; 0055 SimpleJob *m_moveJob; 0056 SimpleJob *m_copyJob; 0057 SimpleJob *m_delJob; 0058 SimpleJob *m_chmodJob; 0059 TransferJob *m_getJob; 0060 TransferJob *m_putJob; 0061 int m_permissions; 0062 bool m_move : 1; 0063 bool m_canResume : 1; 0064 bool m_resumeAnswerSent : 1; 0065 bool m_mustChmod : 1; 0066 bool m_bFileCopyInProgress : 1; 0067 JobFlags m_flags; 0068 0069 void startBestCopyMethod(); 0070 void startCopyJob(); 0071 void startCopyJob(const QUrl &workerUrl); 0072 void startRenameJob(const QUrl &workerUrl); 0073 void startDataPump(); 0074 void connectSubjob(SimpleJob *job); 0075 0076 void slotStart(); 0077 void slotData(KIO::Job *, const QByteArray &data); 0078 void slotDataReq(KIO::Job *, QByteArray &data); 0079 void slotMimetype(KIO::Job *, const QString &type); 0080 /** 0081 * Forward signal from subjob 0082 * @param job the job that emitted this signal 0083 * @param offset the offset to resume from 0084 */ 0085 void slotCanResume(KIO::Job *job, KIO::filesize_t offset); 0086 void processCanResumeResult(KIO::Job *job, RenameDialog_Result result, KIO::filesize_t offset); 0087 0088 Q_DECLARE_PUBLIC(FileCopyJob) 0089 0090 static inline FileCopyJob *newJob(const QUrl &src, const QUrl &dest, int permissions, bool move, JobFlags flags) 0091 { 0092 // qDebug() << src << "->" << dest; 0093 FileCopyJob *job = new FileCopyJob(*new FileCopyJobPrivate(src, dest, permissions, move, flags)); 0094 job->setProperty("destUrl", dest.toString()); 0095 job->setUiDelegate(KIO::createDefaultJobUiDelegate()); 0096 if (!(flags & HideProgressInfo)) { 0097 KIO::getJobTracker()->registerJob(job); 0098 } 0099 if (!(flags & NoPrivilegeExecution)) { 0100 job->d_func()->m_privilegeExecutionEnabled = true; 0101 job->d_func()->m_operationType = move ? Move : Copy; 0102 } 0103 return job; 0104 } 0105 }; 0106 0107 static bool isSrcDestSameSlaveProcess(const QUrl &src, const QUrl &dest) 0108 { 0109 /* clang-format off */ 0110 return src.scheme() == dest.scheme() 0111 && src.host() == dest.host() 0112 && src.port() == dest.port() 0113 && src.userName() == dest.userName() 0114 && src.password() == dest.password(); 0115 /* clang-format on */ 0116 } 0117 0118 /* 0119 * The FileCopyJob works according to the famous Bavarian 0120 * 'Alternating Bitburger Protocol': we either drink a beer or we 0121 * we order a beer, but never both at the same time. 0122 * Translated to KIO workers: We alternate between receiving a block of data 0123 * and sending it away. 0124 */ 0125 FileCopyJob::FileCopyJob(FileCopyJobPrivate &dd) 0126 : Job(dd) 0127 { 0128 Q_D(FileCopyJob); 0129 QTimer::singleShot(0, this, [d]() { 0130 d->slotStart(); 0131 }); 0132 } 0133 0134 void FileCopyJobPrivate::slotStart() 0135 { 0136 Q_Q(FileCopyJob); 0137 if (!m_move) { 0138 JobPrivate::emitCopying(q, m_src, m_dest); 0139 } else { 0140 JobPrivate::emitMoving(q, m_src, m_dest); 0141 } 0142 0143 if (m_move) { 0144 // The if() below must be the same as the one in startBestCopyMethod 0145 if (isSrcDestSameSlaveProcess(m_src, m_dest)) { 0146 startRenameJob(m_src); 0147 return; 0148 } else if (m_src.isLocalFile() && KProtocolManager::canRenameFromFile(m_dest)) { 0149 startRenameJob(m_dest); 0150 return; 0151 } else if (m_dest.isLocalFile() && KProtocolManager::canRenameToFile(m_src)) { 0152 startRenameJob(m_src); 0153 return; 0154 } 0155 // No fast-move available, use copy + del. 0156 } 0157 startBestCopyMethod(); 0158 } 0159 0160 void FileCopyJobPrivate::startBestCopyMethod() 0161 { 0162 if (isSrcDestSameSlaveProcess(m_src, m_dest)) { 0163 startCopyJob(); 0164 } else if (m_src.isLocalFile() && KProtocolManager::canCopyFromFile(m_dest)) { 0165 startCopyJob(m_dest); 0166 } else if (m_dest.isLocalFile() && KProtocolManager::canCopyToFile(m_src) && !KIO::Scheduler::isWorkerOnHoldFor(m_src)) { 0167 startCopyJob(m_src); 0168 } else { 0169 startDataPump(); 0170 } 0171 } 0172 0173 FileCopyJob::~FileCopyJob() 0174 { 0175 } 0176 0177 void FileCopyJob::setSourceSize(KIO::filesize_t size) 0178 { 0179 Q_D(FileCopyJob); 0180 d->m_sourceSize = size; 0181 if (size != (KIO::filesize_t)-1) { 0182 setTotalAmount(KJob::Bytes, size); 0183 } 0184 } 0185 0186 void FileCopyJob::setModificationTime(const QDateTime &mtime) 0187 { 0188 Q_D(FileCopyJob); 0189 d->m_modificationTime = mtime; 0190 } 0191 0192 QUrl FileCopyJob::srcUrl() const 0193 { 0194 return d_func()->m_src; 0195 } 0196 0197 QUrl FileCopyJob::destUrl() const 0198 { 0199 return d_func()->m_dest; 0200 } 0201 0202 void FileCopyJobPrivate::startCopyJob() 0203 { 0204 startCopyJob(m_src); 0205 } 0206 0207 void FileCopyJobPrivate::startCopyJob(const QUrl &workerUrl) 0208 { 0209 Q_Q(FileCopyJob); 0210 // qDebug(); 0211 KIO_ARGS << m_src << m_dest << m_permissions << (qint8)(m_flags & Overwrite); 0212 auto job = new DirectCopyJob(workerUrl, packedArgs); 0213 m_copyJob = job; 0214 m_copyJob->setParentJob(q); 0215 if (m_modificationTime.isValid()) { 0216 m_copyJob->addMetaData(QStringLiteral("modified"), m_modificationTime.toString(Qt::ISODate)); // #55804 0217 } 0218 q->addSubjob(m_copyJob); 0219 connectSubjob(m_copyJob); 0220 q->connect(job, &DirectCopyJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) { 0221 slotCanResume(job, offset); 0222 }); 0223 } 0224 0225 void FileCopyJobPrivate::startRenameJob(const QUrl &workerUrl) 0226 { 0227 Q_Q(FileCopyJob); 0228 m_mustChmod = true; // CMD_RENAME by itself doesn't change permissions 0229 KIO_ARGS << m_src << m_dest << (qint8)(m_flags & Overwrite); 0230 m_moveJob = SimpleJobPrivate::newJobNoUi(workerUrl, CMD_RENAME, packedArgs); 0231 m_moveJob->setParentJob(q); 0232 if (m_modificationTime.isValid()) { 0233 m_moveJob->addMetaData(QStringLiteral("modified"), m_modificationTime.toString(Qt::ISODate)); // #55804 0234 } 0235 q->addSubjob(m_moveJob); 0236 connectSubjob(m_moveJob); 0237 } 0238 0239 void FileCopyJobPrivate::connectSubjob(SimpleJob *job) 0240 { 0241 Q_Q(FileCopyJob); 0242 q->connect(job, &KJob::totalSize, q, [q](KJob *job, qulonglong totalSize) { 0243 Q_UNUSED(job); 0244 if (totalSize != q->totalAmount(KJob::Bytes)) { 0245 q->setTotalAmount(KJob::Bytes, totalSize); 0246 } 0247 }); 0248 0249 q->connect(job, &KJob::processedSize, q, [q, this](const KJob *job, qulonglong processedSize) { 0250 if (job == m_copyJob) { 0251 m_bFileCopyInProgress = processedSize > 0; 0252 } 0253 q->setProcessedAmount(KJob::Bytes, processedSize); 0254 }); 0255 0256 q->connect(job, &KJob::percentChanged, q, [q](KJob *, ulong percent) { 0257 if (percent > q->percent()) { 0258 q->setPercent(percent); 0259 } 0260 }); 0261 0262 if (q->isSuspended()) { 0263 job->suspend(); 0264 } 0265 } 0266 0267 bool FileCopyJob::doSuspend() 0268 { 0269 Q_D(FileCopyJob); 0270 if (d->m_moveJob) { 0271 d->m_moveJob->suspend(); 0272 } 0273 0274 if (d->m_copyJob) { 0275 d->m_copyJob->suspend(); 0276 } 0277 0278 if (d->m_getJob) { 0279 d->m_getJob->suspend(); 0280 } 0281 0282 if (d->m_putJob) { 0283 d->m_putJob->suspend(); 0284 } 0285 0286 Job::doSuspend(); 0287 return true; 0288 } 0289 0290 bool FileCopyJob::doResume() 0291 { 0292 Q_D(FileCopyJob); 0293 if (d->m_moveJob) { 0294 d->m_moveJob->resume(); 0295 } 0296 0297 if (d->m_copyJob) { 0298 d->m_copyJob->resume(); 0299 } 0300 0301 if (d->m_getJob) { 0302 d->m_getJob->resume(); 0303 } 0304 0305 if (d->m_putJob) { 0306 d->m_putJob->resume(); 0307 } 0308 0309 Job::doResume(); 0310 return true; 0311 } 0312 0313 void FileCopyJobPrivate::startDataPump() 0314 { 0315 Q_Q(FileCopyJob); 0316 // qDebug(); 0317 0318 m_canResume = false; 0319 m_resumeAnswerSent = false; 0320 m_getJob = nullptr; // for now 0321 m_putJob = put(m_dest, m_permissions, (m_flags | HideProgressInfo) /* no GUI */); 0322 m_putJob->setParentJob(q); 0323 // qDebug() << "m_putJob=" << m_putJob << "m_dest=" << m_dest; 0324 if (m_modificationTime.isValid()) { 0325 m_putJob->setModificationTime(m_modificationTime); 0326 } 0327 0328 // The first thing the put job will tell us is whether we can 0329 // resume or not (this is always emitted) 0330 q->connect(m_putJob, &KIO::TransferJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) { 0331 slotCanResume(job, offset); 0332 }); 0333 q->connect(m_putJob, &KIO::TransferJob::dataReq, q, [this](KIO::Job *job, QByteArray &data) { 0334 slotDataReq(job, data); 0335 }); 0336 q->addSubjob(m_putJob); 0337 } 0338 0339 void FileCopyJobPrivate::slotCanResume(KIO::Job *job, KIO::filesize_t offset) 0340 { 0341 Q_Q(FileCopyJob); 0342 0343 if (job == m_getJob) { 0344 // Cool, the get job said ok, we can resume 0345 m_canResume = true; 0346 // qDebug() << "'can resume' from the GET job -> we can resume"; 0347 0348 jobSlave(m_getJob)->setOffset(jobSlave(m_putJob)->offset()); 0349 return; 0350 } 0351 0352 if (job == m_putJob || job == m_copyJob) { 0353 // qDebug() << "'can resume' from PUT job. offset=" << KIO::number(offset); 0354 if (offset == 0) { 0355 m_resumeAnswerSent = true; // No need for an answer 0356 } else { 0357 KIO::Job *kioJob = q->parentJob() ? q->parentJob() : q; 0358 auto *askUserActionInterface = KIO::delegateExtension<KIO::AskUserActionInterface *>(kioJob); 0359 if (!KProtocolManager::autoResume() && !(m_flags & Overwrite) && askUserActionInterface) { 0360 auto renameSignal = &AskUserActionInterface::askUserRenameResult; 0361 0362 q->connect(askUserActionInterface, renameSignal, q, [=](KIO::RenameDialog_Result result, const QUrl &, const KJob *askJob) { 0363 Q_ASSERT(kioJob == askJob); 0364 0365 // Only receive askUserRenameResult once per rename dialog 0366 QObject::disconnect(askUserActionInterface, renameSignal, q, nullptr); 0367 0368 processCanResumeResult(job, result, offset); 0369 }); 0370 0371 // Ask confirmation about resuming previous transfer 0372 askUserActionInterface->askUserRename(kioJob, 0373 i18n("File Already Exists"), 0374 m_src, 0375 m_dest, 0376 RenameDialog_Options(RenameDialog_Overwrite | RenameDialog_Resume | RenameDialog_NoRename), 0377 m_sourceSize, 0378 offset); 0379 return; 0380 } 0381 } 0382 0383 processCanResumeResult(job, // 0384 Result_Resume, // The default is to resume 0385 offset); 0386 0387 return; 0388 } 0389 0390 qCWarning(KIO_CORE) << "unknown job=" << job << "m_getJob=" << m_getJob << "m_putJob=" << m_putJob; 0391 } 0392 0393 void FileCopyJobPrivate::processCanResumeResult(KIO::Job *job, RenameDialog_Result result, KIO::filesize_t offset) 0394 { 0395 Q_Q(FileCopyJob); 0396 if (result == Result_Overwrite || (m_flags & Overwrite)) { 0397 offset = 0; 0398 } else if (result == Result_Cancel) { 0399 if (job == m_putJob) { 0400 m_putJob->kill(FileCopyJob::Quietly); 0401 q->removeSubjob(m_putJob); 0402 m_putJob = nullptr; 0403 } else { 0404 m_copyJob->kill(FileCopyJob::Quietly); 0405 q->removeSubjob(m_copyJob); 0406 m_copyJob = nullptr; 0407 } 0408 q->setError(ERR_USER_CANCELED); 0409 q->emitResult(); 0410 return; 0411 } 0412 0413 if (job == m_copyJob) { 0414 jobSlave(m_copyJob)->sendResumeAnswer(offset != 0); 0415 return; 0416 } 0417 0418 if (job == m_putJob) { 0419 m_getJob = KIO::get(m_src, NoReload, HideProgressInfo /* no GUI */); 0420 m_getJob->setParentJob(q); 0421 // qDebug() << "m_getJob=" << m_getJob << m_src; 0422 m_getJob->addMetaData(QStringLiteral("errorPage"), QStringLiteral("false")); 0423 m_getJob->addMetaData(QStringLiteral("AllowCompressedPage"), QStringLiteral("false")); 0424 // Set size in subjob. This helps if the worker doesn't emit totalSize. 0425 if (m_sourceSize != (KIO::filesize_t)-1) { 0426 m_getJob->setTotalAmount(KJob::Bytes, m_sourceSize); 0427 } 0428 0429 if (offset) { 0430 // qDebug() << "Setting metadata for resume to" << (unsigned long) offset; 0431 m_getJob->addMetaData(QStringLiteral("range-start"), KIO::number(offset)); 0432 0433 // Might or might not get emitted 0434 q->connect(m_getJob, &KIO::TransferJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) { 0435 slotCanResume(job, offset); 0436 }); 0437 } 0438 jobSlave(m_putJob)->setOffset(offset); 0439 0440 m_putJob->d_func()->internalSuspend(); 0441 q->addSubjob(m_getJob); 0442 connectSubjob(m_getJob); // Progress info depends on get 0443 m_getJob->d_func()->internalResume(); // Order a beer 0444 0445 q->connect(m_getJob, &KIO::TransferJob::data, q, [this](KIO::Job *job, const QByteArray &data) { 0446 slotData(job, data); 0447 }); 0448 q->connect(m_getJob, &KIO::TransferJob::mimeTypeFound, q, [this](KIO::Job *job, const QString &type) { 0449 slotMimetype(job, type); 0450 }); 0451 } 0452 } 0453 0454 void FileCopyJobPrivate::slotData(KIO::Job *, const QByteArray &data) 0455 { 0456 // qDebug() << "data size:" << data.size(); 0457 Q_ASSERT(m_putJob); 0458 if (!m_putJob) { 0459 return; // Don't crash 0460 } 0461 m_getJob->d_func()->internalSuspend(); 0462 m_putJob->d_func()->internalResume(); // Drink the beer 0463 m_buffer += data; 0464 0465 // On the first set of data incoming, we tell the "put" worker about our 0466 // decision about resuming 0467 if (!m_resumeAnswerSent) { 0468 m_resumeAnswerSent = true; 0469 // qDebug() << "(first time) -> send resume answer " << m_canResume; 0470 jobSlave(m_putJob)->sendResumeAnswer(m_canResume); 0471 } 0472 } 0473 0474 void FileCopyJobPrivate::slotDataReq(KIO::Job *, QByteArray &data) 0475 { 0476 Q_Q(FileCopyJob); 0477 // qDebug(); 0478 if (!m_resumeAnswerSent && !m_getJob) { 0479 // This can't happen 0480 q->setError(ERR_INTERNAL); 0481 q->setErrorText(QStringLiteral("'Put' job did not send canResume or 'Get' job did not send data!")); 0482 m_putJob->kill(FileCopyJob::Quietly); 0483 q->removeSubjob(m_putJob); 0484 m_putJob = nullptr; 0485 q->emitResult(); 0486 return; 0487 } 0488 if (m_getJob) { 0489 m_getJob->d_func()->internalResume(); // Order more beer 0490 m_putJob->d_func()->internalSuspend(); 0491 } 0492 data = m_buffer; 0493 m_buffer = QByteArray(); 0494 } 0495 0496 void FileCopyJobPrivate::slotMimetype(KIO::Job *, const QString &type) 0497 { 0498 Q_Q(FileCopyJob); 0499 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 78) 0500 Q_EMIT q->mimetype(q, type); 0501 #endif 0502 Q_EMIT q->mimeTypeFound(q, type); 0503 } 0504 0505 void FileCopyJob::slotResult(KJob *job) 0506 { 0507 Q_D(FileCopyJob); 0508 // qDebug() << "this=" << this << "job=" << job; 0509 removeSubjob(job); 0510 0511 // If result comes from copyjob then we are not writing anymore. 0512 if (job == d->m_copyJob) { 0513 d->m_bFileCopyInProgress = false; 0514 } 0515 0516 // Did job have an error ? 0517 if (job->error()) { 0518 if ((job == d->m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) { 0519 d->m_moveJob = nullptr; 0520 d->startBestCopyMethod(); 0521 return; 0522 } else if ((job == d->m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) { 0523 d->m_copyJob = nullptr; 0524 d->startDataPump(); 0525 return; 0526 } else if (job == d->m_getJob) { 0527 d->m_getJob = nullptr; 0528 if (d->m_putJob) { 0529 d->m_putJob->kill(Quietly); 0530 removeSubjob(d->m_putJob); 0531 } 0532 } else if (job == d->m_putJob) { 0533 d->m_putJob = nullptr; 0534 if (d->m_getJob) { 0535 d->m_getJob->kill(Quietly); 0536 removeSubjob(d->m_getJob); 0537 } 0538 } else if (job == d->m_chmodJob) { 0539 d->m_chmodJob = nullptr; 0540 if (d->m_delJob) { 0541 d->m_delJob->kill(Quietly); 0542 removeSubjob(d->m_delJob); 0543 } 0544 } else if (job == d->m_delJob) { 0545 d->m_delJob = nullptr; 0546 if (d->m_chmodJob) { 0547 d->m_chmodJob->kill(Quietly); 0548 removeSubjob(d->m_chmodJob); 0549 } 0550 } 0551 setError(job->error()); 0552 setErrorText(job->errorText()); 0553 emitResult(); 0554 return; 0555 } 0556 0557 if (d->m_mustChmod) { 0558 // If d->m_permissions == -1, keep the default permissions 0559 if (d->m_permissions != -1) { 0560 d->m_chmodJob = chmod(d->m_dest, d->m_permissions); 0561 addSubjob(d->m_chmodJob); 0562 } 0563 d->m_mustChmod = false; 0564 } 0565 0566 if (job == d->m_moveJob) { 0567 d->m_moveJob = nullptr; // Finished 0568 } 0569 0570 if (job == d->m_copyJob) { 0571 d->m_copyJob = nullptr; 0572 if (d->m_move) { 0573 d->m_delJob = file_delete(d->m_src, HideProgressInfo /*no GUI*/); // Delete source 0574 addSubjob(d->m_delJob); 0575 } 0576 } 0577 0578 if (job == d->m_getJob) { 0579 // qDebug() << "m_getJob finished"; 0580 d->m_getJob = nullptr; // No action required 0581 if (d->m_putJob) { 0582 d->m_putJob->d_func()->internalResume(); 0583 } 0584 } 0585 0586 if (job == d->m_putJob) { 0587 // qDebug() << "m_putJob finished"; 0588 d->m_putJob = nullptr; 0589 if (d->m_getJob) { 0590 // The get job is still running, probably after emitting data(QByteArray()) 0591 // and before we receive its finished(). 0592 d->m_getJob->d_func()->internalResume(); 0593 } 0594 if (d->m_move) { 0595 d->m_delJob = file_delete(d->m_src, HideProgressInfo /*no GUI*/); // Delete source 0596 addSubjob(d->m_delJob); 0597 } 0598 } 0599 0600 if (job == d->m_delJob) { 0601 d->m_delJob = nullptr; // Finished 0602 } 0603 0604 if (job == d->m_chmodJob) { 0605 d->m_chmodJob = nullptr; // Finished 0606 } 0607 0608 if (!hasSubjobs()) { 0609 emitResult(); 0610 } 0611 } 0612 0613 bool FileCopyJob::doKill() 0614 { 0615 #ifdef Q_OS_WIN 0616 // TODO Use SetConsoleCtrlHandler on Windows or similar behaviour. 0617 // https://stackoverflow.com/questions/2007516/is-there-a-posix-sigterm-alternative-on-windows-a-gentle-kill-for-console-ap 0618 // https://danielkaes.wordpress.com/2009/06/04/how-to-catch-kill-events-with-python/ 0619 // https://phabricator.kde.org/D25117#566107 0620 0621 Q_D(FileCopyJob); 0622 0623 // If we are interrupted in the middle of file copying, 0624 // we may end up with corrupted file at the destination. 0625 // It is better to clean up this file. If a copy is being 0626 // made as part of move operation then delete the dest only if 0627 // source file is intact (m_delJob == NULL). 0628 if (d->m_bFileCopyInProgress && d->m_copyJob && d->m_dest.isLocalFile()) { 0629 if (d->m_flags & Overwrite) { 0630 QFile::remove(d->m_dest.toLocalFile() + QStringLiteral(".part")); 0631 } else { 0632 QFile::remove(d->m_dest.toLocalFile()); 0633 } 0634 } 0635 #endif 0636 return Job::doKill(); 0637 } 0638 0639 FileCopyJob *KIO::file_copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) 0640 { 0641 return FileCopyJobPrivate::newJob(src, dest, permissions, false, flags); 0642 } 0643 0644 FileCopyJob *KIO::file_move(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) 0645 { 0646 FileCopyJob *job = FileCopyJobPrivate::newJob(src, dest, permissions, true, flags); 0647 if (job->uiDelegateExtension()) { 0648 job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::UpdateContent); 0649 } 0650 return job; 0651 } 0652 0653 #include "moc_filecopyjob.cpp"