diff --git a/src/core/filecopyjob.cpp b/src/core/filecopyjob.cpp index 1b99de85..ba2eb5f2 100644 --- a/src/core/filecopyjob.cpp +++ b/src/core/filecopyjob.cpp @@ -1,593 +1,573 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2009 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "filecopyjob.h" #include "job_p.h" #include #include "kprotocolmanager.h" #include "scheduler.h" #include "slave.h" #include using namespace KIO; static inline Slave *jobSlave(SimpleJob *job) { return SimpleJobPrivate::get(job)->m_slave; } /** @internal */ class KIO::FileCopyJobPrivate: public KIO::JobPrivate { public: FileCopyJobPrivate(const QUrl &src, const QUrl &dest, int permissions, bool move, JobFlags flags) : m_sourceSize(filesize_t(-1)), m_src(src), m_dest(dest), m_moveJob(nullptr), m_copyJob(nullptr), m_delJob(nullptr), m_chmodJob(nullptr), m_getJob(nullptr), m_putJob(nullptr), m_permissions(permissions), m_move(move), m_mustChmod(0), m_flags(flags) { } KIO::filesize_t m_sourceSize; QDateTime m_modificationTime; QUrl m_src; QUrl m_dest; QByteArray m_buffer; SimpleJob *m_moveJob; SimpleJob *m_copyJob; SimpleJob *m_delJob; SimpleJob *m_chmodJob; TransferJob *m_getJob; TransferJob *m_putJob; int m_permissions; bool m_move: 1; bool m_canResume: 1; bool m_resumeAnswerSent: 1; bool m_mustChmod: 1; JobFlags m_flags; void startBestCopyMethod(); void startCopyJob(); void startCopyJob(const QUrl &slave_url); void startRenameJob(const QUrl &slave_url); void startDataPump(); void connectSubjob(SimpleJob *job); void slotStart(); void slotData(KIO::Job *, const QByteArray &data); void slotDataReq(KIO::Job *, QByteArray &data); void slotMimetype(KIO::Job *, const QString &type); - /** - * Forward signal from subjob - * @param job the job that emitted this signal - * @param size the processed size in bytes - */ - void slotProcessedSize(KJob *job, qulonglong size); - /** - * Forward signal from subjob - * @param job the job that emitted this signal - * @param size the total size - */ - void slotTotalSize(KJob *job, qulonglong size); - /** - * Forward signal from subjob - * @param job the job that emitted this signal - * @param pct the percentage - */ - void slotPercent(KJob *job, unsigned long pct); /** * Forward signal from subjob * @param job the job that emitted this signal * @param offset the offset to resume from */ void slotCanResume(KIO::Job *job, KIO::filesize_t offset); Q_DECLARE_PUBLIC(FileCopyJob) static inline FileCopyJob *newJob(const QUrl &src, const QUrl &dest, int permissions, bool move, JobFlags flags) { //qDebug() << src << "->" << dest; FileCopyJob *job = new FileCopyJob( *new FileCopyJobPrivate(src, dest, permissions, move, flags)); job->setProperty("destUrl", dest.toString()); job->setUiDelegate(KIO::createDefaultJobUiDelegate()); if (!(flags & HideProgressInfo)) { KIO::getJobTracker()->registerJob(job); } if (!(flags & NoPrivilegeExecution)) { job->d_func()->m_privilegeExecutionEnabled = true; job->d_func()->m_operationType = move ? Move : Copy; } return job; } }; /* * The FileCopyJob works according to the famous Bavarian * 'Alternating Bitburger Protocol': we either drink a beer or we * we order a beer, but never both at the same time. * Translated to io-slaves: We alternate between receiving a block of data * and sending it away. */ FileCopyJob::FileCopyJob(FileCopyJobPrivate &dd) : Job(dd) { - //qDebug(); - QTimer::singleShot(0, this, SLOT(slotStart())); + Q_D(FileCopyJob); + QTimer::singleShot(0, this, [d]() { + d->slotStart(); + }); } void FileCopyJobPrivate::slotStart() { Q_Q(FileCopyJob); if (!m_move) { JobPrivate::emitCopying(q, m_src, m_dest); } else { JobPrivate::emitMoving(q, m_src, m_dest); } if (m_move) { // The if() below must be the same as the one in startBestCopyMethod if ((m_src.scheme() == m_dest.scheme()) && (m_src.host() == m_dest.host()) && (m_src.port() == m_dest.port()) && (m_src.userName() == m_dest.userName()) && (m_src.password() == m_dest.password())) { startRenameJob(m_src); return; } else if (m_src.isLocalFile() && KProtocolManager::canRenameFromFile(m_dest)) { startRenameJob(m_dest); return; } else if (m_dest.isLocalFile() && KProtocolManager::canRenameToFile(m_src)) { startRenameJob(m_src); return; } // No fast-move available, use copy + del. } startBestCopyMethod(); } void FileCopyJobPrivate::startBestCopyMethod() { if ((m_src.scheme() == m_dest.scheme()) && (m_src.host() == m_dest.host()) && (m_src.port() == m_dest.port()) && (m_src.userName() == m_dest.userName()) && (m_src.password() == m_dest.password())) { startCopyJob(); } else if (m_src.isLocalFile() && KProtocolManager::canCopyFromFile(m_dest)) { startCopyJob(m_dest); } else if (m_dest.isLocalFile() && KProtocolManager::canCopyToFile(m_src) && !KIO::Scheduler::isSlaveOnHoldFor(m_src)) { startCopyJob(m_src); } else { startDataPump(); } } FileCopyJob::~FileCopyJob() { } void FileCopyJob::setSourceSize(KIO::filesize_t size) { Q_D(FileCopyJob); d->m_sourceSize = size; if (size != (KIO::filesize_t) - 1) { setTotalAmount(KJob::Bytes, size); } } void FileCopyJob::setModificationTime(const QDateTime &mtime) { Q_D(FileCopyJob); d->m_modificationTime = mtime; } QUrl FileCopyJob::srcUrl() const { return d_func()->m_src; } QUrl FileCopyJob::destUrl() const { return d_func()->m_dest; } void FileCopyJobPrivate::startCopyJob() { startCopyJob(m_src); } void FileCopyJobPrivate::startCopyJob(const QUrl &slave_url) { Q_Q(FileCopyJob); //qDebug(); KIO_ARGS << m_src << m_dest << m_permissions << (qint8)(m_flags & Overwrite); - m_copyJob = new DirectCopyJob(slave_url, packedArgs); + auto job = new DirectCopyJob(slave_url, packedArgs); + m_copyJob = job; m_copyJob->setParentJob(q); if (m_modificationTime.isValid()) { m_copyJob->addMetaData(QStringLiteral("modified"), m_modificationTime.toString(Qt::ISODate)); // #55804 } q->addSubjob(m_copyJob); connectSubjob(m_copyJob); - q->connect(m_copyJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)), - SLOT(slotCanResume(KIO::Job*,KIO::filesize_t))); + q->connect(job, &DirectCopyJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) { + slotCanResume(job, offset); + }); } void FileCopyJobPrivate::startRenameJob(const QUrl &slave_url) { Q_Q(FileCopyJob); m_mustChmod = true; // CMD_RENAME by itself doesn't change permissions KIO_ARGS << m_src << m_dest << (qint8)(m_flags & Overwrite); m_moveJob = SimpleJobPrivate::newJobNoUi(slave_url, CMD_RENAME, packedArgs); m_moveJob->setParentJob(q); if (m_modificationTime.isValid()) { m_moveJob->addMetaData(QStringLiteral("modified"), m_modificationTime.toString(Qt::ISODate)); // #55804 } q->addSubjob(m_moveJob); connectSubjob(m_moveJob); } void FileCopyJobPrivate::connectSubjob(SimpleJob *job) { Q_Q(FileCopyJob); - q->connect(job, SIGNAL(totalSize(KJob*,qulonglong)), - SLOT(slotTotalSize(KJob*,qulonglong))); + q->connect(job, &KJob::totalSize, q, [q](KJob *job, qulonglong totalSize) { + Q_UNUSED(job); + if (totalSize != q->totalAmount(KJob::Bytes)) { + q->setTotalAmount(KJob::Bytes, totalSize); + } + }); - q->connect(job, SIGNAL(processedSize(KJob*,qulonglong)), - SLOT(slotProcessedSize(KJob*,qulonglong))); + q->connect(job, &KJob::processedSize, q, [q](KJob *job, qulonglong processedSize) { + Q_UNUSED(job); + q->setProcessedAmount(KJob::Bytes, processedSize); + }); + + q->connect(job, QOverload::of(&KJob::percent), q, [q](KJob *job, ulong percent) { + Q_UNUSED(job); + if (percent > q->percent()) { + q->setPercent(percent); + } + }); - q->connect(job, SIGNAL(percent(KJob*,ulong)), - SLOT(slotPercent(KJob*,ulong))); if (q->isSuspended()) { job->suspend(); } } bool FileCopyJob::doSuspend() { Q_D(FileCopyJob); if (d->m_moveJob) { d->m_moveJob->suspend(); } if (d->m_copyJob) { d->m_copyJob->suspend(); } if (d->m_getJob) { d->m_getJob->suspend(); } if (d->m_putJob) { d->m_putJob->suspend(); } Job::doSuspend(); return true; } bool FileCopyJob::doResume() { Q_D(FileCopyJob); if (d->m_moveJob) { d->m_moveJob->resume(); } if (d->m_copyJob) { d->m_copyJob->resume(); } if (d->m_getJob) { d->m_getJob->resume(); } if (d->m_putJob) { d->m_putJob->resume(); } Job::doResume(); return true; } -void FileCopyJobPrivate::slotProcessedSize(KJob *, qulonglong size) -{ - Q_Q(FileCopyJob); - q->setProcessedAmount(KJob::Bytes, size); -} - -void FileCopyJobPrivate::slotTotalSize(KJob *, qulonglong size) -{ - Q_Q(FileCopyJob); - if (size != q->totalAmount(KJob::Bytes)) { - q->setTotalAmount(KJob::Bytes, size); - } -} - -void FileCopyJobPrivate::slotPercent(KJob *, unsigned long pct) -{ - Q_Q(FileCopyJob); - if (pct > q->percent()) { - q->setPercent(pct); - } -} - void FileCopyJobPrivate::startDataPump() { Q_Q(FileCopyJob); //qDebug(); m_canResume = false; m_resumeAnswerSent = false; m_getJob = nullptr; // for now m_putJob = put(m_dest, m_permissions, (m_flags | HideProgressInfo) /* no GUI */); m_putJob->setParentJob(q); //qDebug() << "m_putJob=" << m_putJob << "m_dest=" << m_dest; if (m_modificationTime.isValid()) { m_putJob->setModificationTime(m_modificationTime); } // The first thing the put job will tell us is whether we can // resume or not (this is always emitted) - q->connect(m_putJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)), - SLOT(slotCanResume(KIO::Job*,KIO::filesize_t))); - q->connect(m_putJob, SIGNAL(dataReq(KIO::Job*,QByteArray&)), - SLOT(slotDataReq(KIO::Job*,QByteArray&))); + q->connect(m_putJob, &KIO::TransferJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) { + slotCanResume(job, offset); + }); + q->connect(m_putJob, &KIO::TransferJob::dataReq, q, [this](KIO::Job *job, QByteArray &data) { + slotDataReq(job, data); + }); q->addSubjob(m_putJob); } void FileCopyJobPrivate::slotCanResume(KIO::Job *job, KIO::filesize_t offset) { Q_Q(FileCopyJob); if (job == m_putJob || job == m_copyJob) { //qDebug() << "'can resume' from PUT job. offset=" << KIO::number(offset); if (offset) { RenameDialog_Result res = R_RESUME; if (!KProtocolManager::autoResume() && !(m_flags & Overwrite) && m_uiDelegateExtension) { QString newPath; KIO::Job *job = (q->parentJob()) ? q->parentJob() : q; // Ask confirmation about resuming previous transfer res = m_uiDelegateExtension->askFileRename( job, i18n("File Already Exists"), m_src, m_dest, RenameDialog_Options(RenameDialog_Overwrite | RenameDialog_Resume | RenameDialog_NoRename), newPath, m_sourceSize, offset); } if (res == R_OVERWRITE || (m_flags & Overwrite)) { offset = 0; } else if (res == R_CANCEL) { if (job == m_putJob) { m_putJob->kill(FileCopyJob::Quietly); q->removeSubjob(m_putJob); m_putJob = nullptr; } else { m_copyJob->kill(FileCopyJob::Quietly); q->removeSubjob(m_copyJob); m_copyJob = nullptr; } q->setError(ERR_USER_CANCELED); q->emitResult(); return; } } else { m_resumeAnswerSent = true; // No need for an answer } if (job == m_putJob) { m_getJob = KIO::get(m_src, NoReload, HideProgressInfo /* no GUI */); m_getJob->setParentJob(q); //qDebug() << "m_getJob=" << m_getJob << m_src; m_getJob->addMetaData(QStringLiteral("errorPage"), QStringLiteral("false")); m_getJob->addMetaData(QStringLiteral("AllowCompressedPage"), QStringLiteral("false")); // Set size in subjob. This helps if the slave doesn't emit totalSize. if (m_sourceSize != (KIO::filesize_t) - 1) { m_getJob->setTotalAmount(KJob::Bytes, m_sourceSize); } if (offset) { //qDebug() << "Setting metadata for resume to" << (unsigned long) offset; m_getJob->addMetaData(QStringLiteral("range-start"), KIO::number(offset)); // Might or might not get emitted - q->connect(m_getJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)), - SLOT(slotCanResume(KIO::Job*,KIO::filesize_t))); + q->connect(m_getJob, &KIO::TransferJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) { + slotCanResume(job, offset); + }); } jobSlave(m_putJob)->setOffset(offset); m_putJob->d_func()->internalSuspend(); q->addSubjob(m_getJob); connectSubjob(m_getJob); // Progress info depends on get m_getJob->d_func()->internalResume(); // Order a beer - q->connect(m_getJob, SIGNAL(data(KIO::Job*,QByteArray)), - SLOT(slotData(KIO::Job*,QByteArray))); - q->connect(m_getJob, SIGNAL(mimetype(KIO::Job*,QString)), - SLOT(slotMimetype(KIO::Job*,QString))); + q->connect(m_getJob, &KIO::TransferJob::data, q, [this](KIO::Job *job, const QByteArray &data) { + slotData(job, data); + }); + q->connect(m_getJob, QOverload::of(&KIO::TransferJob::mimetype), q, [this](KIO::Job *job, const QString &type) { + slotMimetype(job, type); + }); } else { // copyjob jobSlave(m_copyJob)->sendResumeAnswer(offset != 0); } } else if (job == m_getJob) { // Cool, the get job said ok, we can resume m_canResume = true; //qDebug() << "'can resume' from the GET job -> we can resume"; jobSlave(m_getJob)->setOffset(jobSlave(m_putJob)->offset()); } else { qCWarning(KIO_CORE) << "unknown job=" << job << "m_getJob=" << m_getJob << "m_putJob=" << m_putJob; } } void FileCopyJobPrivate::slotData(KIO::Job *, const QByteArray &data) { //qDebug() << "data size:" << data.size(); Q_ASSERT(m_putJob); if (!m_putJob) { return; // Don't crash } m_getJob->d_func()->internalSuspend(); m_putJob->d_func()->internalResume(); // Drink the beer m_buffer += data; // On the first set of data incoming, we tell the "put" slave about our // decision about resuming if (!m_resumeAnswerSent) { m_resumeAnswerSent = true; //qDebug() << "(first time) -> send resume answer " << m_canResume; jobSlave(m_putJob)->sendResumeAnswer(m_canResume); } } void FileCopyJobPrivate::slotDataReq(KIO::Job *, QByteArray &data) { Q_Q(FileCopyJob); //qDebug(); if (!m_resumeAnswerSent && !m_getJob) { // This can't happen q->setError(ERR_INTERNAL); q->setErrorText(QStringLiteral("'Put' job did not send canResume or 'Get' job did not send data!")); m_putJob->kill(FileCopyJob::Quietly); q->removeSubjob(m_putJob); m_putJob = nullptr; q->emitResult(); return; } if (m_getJob) { m_getJob->d_func()->internalResume(); // Order more beer m_putJob->d_func()->internalSuspend(); } data = m_buffer; m_buffer = QByteArray(); } void FileCopyJobPrivate::slotMimetype(KIO::Job *, const QString &type) { Q_Q(FileCopyJob); emit q->mimetype(q, type); } void FileCopyJob::slotResult(KJob *job) { Q_D(FileCopyJob); //qDebug() << "this=" << this << "job=" << job; removeSubjob(job); // Did job have an error ? if (job->error()) { if ((job == d->m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) { d->m_moveJob = nullptr; d->startBestCopyMethod(); return; } else if ((job == d->m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) { d->m_copyJob = nullptr; d->startDataPump(); return; } else if (job == d->m_getJob) { d->m_getJob = nullptr; if (d->m_putJob) { d->m_putJob->kill(Quietly); removeSubjob(d->m_putJob); } } else if (job == d->m_putJob) { d->m_putJob = nullptr; if (d->m_getJob) { d->m_getJob->kill(Quietly); removeSubjob(d->m_getJob); } } setError(job->error()); setErrorText(job->errorText()); emitResult(); return; } if (d->m_mustChmod) { // If d->m_permissions == -1, keep the default permissions if (d->m_permissions != -1) { d->m_chmodJob = chmod(d->m_dest, d->m_permissions); addSubjob(d->m_chmodJob); } d->m_mustChmod = false; } if (job == d->m_moveJob) { d->m_moveJob = nullptr; // Finished } if (job == d->m_copyJob) { d->m_copyJob = nullptr; if (d->m_move) { d->m_delJob = file_delete(d->m_src, HideProgressInfo/*no GUI*/); // Delete source addSubjob(d->m_delJob); } } if (job == d->m_getJob) { //qDebug() << "m_getJob finished"; d->m_getJob = nullptr; // No action required if (d->m_putJob) { d->m_putJob->d_func()->internalResume(); } } if (job == d->m_putJob) { //qDebug() << "m_putJob finished"; d->m_putJob = nullptr; if (d->m_getJob) { // The get job is still running, probably after emitting data(QByteArray()) // and before we receive its finished(). d->m_getJob->d_func()->internalResume(); } if (d->m_move) { d->m_delJob = file_delete(d->m_src, HideProgressInfo/*no GUI*/); // Delete source addSubjob(d->m_delJob); } } if (job == d->m_delJob) { d->m_delJob = nullptr; // Finished } if (job == d->m_chmodJob) { d->m_chmodJob = nullptr; // Finished } if (!hasSubjobs()) { emitResult(); } } FileCopyJob *KIO::file_copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) { return FileCopyJobPrivate::newJob(src, dest, permissions, false, flags); } FileCopyJob *KIO::file_move(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) { FileCopyJob *job = FileCopyJobPrivate::newJob(src, dest, permissions, true, flags); if (job->uiDelegateExtension()) { job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::UpdateContent); } return job; } #include "moc_filecopyjob.cpp" diff --git a/src/core/filecopyjob.h b/src/core/filecopyjob.h index e5ccd8f4..121c8f7f 100644 --- a/src/core/filecopyjob.h +++ b/src/core/filecopyjob.h @@ -1,157 +1,148 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2009 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIO_FILECOPYJOB_H #define KIO_FILECOPYJOB_H #include "job_base.h" #include // filesize_t namespace KIO { class FileCopyJobPrivate; /** * @class KIO::FileCopyJob filecopyjob.h * * The FileCopyJob copies data from one place to another. * @see KIO::file_copy() * @see KIO::file_move() */ class KIOCORE_EXPORT FileCopyJob : public Job { Q_OBJECT public: ~FileCopyJob() override; /** * If you know the size of the source file, call this method * to inform this job. It will be displayed in the "resume" dialog. * @param size the size of the source file */ void setSourceSize(KIO::filesize_t size); /** * Sets the modification time of the file * * Note that this is ignored if a direct copy (SlaveBase::copy) can be done, * in which case the mtime of the source is applied to the destination (if the protocol * supports the concept). */ void setModificationTime(const QDateTime &mtime); /** * Returns the source URL. * @return the source URL */ QUrl srcUrl() const; /** * Returns the destination URL. * @return the destination URL */ QUrl destUrl() const; bool doSuspend() override; bool doResume() override; Q_SIGNALS: /** * Mimetype determined during a file copy. * This is never emitted during a move, and might not be emitted during * a file copy, depending on the slave. But when a get and a put are * being used (which is the common case), this signal forwards the * mimetype information from the get job. * * @param job the job that emitted this signal * @param type the mime type */ void mimetype(KIO::Job *job, const QString &type); protected Q_SLOTS: /** * Called whenever a subjob finishes. * @param job the job that emitted this signal */ void slotResult(KJob *job) override; protected: FileCopyJob(FileCopyJobPrivate &dd); private: - Q_PRIVATE_SLOT(d_func(), void slotStart()) - Q_PRIVATE_SLOT(d_func(), void slotData(KIO::Job *, const QByteArray &data)) - Q_PRIVATE_SLOT(d_func(), void slotDataReq(KIO::Job *, QByteArray &data)) - Q_PRIVATE_SLOT(d_func(), void slotMimetype(KIO::Job *, const QString &type)) - Q_PRIVATE_SLOT(d_func(), void slotProcessedSize(KJob *job, qulonglong size)) - Q_PRIVATE_SLOT(d_func(), void slotTotalSize(KJob *job, qulonglong size)) - Q_PRIVATE_SLOT(d_func(), void slotPercent(KJob *job, unsigned long pct)) - Q_PRIVATE_SLOT(d_func(), void slotCanResume(KIO::Job *job, KIO::filesize_t offset)) - Q_DECLARE_PRIVATE(FileCopyJob) }; /** * Copy a single file. * * Uses either SlaveBase::copy() if the slave supports that * or get() and put() otherwise. * @param src Where to get the file. * @param dest Where to put the file. * @param permissions May be -1. In this case no special permission mode is set. * @param flags Can be HideProgressInfo, Overwrite and Resume here. WARNING: * Setting Resume means that the data will be appended to @p dest if @p dest exists. * @return the job handling the operation. */ KIOCORE_EXPORT FileCopyJob *file_copy(const QUrl &src, const QUrl &dest, int permissions = -1, JobFlags flags = DefaultFlags); /** * Overload for catching code mistakes. Do NOT call this method (it is not implemented), * insert a value for permissions (-1 by default) before the JobFlags. * @since 4.5 */ FileCopyJob *file_copy(const QUrl &src, const QUrl &dest, JobFlags flags) Q_DECL_EQ_DELETE; // not implemented - on purpose. /** * Move a single file. * * Use either SlaveBase::rename() if the slave supports that, * or copy() and del() otherwise, or eventually get() & put() & del() * @param src Where to get the file. * @param dest Where to put the file. * @param permissions May be -1. In this case no special permission mode is set. * @param flags Can be HideProgressInfo, Overwrite and Resume here. WARNING: * Setting Resume means that the data will be appended to @p dest if @p dest exists. * @return the job handling the operation. */ KIOCORE_EXPORT FileCopyJob *file_move(const QUrl &src, const QUrl &dest, int permissions = -1, JobFlags flags = DefaultFlags); /** * Overload for catching code mistakes. Do NOT call this method (it is not implemented), * insert a value for permissions (-1 by default) before the JobFlags. * @since 4.3 */ FileCopyJob *file_move(const QUrl &src, const QUrl &dest, JobFlags flags) Q_DECL_EQ_DELETE; // not implemented - on purpose. } #endif diff --git a/src/core/job.cpp b/src/core/job.cpp index 5195d05c..46ed0c5b 100644 --- a/src/core/job.cpp +++ b/src/core/job.cpp @@ -1,394 +1,389 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2009 David Faure Waldo Bastian This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "job.h" #include "job_p.h" #include #include #include #include #include #include #include "slave.h" #include "scheduler.h" using namespace KIO; Job::Job() : KCompositeJob(nullptr) , d_ptr(new JobPrivate) { d_ptr->q_ptr = this; setCapabilities(KJob::Killable | KJob::Suspendable); } Job::Job(JobPrivate &dd) : KCompositeJob(nullptr) , d_ptr(&dd) { d_ptr->q_ptr = this; setCapabilities(KJob::Killable | KJob::Suspendable); } Job::~Job() { delete d_ptr; } // Exists for historical reasons only KJobUiDelegate *Job::ui() const { return uiDelegate(); } JobUiDelegateExtension *Job::uiDelegateExtension() const { Q_D(const Job); return d->m_uiDelegateExtension; } void Job::setUiDelegateExtension(JobUiDelegateExtension *extension) { Q_D(Job); d->m_uiDelegateExtension = extension; } bool Job::addSubjob(KJob *jobBase) { //qDebug() << "addSubjob(" << jobBase << ") this=" << this; bool ok = KCompositeJob::addSubjob(jobBase); KIO::Job *job = dynamic_cast(jobBase); if (ok && job) { // Copy metadata into subjob (e.g. window-id, user-timestamp etc.) Q_D(Job); job->mergeMetaData(d->m_outgoingMetaData); // Forward information from that subjob. - connect(job, SIGNAL(speed(KJob*,ulong)), - SLOT(slotSpeed(KJob*,ulong))); - + connect(job, &KJob::speed, this, [this](KJob *job, ulong speed) { + Q_UNUSED(job); + emitSpeed(speed); + }); job->setProperty("window", property("window")); // see KJobWidgets job->setProperty("userTimestamp", property("userTimestamp")); // see KJobWidgets job->setUiDelegateExtension(d->m_uiDelegateExtension); } return ok; } bool Job::removeSubjob(KJob *jobBase) { //qDebug() << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count(); return KCompositeJob::removeSubjob(jobBase); } static QString url_description_string(const QUrl& url) { return url.scheme() == QLatin1String("data") ? QStringLiteral("data:[...]") : KStringHandler::csqueeze(url.toDisplayString(QUrl::PreferLocalFile), 100); } KIO::JobPrivate::~JobPrivate() { } void JobPrivate::emitMoving(KIO::Job *job, const QUrl &src, const QUrl &dest) { emit job->description(job, i18nc("@title job", "Moving"), qMakePair(i18nc("The source of a file operation", "Source"), url_description_string(src)), qMakePair(i18nc("The destination of a file operation", "Destination"), url_description_string(dest))); } void JobPrivate::emitCopying(KIO::Job *job, const QUrl &src, const QUrl &dest) { emit job->description(job, i18nc("@title job", "Copying"), qMakePair(i18nc("The source of a file operation", "Source"), url_description_string(src)), qMakePair(i18nc("The destination of a file operation", "Destination"), url_description_string(dest))); } void JobPrivate::emitCreatingDir(KIO::Job *job, const QUrl &dir) { emit job->description(job, i18nc("@title job", "Creating directory"), qMakePair(i18n("Directory"), url_description_string(dir))); } void JobPrivate::emitDeleting(KIO::Job *job, const QUrl &url) { emit job->description(job, i18nc("@title job", "Deleting"), qMakePair(i18n("File"), url_description_string(url))); } void JobPrivate::emitStating(KIO::Job *job, const QUrl &url) { emit job->description(job, i18nc("@title job", "Examining"), qMakePair(i18n("File"), url_description_string(url))); } void JobPrivate::emitTransferring(KIO::Job *job, const QUrl &url) { emit job->description(job, i18nc("@title job", "Transferring"), qMakePair(i18nc("The source of a file operation", "Source"), url_description_string(url))); } void JobPrivate::emitMounting(KIO::Job *job, const QString &dev, const QString &point) { emit job->description(job, i18nc("@title job", "Mounting"), qMakePair(i18n("Device"), dev), qMakePair(i18n("Mountpoint"), point)); } void JobPrivate::emitUnmounting(KIO::Job *job, const QString &point) { emit job->description(job, i18nc("@title job", "Unmounting"), qMakePair(i18n("Mountpoint"), point)); } bool Job::doKill() { // kill all subjobs, without triggering their result slot Q_FOREACH (KJob *it, subjobs()) { it->kill(KJob::Quietly); } clearSubjobs(); return true; } bool Job::doSuspend() { Q_FOREACH (KJob *it, subjobs()) { if (!it->suspend()) { return false; } } return true; } bool Job::doResume() { Q_FOREACH (KJob *it, subjobs()) { if (!it->resume()) { return false; } } return true; } -void JobPrivate::slotSpeed(KJob *, unsigned long speed) -{ - //qDebug() << speed; - q_func()->emitSpeed(speed); -} - //Job::errorString is implemented in job_error.cpp void Job::setParentJob(Job *job) { Q_D(Job); Q_ASSERT(d->m_parentJob == nullptr); Q_ASSERT(job); d->m_parentJob = job; } Job *Job::parentJob() const { return d_func()->m_parentJob; } MetaData Job::metaData() const { return d_func()->m_incomingMetaData; } QString Job::queryMetaData(const QString &key) { return d_func()->m_incomingMetaData.value(key, QString()); } void Job::setMetaData(const KIO::MetaData &_metaData) { Q_D(Job); d->m_outgoingMetaData = _metaData; } void Job::addMetaData(const QString &key, const QString &value) { d_func()->m_outgoingMetaData.insert(key, value); } void Job::addMetaData(const QMap &values) { Q_D(Job); QMap::const_iterator it = values.begin(); for (; it != values.end(); ++it) { d->m_outgoingMetaData.insert(it.key(), it.value()); } } void Job::mergeMetaData(const QMap &values) { Q_D(Job); QMap::const_iterator it = values.begin(); for (; it != values.end(); ++it) // there's probably a faster way if (!d->m_outgoingMetaData.contains(it.key())) { d->m_outgoingMetaData.insert(it.key(), it.value()); } } MetaData Job::outgoingMetaData() const { return d_func()->m_outgoingMetaData; } QByteArray JobPrivate::privilegeOperationData() { PrivilegeOperationStatus status = OperationNotAllowed; if (m_parentJob) { QByteArray jobData = m_parentJob->d_func()->privilegeOperationData(); // Copy meta-data from parent job m_incomingMetaData.insert(QStringLiteral("TestData"), m_parentJob->queryMetaData(QStringLiteral("TestData"))); return jobData; } else { if (m_privilegeExecutionEnabled) { status = OperationAllowed; switch (m_operationType) { case ChangeAttr: m_caption = i18n("Change Attribute"); m_message = i18n("Root privileges are required to change file attributes. " "Do you want to continue?"); break; case Copy: m_caption = i18n("Copy Files"); m_message = i18n("Root privileges are required to complete the copy operation. " "Do you want to continue?"); break; case Delete: m_caption = i18n("Delete Files"); m_message = i18n("Root privileges are required to complete the delete operation. " "However, doing so may damage your system. Do you want to continue?"); break; case MkDir: m_caption = i18n("Create Folder"); m_message = i18n("Root privileges are required to create this folder. " "Do you want to continue?"); break; case Move: m_caption = i18n("Move Items"); m_message = i18n("Root privileges are required to complete the move operation. " "Do you want to continue?"); break; case Rename: m_caption = i18n("Rename"); m_message = i18n("Root privileges are required to complete renaming. " "Do you want to continue?"); break; case Symlink: m_caption = i18n("Create Symlink"); m_message = i18n("Root privileges are required to create a symlink. " "Do you want to continue?"); break; case Transfer: m_caption = i18n("Transfer data"); m_message = i18n("Root privileges are required to complete transferring data. " "Do you want to continue?"); Q_FALLTHROUGH(); default: break; } if (m_outgoingMetaData.value(QStringLiteral("UnitTesting")) == QLatin1String("true")) { // Set meta-data for the top-level job m_incomingMetaData.insert(QStringLiteral("TestData"), QStringLiteral("PrivilegeOperationAllowed")); } } } QByteArray parentJobData; QDataStream ds(&parentJobData, QIODevice::WriteOnly); ds << status << m_caption << m_message; return parentJobData; } ////////////////////////// class KIO::DirectCopyJobPrivate: public KIO::SimpleJobPrivate { public: DirectCopyJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs) : SimpleJobPrivate(url, command, packedArgs) {} /** * @internal * Called by the scheduler when a @p slave gets to * work on this job. * @param slave the slave that starts working on this job */ void start(Slave *slave) override; Q_DECLARE_PUBLIC(DirectCopyJob) }; DirectCopyJob::DirectCopyJob(const QUrl &url, const QByteArray &packedArgs) : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs)) { setUiDelegate(KIO::createDefaultJobUiDelegate()); } DirectCopyJob::~DirectCopyJob() { } void DirectCopyJobPrivate::start(Slave *slave) { Q_Q(DirectCopyJob); q->connect(slave, &SlaveInterface::canResume, q, &DirectCopyJob::slotCanResume); SimpleJobPrivate::start(slave); } void DirectCopyJob::slotCanResume(KIO::filesize_t offset) { emit canResume(this, offset); } ////////////////////////// SimpleJob *KIO::file_delete(const QUrl &src, JobFlags flags) { KIO_ARGS << src << qint8(true); // isFile SimpleJob *job = SimpleJobPrivate::newJob(src, CMD_DEL, packedArgs, flags); if (job->uiDelegateExtension()) { job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::RemoveContent); } return job; } ////////// //// #include "moc_job_base.cpp" #include "moc_job_p.cpp" diff --git a/src/core/job_base.h b/src/core/job_base.h index c776b734..1b8bacf8 100644 --- a/src/core/job_base.h +++ b/src/core/job_base.h @@ -1,322 +1,315 @@ /* This file is part of the KDE libraries Copyright (C) 2000 Stephan Kulow 2000-2009 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIO_JOB_BASE_H #define KIO_JOB_BASE_H #include #include namespace KIO { class JobUiDelegateExtension; class JobPrivate; /** * @class KIO::Job job_base.h * * The base class for all jobs. * For all jobs created in an application, the code looks like * * \code * KIO::Job * job = KIO::someoperation( some parameters ); * connect( job, SIGNAL( result( KJob * ) ), * this, SLOT( slotResult( KJob * ) ) ); * \endcode * (other connects, specific to the job) * * And slotResult is usually at least: * * \code * if ( job->error() ) * job->uiDelegate()->showErrorMessage(); * \endcode * @see KIO::Scheduler */ class KIOCORE_EXPORT Job : public KCompositeJob { Q_OBJECT protected: Job(); Job(JobPrivate &dd); public: virtual ~Job(); void start() override {} // Since KIO autostarts its jobs /** * Retrieves the UI delegate of this job. * * @deprecated since 5.0, can now be replaced with uiDelegate() * * @return the delegate used by the job to communicate with the UI */ #ifndef KIOCORE_NO_DEPRECATED KIOCORE_DEPRECATED KJobUiDelegate *ui() const; #endif /** * Retrieves the UI delegate extension used by this job. * @since 5.0 */ JobUiDelegateExtension *uiDelegateExtension() const; /** * Sets the UI delegate extension to be used by this job. * The default UI delegate extension is KIO::defaultJobUiDelegateExtension() */ void setUiDelegateExtension(JobUiDelegateExtension *extension); protected: /** * Abort this job. * This kills all subjobs and deletes the job. * */ bool doKill() override; /** * Suspend this job * @see resume */ bool doSuspend() override; /** * Resume this job * @see suspend */ bool doResume() override; public: /** * Converts an error code and a non-i18n error message into an * error message in the current language. The low level (non-i18n) * error message (usually a url) is put into the translated error * message using %1. * * Example for errid == ERR_CANNOT_OPEN_FOR_READING: * \code * i18n( "Could not read\n%1" ).arg( errortext ); * \endcode * Use this to display the error yourself, but for a dialog box * use uiDelegate()->showErrorMessage(). Do not call it if error() * is not 0. * @return the error message and if there is no error, a message * telling the user that the app is broken, so check with * error() whether there is an error */ QString errorString() const override; /** * Converts an error code and a non-i18n error message into i18n * strings suitable for presentation in a detailed error message box. * * @param reqUrl the request URL that generated this error message * @param method the method that generated this error message * (unimplemented) * @return the following strings: caption, error + description, * causes+solutions */ QStringList detailedErrorStrings(const QUrl *reqUrl = nullptr, int method = -1) const; /** * Set the parent Job. * One example use of this is when FileCopyJob calls RenameDialog::open, * it must pass the correct progress ID of the parent CopyJob * (to hide the progress dialog). * You can set the parent job only once. By default a job does not * have a parent job. * @param parentJob the new parent job */ void setParentJob(Job *parentJob); /** * Returns the parent job, if there is one. * @return the parent job, or @c nullptr if there is none * @see setParentJob */ Job *parentJob() const; /** * Set meta data to be sent to the slave, replacing existing * meta data. * @param metaData the meta data to set * @see addMetaData() * @see mergeMetaData() */ void setMetaData(const KIO::MetaData &metaData); /** * Add key/value pair to the meta data that is sent to the slave. * @param key the key of the meta data * @param value the value of the meta data * @see setMetaData() * @see mergeMetaData() */ void addMetaData(const QString &key, const QString &value); /** * Add key/value pairs to the meta data that is sent to the slave. * If a certain key already existed, it will be overridden. * @param values the meta data to add * @see setMetaData() * @see mergeMetaData() */ void addMetaData(const QMap &values); /** * Add key/value pairs to the meta data that is sent to the slave. * If a certain key already existed, it will remain unchanged. * @param values the meta data to merge * @see setMetaData() * @see addMetaData() */ void mergeMetaData(const QMap &values); /** * @internal. For the scheduler. Do not use. */ MetaData outgoingMetaData() const; /** * Get meta data received from the slave. * (Valid when first data is received and/or slave is finished) * @return the job's meta data */ MetaData metaData() const; /** * Query meta data received from the slave. * (Valid when first data is received and/or slave is finished) * @param key the key of the meta data to retrieve * @return the value of the meta data, or QString() if the * @p key does not exist */ QString queryMetaData(const QString &key); protected: Q_SIGNALS: /** * @deprecated. Don't use ! * Emitted when the job is canceled. * Signal result() is emitted as well, and error() is, * in this case, ERR_USER_CANCELED. * @param job the job that emitted this signal */ #ifndef KIOCORE_NO_DEPRECATED KIOCORE_DEPRECATED void canceled(KJob *job); #endif /** * Emitted when the slave successfully connected to the host. * There is no guarantee the slave will send this, and this is * currently unused (in the applications). * @param job the job that emitted this signal */ void connected(KIO::Job *job); protected: /** * Add a job that has to be finished before a result * is emitted. This has obviously to be called before * the finish signal is emitted by the slave. * * @param job the subjob to add */ bool addSubjob(KJob *job) override; /** * Mark a sub job as being done. * * Note that this does not terminate the parent job, even if @p job * is the last subjob. emitResult must be called to indicate that * the job is complete. * * @param job the subjob to remove */ bool removeSubjob(KJob *job) override; protected: JobPrivate *const d_ptr; private: - /** - * Forward signal from subjob. - * @param job the subjob - * @param speed the speed in bytes/s - * @see speed() - */ - Q_PRIVATE_SLOT(d_func(), void slotSpeed(KJob *job, unsigned long speed)) Q_DECLARE_PRIVATE(Job) }; /** * Flags for the job properties. * Not all flags are supported in all cases. Please see documentation of * the calling function! */ enum JobFlag { /** * Show the progress info GUI, no Resume and no Overwrite */ DefaultFlags = 0, /** * Hide progress information dialog, i.e. don't show a GUI. */ HideProgressInfo = 1, /** * When set, automatically append to the destination file if it exists already. * WARNING: this is NOT the builtin support for offering the user to resume a previous * partial download. The Resume option is much less used, it allows to append * to an existing file. * This is used by KIO::put(), KIO::file_copy(), KIO::file_move(). */ Resume = 2, /** * When set, automatically overwrite the destination if it exists already. * This is used by KIO::rename(), KIO::put(), KIO::file_copy(), KIO::file_move(), KIO::symlink(). * Otherwise the operation will fail with ERR_FILE_ALREADY_EXIST or ERR_DIR_ALREADY_EXIST. */ Overwrite = 4, /** * When set, notifies the slave that application/job does not want privilege execution. * So in case of failure due to insufficient privileges show an error without attempting * to run the operation as root first. * * @since 5.43 */ NoPrivilegeExecution = 8, }; Q_DECLARE_FLAGS(JobFlags, JobFlag) Q_DECLARE_OPERATORS_FOR_FLAGS(JobFlags) enum LoadType { Reload, NoReload }; } #endif