File indexing completed on 2024-04-28 11:40:56

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     SPDX-FileCopyrightText: 2000-2009 Waldo Bastian <bastian@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "job.h"
0011 #include "job_p.h"
0012 
0013 #include <time.h>
0014 
0015 #include <KLocalizedString>
0016 #include <KStringHandler>
0017 
0018 #include "slave.h"
0019 #include <kio/jobuidelegateextension.h>
0020 
0021 using namespace KIO;
0022 
0023 Job::Job()
0024     : KCompositeJob(nullptr)
0025     , d_ptr(new JobPrivate)
0026 {
0027     d_ptr->q_ptr = this;
0028     setCapabilities(KJob::Killable | KJob::Suspendable);
0029 }
0030 
0031 Job::Job(JobPrivate &dd)
0032     : KCompositeJob(nullptr)
0033     , d_ptr(&dd)
0034 {
0035     d_ptr->q_ptr = this;
0036     setCapabilities(KJob::Killable | KJob::Suspendable);
0037 }
0038 
0039 Job::~Job()
0040 {
0041     delete d_ptr;
0042 }
0043 
0044 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 0)
0045 KJobUiDelegate *Job::ui() const
0046 {
0047     return uiDelegate();
0048 }
0049 #endif
0050 
0051 JobUiDelegateExtension *Job::uiDelegateExtension() const
0052 {
0053     Q_D(const Job);
0054     return d->m_uiDelegateExtension;
0055 }
0056 
0057 void Job::setUiDelegateExtension(JobUiDelegateExtension *extension)
0058 {
0059     Q_D(Job);
0060     d->m_uiDelegateExtension = extension;
0061 }
0062 
0063 bool Job::addSubjob(KJob *jobBase)
0064 {
0065     // qDebug() << "addSubjob(" << jobBase << ") this=" << this;
0066 
0067     bool ok = KCompositeJob::addSubjob(jobBase);
0068     KIO::Job *job = qobject_cast<KIO::Job *>(jobBase);
0069     if (ok && job) {
0070         // Copy metadata into subjob (e.g. window-id, user-timestamp etc.)
0071         Q_D(Job);
0072         job->mergeMetaData(d->m_outgoingMetaData);
0073 
0074         // Forward information from that subjob.
0075         connect(job, &KJob::speed, this, [this](KJob *job, ulong speed) {
0076             Q_UNUSED(job);
0077             emitSpeed(speed);
0078         });
0079         job->setProperty("widget", property("widget")); // see KJobWidgets
0080         job->setProperty("window", property("window")); // see KJobWidgets
0081         job->setProperty("userTimestamp", property("userTimestamp")); // see KJobWidgets
0082         job->setUiDelegateExtension(d->m_uiDelegateExtension);
0083     }
0084     return ok;
0085 }
0086 
0087 bool Job::removeSubjob(KJob *jobBase)
0088 {
0089     // qDebug() << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count();
0090     return KCompositeJob::removeSubjob(jobBase);
0091 }
0092 
0093 static QString url_description_string(const QUrl &url)
0094 {
0095     return url.scheme() == QLatin1String("data") ? QStringLiteral("data:[...]") : KStringHandler::csqueeze(url.toDisplayString(QUrl::PreferLocalFile), 100);
0096 }
0097 
0098 KIO::JobPrivate::~JobPrivate()
0099 {
0100 }
0101 
0102 void JobPrivate::emitMoving(KIO::Job *job, const QUrl &src, const QUrl &dest)
0103 {
0104     static const QString s_title = i18nc("@title job", "Moving");
0105     static const QString s_source = i18nc("The source of a file operation", "Source");
0106     static const QString s_destination = i18nc("The destination of a file operation", "Destination");
0107     Q_EMIT job->description(job, s_title, qMakePair(s_source, url_description_string(src)), qMakePair(s_destination, url_description_string(dest)));
0108 }
0109 
0110 void JobPrivate::emitRenaming(KIO::Job *job, const QUrl &src, const QUrl &dest)
0111 {
0112     static const QString s_title = i18nc("@title job", "Renaming");
0113     static const QString s_source = i18nc("The source of a file operation", "Source");
0114     static const QString s_destination = i18nc("The destination of a file operation", "Destination");
0115     Q_EMIT job->description(job, s_title, {s_source, url_description_string(src)}, {s_destination, url_description_string(dest)});
0116 }
0117 
0118 void JobPrivate::emitCopying(KIO::Job *job, const QUrl &src, const QUrl &dest)
0119 {
0120     static const QString s_title = i18nc("@title job", "Copying");
0121     static const QString s_source = i18nc("The source of a file operation", "Source");
0122     static const QString s_destination = i18nc("The destination of a file operation", "Destination");
0123     Q_EMIT job->description(job, s_title, qMakePair(s_source, url_description_string(src)), qMakePair(s_destination, url_description_string(dest)));
0124 }
0125 
0126 void JobPrivate::emitCreatingDir(KIO::Job *job, const QUrl &dir)
0127 {
0128     static const QString s_title = i18nc("@title job", "Creating directory");
0129     static const QString s_directory = i18n("Directory");
0130     Q_EMIT job->description(job, s_title, qMakePair(s_directory, url_description_string(dir)));
0131 }
0132 
0133 void JobPrivate::emitDeleting(KIO::Job *job, const QUrl &url)
0134 {
0135     static const QString s_title = i18nc("@title job", "Deleting");
0136     static const QString s_file = i18n("File");
0137     Q_EMIT job->description(job, s_title, qMakePair(s_file, url_description_string(url)));
0138 }
0139 
0140 void JobPrivate::emitStating(KIO::Job *job, const QUrl &url)
0141 {
0142     static const QString s_title = i18nc("@title job", "Examining");
0143     static const QString s_file = i18n("File");
0144     Q_EMIT job->description(job, s_title, qMakePair(s_file, url_description_string(url)));
0145 }
0146 
0147 void JobPrivate::emitTransferring(KIO::Job *job, const QUrl &url)
0148 {
0149     static const QString s_title = i18nc("@title job", "Transferring");
0150     static const QString s_source = i18nc("The source of a file operation", "Source");
0151     Q_EMIT job->description(job, s_title, qMakePair(s_source, url_description_string(url)));
0152 }
0153 
0154 void JobPrivate::emitMounting(KIO::Job *job, const QString &dev, const QString &point)
0155 {
0156     Q_EMIT job->description(job, i18nc("@title job", "Mounting"), qMakePair(i18n("Device"), dev), qMakePair(i18n("Mountpoint"), point));
0157 }
0158 
0159 void JobPrivate::emitUnmounting(KIO::Job *job, const QString &point)
0160 {
0161     Q_EMIT job->description(job, i18nc("@title job", "Unmounting"), qMakePair(i18n("Mountpoint"), point));
0162 }
0163 
0164 bool Job::doKill()
0165 {
0166     // kill all subjobs, without triggering their result slot
0167     for (KJob *job : subjobs()) {
0168         job->kill(KJob::Quietly);
0169     }
0170     clearSubjobs();
0171 
0172     return true;
0173 }
0174 
0175 bool Job::doSuspend()
0176 {
0177     for (KJob *job : subjobs()) {
0178         if (!job->suspend()) {
0179             return false;
0180         }
0181     }
0182 
0183     return true;
0184 }
0185 
0186 bool Job::doResume()
0187 {
0188     for (KJob *job : subjobs()) {
0189         if (!job->resume()) {
0190             return false;
0191         }
0192     }
0193 
0194     return true;
0195 }
0196 
0197 // Job::errorString is implemented in job_error.cpp
0198 
0199 void Job::setParentJob(Job *job)
0200 {
0201     Q_D(Job);
0202     Q_ASSERT(d->m_parentJob == nullptr);
0203     Q_ASSERT(job);
0204     d->m_parentJob = job;
0205 }
0206 
0207 Job *Job::parentJob() const
0208 {
0209     return d_func()->m_parentJob;
0210 }
0211 
0212 MetaData Job::metaData() const
0213 {
0214     return d_func()->m_incomingMetaData;
0215 }
0216 
0217 QString Job::queryMetaData(const QString &key)
0218 {
0219     return d_func()->m_incomingMetaData.value(key, QString());
0220 }
0221 
0222 void Job::setMetaData(const KIO::MetaData &_metaData)
0223 {
0224     Q_D(Job);
0225     d->m_outgoingMetaData = _metaData;
0226 }
0227 
0228 void Job::addMetaData(const QString &key, const QString &value)
0229 {
0230     d_func()->m_outgoingMetaData.insert(key, value);
0231 }
0232 
0233 void Job::addMetaData(const QMap<QString, QString> &values)
0234 {
0235     Q_D(Job);
0236     for (auto it = values.cbegin(); it != values.cend(); ++it) {
0237         d->m_outgoingMetaData.insert(it.key(), it.value());
0238     }
0239 }
0240 
0241 void Job::mergeMetaData(const QMap<QString, QString> &values)
0242 {
0243     Q_D(Job);
0244     for (auto it = values.cbegin(); it != values.cend(); ++it) {
0245         // there's probably a faster way
0246         if (!d->m_outgoingMetaData.contains(it.key())) {
0247             d->m_outgoingMetaData.insert(it.key(), it.value());
0248         }
0249     }
0250 }
0251 
0252 MetaData Job::outgoingMetaData() const
0253 {
0254     return d_func()->m_outgoingMetaData;
0255 }
0256 
0257 QByteArray JobPrivate::privilegeOperationData()
0258 {
0259     PrivilegeOperationStatus status = OperationNotAllowed;
0260 
0261     if (m_parentJob) {
0262         QByteArray jobData = m_parentJob->d_func()->privilegeOperationData();
0263         // Copy meta-data from parent job
0264         m_incomingMetaData.insert(QStringLiteral("TestData"), m_parentJob->queryMetaData(QStringLiteral("TestData")));
0265         return jobData;
0266     } else {
0267         if (m_privilegeExecutionEnabled) {
0268             status = OperationAllowed;
0269             switch (m_operationType) {
0270             case ChangeAttr:
0271                 m_title = i18n("Change Attribute");
0272                 m_message = i18n(
0273                     "Root privileges are required to change file attributes. "
0274                     "Do you want to continue?");
0275                 break;
0276             case Copy:
0277                 m_title = i18n("Copy Files");
0278                 m_message = i18n(
0279                     "Root privileges are required to complete the copy operation. "
0280                     "Do you want to continue?");
0281                 break;
0282             case Delete:
0283                 m_title = i18n("Delete Files");
0284                 m_message = i18n(
0285                     "Root privileges are required to complete the delete operation. "
0286                     "However, doing so may damage your system. Do you want to continue?");
0287                 break;
0288             case MkDir:
0289                 m_title = i18n("Create Folder");
0290                 m_message = i18n(
0291                     "Root privileges are required to create this folder. "
0292                     "Do you want to continue?");
0293                 break;
0294             case Move:
0295                 m_title = i18n("Move Items");
0296                 m_message = i18n(
0297                     "Root privileges are required to complete the move operation. "
0298                     "Do you want to continue?");
0299                 break;
0300             case Rename:
0301                 m_title = i18n("Rename");
0302                 m_message = i18n(
0303                     "Root privileges are required to complete renaming. "
0304                     "Do you want to continue?");
0305                 break;
0306             case Symlink:
0307                 m_title = i18n("Create Symlink");
0308                 m_message = i18n(
0309                     "Root privileges are required to create a symlink. "
0310                     "Do you want to continue?");
0311                 break;
0312             case Transfer:
0313                 m_title = i18n("Transfer data");
0314                 m_message = i18n(
0315                     "Root privileges are required to complete transferring data. "
0316                     "Do you want to continue?");
0317                 Q_FALLTHROUGH();
0318             default:
0319                 break;
0320             }
0321 
0322             if (m_outgoingMetaData.value(QStringLiteral("UnitTesting")) == QLatin1String("true")) {
0323                 // Set meta-data for the top-level job
0324                 m_incomingMetaData.insert(QStringLiteral("TestData"), QStringLiteral("PrivilegeOperationAllowed"));
0325             }
0326         }
0327     }
0328 
0329     QByteArray parentJobData;
0330     QDataStream ds(&parentJobData, QIODevice::WriteOnly);
0331     ds << status << m_title << m_message;
0332     return parentJobData;
0333 }
0334 
0335 //////////////////////////
0336 
0337 class KIO::DirectCopyJobPrivate : public KIO::SimpleJobPrivate
0338 {
0339 public:
0340     DirectCopyJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs)
0341         : SimpleJobPrivate(url, command, packedArgs)
0342     {
0343     }
0344 
0345     /**
0346      * @internal
0347      * Called by the scheduler when a @p slave gets to
0348      * work on this job.
0349      * @param slave the slave that starts working on this job
0350      */
0351     void start(Slave *slave) override;
0352 
0353     Q_DECLARE_PUBLIC(DirectCopyJob)
0354 };
0355 
0356 DirectCopyJob::DirectCopyJob(const QUrl &url, const QByteArray &packedArgs)
0357     : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs))
0358 {
0359     setUiDelegate(KIO::createDefaultJobUiDelegate());
0360 }
0361 
0362 DirectCopyJob::~DirectCopyJob()
0363 {
0364 }
0365 
0366 void DirectCopyJobPrivate::start(Slave *slave)
0367 {
0368     Q_Q(DirectCopyJob);
0369     q->connect(slave, &SlaveInterface::canResume, q, &DirectCopyJob::slotCanResume);
0370     SimpleJobPrivate::start(slave);
0371 }
0372 
0373 void DirectCopyJob::slotCanResume(KIO::filesize_t offset)
0374 {
0375     Q_EMIT canResume(this, offset);
0376 }
0377 
0378 //////////////////////////
0379 
0380 SimpleJob *KIO::file_delete(const QUrl &src, JobFlags flags)
0381 {
0382     KIO_ARGS << src << qint8(true); // isFile
0383     SimpleJob *job = SimpleJobPrivate::newJob(src, CMD_DEL, packedArgs, flags);
0384     if (job->uiDelegateExtension()) {
0385         job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::RemoveContent);
0386     }
0387     return job;
0388 }
0389 
0390 //////////
0391 ////
0392 
0393 #include "moc_job_base.cpp"
0394 #include "moc_job_p.cpp"