File indexing completed on 2024-04-14 03:53:01

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
0004     SPDX-FileCopyrightText: 2000-2013 David Faure <faure@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "simplejob.h"
0010 #include "job_p.h"
0011 #include "kprotocolinfo.h"
0012 #include "scheduler.h"
0013 #include "worker_p.h"
0014 #include <QDebug>
0015 #include <QTimer>
0016 #include <kdirnotify.h>
0017 
0018 using namespace KIO;
0019 
0020 SimpleJob::SimpleJob(SimpleJobPrivate &dd)
0021     : Job(dd)
0022 {
0023     d_func()->simpleJobInit();
0024 }
0025 
0026 void SimpleJobPrivate::simpleJobInit()
0027 {
0028     Q_Q(SimpleJob);
0029     if (!m_url.isValid() || m_url.scheme().isEmpty()) {
0030         qCWarning(KIO_CORE) << "Invalid URL:" << m_url;
0031         q->setError(ERR_MALFORMED_URL);
0032         q->setErrorText(m_url.toString());
0033         QTimer::singleShot(0, q, &SimpleJob::slotFinished);
0034         return;
0035     }
0036 
0037     Scheduler::doJob(q);
0038 }
0039 
0040 bool SimpleJob::doKill()
0041 {
0042     Q_D(SimpleJob);
0043     if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) {
0044         d->m_extraFlags |= JobPrivate::EF_KillCalled;
0045         Scheduler::cancelJob(this); // deletes the worker if not 0
0046     } else {
0047         qCWarning(KIO_CORE) << this << "killed twice, this is overkill";
0048     }
0049     return Job::doKill();
0050 }
0051 
0052 bool SimpleJob::doSuspend()
0053 {
0054     Q_D(SimpleJob);
0055     if (d->m_worker) {
0056         d->m_worker->suspend();
0057     }
0058     return Job::doSuspend();
0059 }
0060 
0061 bool SimpleJob::doResume()
0062 {
0063     Q_D(SimpleJob);
0064     if (d->m_worker) {
0065         d->m_worker->resume();
0066     }
0067     return Job::doResume();
0068 }
0069 
0070 const QUrl &SimpleJob::url() const
0071 {
0072     return d_func()->m_url;
0073 }
0074 
0075 void SimpleJob::putOnHold()
0076 {
0077     Q_D(SimpleJob);
0078     Q_ASSERT(d->m_worker);
0079     if (d->m_worker) {
0080         Scheduler::putWorkerOnHold(this, d->m_url);
0081     }
0082     // we should now be disassociated from the worker
0083     Q_ASSERT(!d->m_worker);
0084     kill(Quietly);
0085 }
0086 
0087 void SimpleJob::removeOnHold()
0088 {
0089     Scheduler::removeWorkerOnHold();
0090 }
0091 
0092 bool SimpleJob::isRedirectionHandlingEnabled() const
0093 {
0094     return d_func()->m_redirectionHandlingEnabled;
0095 }
0096 
0097 void SimpleJob::setRedirectionHandlingEnabled(bool handle)
0098 {
0099     Q_D(SimpleJob);
0100     d->m_redirectionHandlingEnabled = handle;
0101 }
0102 
0103 SimpleJob::~SimpleJob()
0104 {
0105     Q_D(SimpleJob);
0106     // last chance to remove this job from the scheduler!
0107     if (d->m_schedSerial) {
0108         // qDebug() << "Killing job" << this << "in destructor!"/* << qBacktrace()*/;
0109         Scheduler::cancelJob(this);
0110     }
0111 }
0112 
0113 void SimpleJobPrivate::start(Worker *worker)
0114 {
0115     Q_Q(SimpleJob);
0116     m_worker = worker;
0117 
0118     // Worker::setJob can send us SSL metadata if there is a persistent connection
0119     QObject::connect(worker, &Worker::metaData, q, &SimpleJob::slotMetaData);
0120 
0121     worker->setJob(q);
0122 
0123     QObject::connect(worker, &Worker::error, q, &SimpleJob::slotError);
0124 
0125     QObject::connect(worker, &Worker::warning, q, &SimpleJob::slotWarning);
0126 
0127     QObject::connect(worker, &Worker::finished, q, &SimpleJob::slotFinished);
0128 
0129     QObject::connect(worker, &Worker::infoMessage, q, [this](const QString &message) {
0130         _k_slotWorkerInfoMessage(message);
0131     });
0132 
0133     QObject::connect(worker, &Worker::connected, q, [this]() {
0134         slotConnected();
0135     });
0136 
0137     QObject::connect(worker, &Worker::privilegeOperationRequested, q, [this]() {
0138         slotPrivilegeOperationRequested();
0139     });
0140 
0141     if ((m_extraFlags & EF_TransferJobDataSent) == 0) { // this is a "get" job
0142         QObject::connect(worker, &Worker::totalSize, q, [this](KIO::filesize_t size) {
0143             slotTotalSize(size);
0144         });
0145 
0146         QObject::connect(worker, &Worker::processedSize, q, [this](KIO::filesize_t size) {
0147             slotProcessedSize(size);
0148         });
0149 
0150         QObject::connect(worker, &Worker::speed, q, [this](ulong speed) {
0151             slotSpeed(speed);
0152         });
0153     }
0154 
0155     const QVariant windowIdProp = q->property("window-id"); // see KJobWidgets::setWindow
0156     if (windowIdProp.isValid()) {
0157         m_outgoingMetaData.insert(QStringLiteral("window-id"), QString::number(windowIdProp.toULongLong()));
0158     }
0159 
0160     const QVariant userTimestampProp = q->property("userTimestamp"); // see KJobWidgets::updateUserTimestamp
0161     if (userTimestampProp.isValid()) {
0162         m_outgoingMetaData.insert(QStringLiteral("user-timestamp"), QString::number(userTimestampProp.toULongLong()));
0163     }
0164 
0165     if (q->uiDelegate() == nullptr) { // not interactive
0166         m_outgoingMetaData.insert(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
0167     }
0168 
0169     if (!m_outgoingMetaData.isEmpty()) {
0170         KIO_ARGS << m_outgoingMetaData;
0171         worker->send(CMD_META_DATA, packedArgs);
0172     }
0173 
0174     worker->send(m_command, m_packedArgs);
0175     if (q->isSuspended()) {
0176         worker->suspend();
0177     }
0178 }
0179 
0180 void SimpleJobPrivate::workerDone()
0181 {
0182     Q_Q(SimpleJob);
0183     if (m_worker) {
0184         if (m_command == CMD_OPEN) {
0185             m_worker->send(CMD_CLOSE);
0186         }
0187         q->disconnect(m_worker); // Remove all signals between worker and job
0188     }
0189     // only finish a job once; Scheduler::jobFinished() resets schedSerial to zero.
0190     if (m_schedSerial) {
0191         Scheduler::jobFinished(q, m_worker);
0192     }
0193 }
0194 
0195 void SimpleJob::slotFinished()
0196 {
0197     Q_D(SimpleJob);
0198     // Return worker to the scheduler
0199     d->workerDone();
0200 
0201     if (!hasSubjobs()) {
0202         if (!error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME)) {
0203             if (d->m_command == CMD_MKDIR) {
0204                 const QUrl urlDir = url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
0205 #ifndef KIO_ANDROID_STUB
0206                 org::kde::KDirNotify::emitFilesAdded(urlDir);
0207 #endif
0208             } else { /*if ( m_command == CMD_RENAME )*/
0209                 QUrl src;
0210                 QUrl dst;
0211                 QDataStream str(d->m_packedArgs);
0212                 str >> src >> dst;
0213                 if (src.adjusted(QUrl::RemoveFilename) == dst.adjusted(QUrl::RemoveFilename) // For the user, moving isn't
0214                                                                                              // renaming. Only renaming is.
0215                 ) {
0216 #ifndef KIO_ANDROID_STUB
0217                     org::kde::KDirNotify::emitFileRenamed(src, dst);
0218 #endif
0219                 }
0220 
0221 #ifndef KIO_ANDROID_STUB
0222                 org::kde::KDirNotify::emitFileMoved(src, dst);
0223 #endif
0224                 if (d->m_uiDelegateExtension) {
0225                     d->m_uiDelegateExtension->updateUrlInClipboard(src, dst);
0226                 }
0227             }
0228         }
0229         emitResult();
0230     }
0231 }
0232 
0233 void SimpleJob::slotError(int err, const QString &errorText)
0234 {
0235     Q_D(SimpleJob);
0236     setError(err);
0237     setErrorText(errorText);
0238     if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty()) {
0239         setErrorText(QString());
0240     }
0241     // error terminates the job
0242     slotFinished();
0243 }
0244 
0245 void SimpleJob::slotWarning(const QString &errorText)
0246 {
0247     Q_EMIT warning(this, errorText);
0248 }
0249 
0250 void SimpleJobPrivate::_k_slotWorkerInfoMessage(const QString &msg)
0251 {
0252     Q_EMIT q_func()->infoMessage(q_func(), msg);
0253 }
0254 
0255 void SimpleJobPrivate::slotConnected()
0256 {
0257     Q_EMIT q_func()->connected(q_func());
0258 }
0259 
0260 void SimpleJobPrivate::slotTotalSize(KIO::filesize_t size)
0261 {
0262     Q_Q(SimpleJob);
0263     if (size != q->totalAmount(KJob::Bytes)) {
0264         q->setTotalAmount(KJob::Bytes, size);
0265     }
0266 }
0267 
0268 void SimpleJobPrivate::slotProcessedSize(KIO::filesize_t size)
0269 {
0270     Q_Q(SimpleJob);
0271     // qDebug() << KIO::number(size);
0272     q->setProcessedAmount(KJob::Bytes, size);
0273 }
0274 
0275 void SimpleJobPrivate::slotSpeed(unsigned long speed)
0276 {
0277     // qDebug() << speed;
0278     q_func()->emitSpeed(speed);
0279 }
0280 
0281 void SimpleJobPrivate::restartAfterRedirection(QUrl *redirectionUrl)
0282 {
0283     Q_Q(SimpleJob);
0284     // Return worker to the scheduler while we still have the old URL in place; the scheduler
0285     // requires a job URL to stay invariant while the job is running.
0286     workerDone();
0287 
0288     m_url = *redirectionUrl;
0289     redirectionUrl->clear();
0290     if ((m_extraFlags & EF_KillCalled) == 0) {
0291         Scheduler::doJob(q);
0292     }
0293 }
0294 
0295 void SimpleJob::slotMetaData(const KIO::MetaData &_metaData)
0296 {
0297     Q_D(SimpleJob);
0298     QMapIterator<QString, QString> it(_metaData);
0299     while (it.hasNext()) {
0300         it.next();
0301         if (it.key().startsWith(QLatin1String("{internal~"), Qt::CaseInsensitive)) {
0302             d->m_internalMetaData.insert(it.key(), it.value());
0303         } else {
0304             d->m_incomingMetaData.insert(it.key(), it.value());
0305         }
0306     }
0307 
0308     // Update the internal meta-data values as soon as possible. Waiting until
0309     // the KIO worker is finished has unintended consequences if the client starts
0310     // a new connection without waiting for the KIO worker to finish.
0311     if (!d->m_internalMetaData.isEmpty()) {
0312         Scheduler::updateInternalMetaData(this);
0313     }
0314 }
0315 
0316 void SimpleJobPrivate::slotPrivilegeOperationRequested()
0317 {
0318     m_worker->send(MSG_PRIVILEGE_EXEC, privilegeOperationData());
0319 }
0320 
0321 //////////
0322 SimpleJob *KIO::rmdir(const QUrl &url)
0323 {
0324     // qDebug() << "rmdir " << url;
0325     KIO_ARGS << url << qint8(false); // isFile is false
0326     return SimpleJobPrivate::newJob(url, CMD_DEL, packedArgs);
0327 }
0328 
0329 SimpleJob *KIO::chmod(const QUrl &url, int permissions)
0330 {
0331     // qDebug() << "chmod " << url;
0332     KIO_ARGS << url << permissions;
0333     return SimpleJobPrivate::newJob(url, CMD_CHMOD, packedArgs);
0334 }
0335 
0336 SimpleJob *KIO::chown(const QUrl &url, const QString &owner, const QString &group)
0337 {
0338     KIO_ARGS << url << owner << group;
0339     return SimpleJobPrivate::newJob(url, CMD_CHOWN, packedArgs);
0340 }
0341 
0342 SimpleJob *KIO::setModificationTime(const QUrl &url, const QDateTime &mtime)
0343 {
0344     // qDebug() << "setModificationTime " << url << " " << mtime;
0345     KIO_ARGS << url << mtime;
0346     return SimpleJobPrivate::newJobNoUi(url, CMD_SETMODIFICATIONTIME, packedArgs);
0347 }
0348 
0349 SimpleJob *KIO::rename(const QUrl &src, const QUrl &dest, JobFlags flags)
0350 {
0351     // qDebug() << "rename " << src << " " << dest;
0352     KIO_ARGS << src << dest << (qint8)(flags & Overwrite);
0353     return SimpleJobPrivate::newJob(src, CMD_RENAME, packedArgs, flags);
0354 }
0355 
0356 SimpleJob *KIO::symlink(const QString &target, const QUrl &dest, JobFlags flags)
0357 {
0358     // qDebug() << "symlink target=" << target << " " << dest;
0359     KIO_ARGS << target << dest << (qint8)(flags & Overwrite);
0360     return SimpleJobPrivate::newJob(dest, CMD_SYMLINK, packedArgs, flags);
0361 }
0362 
0363 SimpleJob *KIO::special(const QUrl &url, const QByteArray &data, JobFlags flags)
0364 {
0365     // qDebug() << "special " << url;
0366     return SimpleJobPrivate::newJob(url, CMD_SPECIAL, data, flags);
0367 }
0368 
0369 SimpleJob *KIO::mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags)
0370 {
0371     KIO_ARGS << int(1) << qint8(ro ? 1 : 0) << QString::fromLatin1(fstype) << dev << point;
0372     SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags);
0373     if (!(flags & HideProgressInfo)) {
0374         KIO::JobPrivate::emitMounting(job, dev, point);
0375     }
0376     return job;
0377 }
0378 
0379 SimpleJob *KIO::unmount(const QString &point, JobFlags flags)
0380 {
0381     KIO_ARGS << int(2) << point;
0382     SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags);
0383     if (!(flags & HideProgressInfo)) {
0384         KIO::JobPrivate::emitUnmounting(job, point);
0385     }
0386     return job;
0387 }
0388 
0389 //////////
0390 
0391 SimpleJob *KIO::http_update_cache(const QUrl &url, bool no_cache, const QDateTime &expireDate)
0392 {
0393     Q_ASSERT(url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https"));
0394     // Send http update_cache command (2)
0395     KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate.toMSecsSinceEpoch() / 1000);
0396     return SimpleJobPrivate::newJob(url, CMD_SPECIAL, packedArgs);
0397 }
0398 
0399 #include "moc_simplejob.cpp"