File indexing completed on 2024-04-28 15:26:32

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 "slave.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 slave 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_slave) {
0056         d->m_slave->suspend();
0057     }
0058     return Job::doSuspend();
0059 }
0060 
0061 bool SimpleJob::doResume()
0062 {
0063     Q_D(SimpleJob);
0064     if (d->m_slave) {
0065         d->m_slave->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_slave);
0079     if (d->m_slave) {
0080         Scheduler::putWorkerOnHold(this, d->m_url);
0081     }
0082     // we should now be disassociated from the slave
0083     Q_ASSERT(!d->m_slave);
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(Slave *slave)
0114 {
0115     Q_Q(SimpleJob);
0116     m_slave = slave;
0117 
0118     // Slave::setJob can send us SSL metadata if there is a persistent connection
0119     QObject::connect(slave, &Slave::metaData, q, &SimpleJob::slotMetaData);
0120 
0121     slave->setJob(q);
0122 
0123     QObject::connect(slave, &Slave::error, q, &SimpleJob::slotError);
0124 
0125     QObject::connect(slave, &Slave::warning, q, &SimpleJob::slotWarning);
0126 
0127     QObject::connect(slave, &Slave::finished, q, &SimpleJob::slotFinished);
0128 
0129     QObject::connect(slave, &Slave::infoMessage, q, [this](const QString &message) {
0130         _k_slotSlaveInfoMessage(message);
0131     });
0132 
0133     QObject::connect(slave, &Slave::connected, q, [this]() {
0134         slotConnected();
0135     });
0136 
0137     QObject::connect(slave, &Slave::privilegeOperationRequested, q, [this]() {
0138         slotPrivilegeOperationRequested();
0139     });
0140 
0141     if ((m_extraFlags & EF_TransferJobDataSent) == 0) { // this is a "get" job
0142         QObject::connect(slave, &Slave::totalSize, q, [this](KIO::filesize_t size) {
0143             slotTotalSize(size);
0144         });
0145 
0146         QObject::connect(slave, &Slave::processedSize, q, [this](KIO::filesize_t size) {
0147             slotProcessedSize(size);
0148         });
0149 
0150         QObject::connect(slave, &Slave::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         slave->send(CMD_META_DATA, packedArgs);
0172     }
0173 
0174     if (!m_subUrl.isEmpty()) { // TODO KF6 remove
0175         KIO_ARGS << m_subUrl;
0176         slave->send(CMD_SUBURL, packedArgs);
0177     }
0178 
0179     slave->send(m_command, m_packedArgs);
0180     if (q->isSuspended()) {
0181         slave->suspend();
0182     }
0183 }
0184 
0185 void SimpleJobPrivate::slaveDone()
0186 {
0187     Q_Q(SimpleJob);
0188     if (m_slave) {
0189         if (m_command == CMD_OPEN) {
0190             m_slave->send(CMD_CLOSE);
0191         }
0192         q->disconnect(m_slave); // Remove all signals between slave and job
0193     }
0194     // only finish a job once; Scheduler::jobFinished() resets schedSerial to zero.
0195     if (m_schedSerial) {
0196         Scheduler::jobFinished(q, m_slave);
0197     }
0198 }
0199 
0200 void SimpleJob::slotFinished()
0201 {
0202     Q_D(SimpleJob);
0203     // Return slave to the scheduler
0204     d->slaveDone();
0205 
0206     if (!hasSubjobs()) {
0207         if (!error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME)) {
0208             if (d->m_command == CMD_MKDIR) {
0209                 const QUrl urlDir = url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
0210 #ifndef KIO_ANDROID_STUB
0211                 org::kde::KDirNotify::emitFilesAdded(urlDir);
0212 #endif
0213             } else { /*if ( m_command == CMD_RENAME )*/
0214                 QUrl src;
0215                 QUrl dst;
0216                 QDataStream str(d->m_packedArgs);
0217                 str >> src >> dst;
0218                 if (src.adjusted(QUrl::RemoveFilename) == dst.adjusted(QUrl::RemoveFilename) // For the user, moving isn't renaming. Only renaming is.
0219 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101)
0220                     && !KProtocolInfo::slaveHandlesNotify(dst.scheme()).contains(QLatin1String("Rename"))
0221 #endif
0222                 ) {
0223 #ifndef KIO_ANDROID_STUB
0224                     org::kde::KDirNotify::emitFileRenamed(src, dst);
0225 #endif
0226                 }
0227 
0228 #ifndef KIO_ANDROID_STUB
0229                 org::kde::KDirNotify::emitFileMoved(src, dst);
0230 #endif
0231                 if (d->m_uiDelegateExtension) {
0232                     d->m_uiDelegateExtension->updateUrlInClipboard(src, dst);
0233                 }
0234             }
0235         }
0236         emitResult();
0237     }
0238 }
0239 
0240 void SimpleJob::slotError(int err, const QString &errorText)
0241 {
0242     Q_D(SimpleJob);
0243     setError(err);
0244     setErrorText(errorText);
0245     if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty()) {
0246         setErrorText(QString());
0247     }
0248     // error terminates the job
0249     slotFinished();
0250 }
0251 
0252 void SimpleJob::slotWarning(const QString &errorText)
0253 {
0254     Q_EMIT warning(this, errorText);
0255 }
0256 
0257 void SimpleJobPrivate::_k_slotSlaveInfoMessage(const QString &msg)
0258 {
0259     Q_EMIT q_func()->infoMessage(q_func(), msg);
0260 }
0261 
0262 void SimpleJobPrivate::slotConnected()
0263 {
0264     Q_EMIT q_func()->connected(q_func());
0265 }
0266 
0267 void SimpleJobPrivate::slotTotalSize(KIO::filesize_t size)
0268 {
0269     Q_Q(SimpleJob);
0270     if (size != q->totalAmount(KJob::Bytes)) {
0271         q->setTotalAmount(KJob::Bytes, size);
0272     }
0273 }
0274 
0275 void SimpleJobPrivate::slotProcessedSize(KIO::filesize_t size)
0276 {
0277     Q_Q(SimpleJob);
0278     // qDebug() << KIO::number(size);
0279     q->setProcessedAmount(KJob::Bytes, size);
0280 }
0281 
0282 void SimpleJobPrivate::slotSpeed(unsigned long speed)
0283 {
0284     // qDebug() << speed;
0285     q_func()->emitSpeed(speed);
0286 }
0287 
0288 void SimpleJobPrivate::restartAfterRedirection(QUrl *redirectionUrl)
0289 {
0290     Q_Q(SimpleJob);
0291     // Return slave to the scheduler while we still have the old URL in place; the scheduler
0292     // requires a job URL to stay invariant while the job is running.
0293     slaveDone();
0294 
0295     m_url = *redirectionUrl;
0296     redirectionUrl->clear();
0297     if ((m_extraFlags & EF_KillCalled) == 0) {
0298         Scheduler::doJob(q);
0299     }
0300 }
0301 
0302 void SimpleJob::slotMetaData(const KIO::MetaData &_metaData)
0303 {
0304     Q_D(SimpleJob);
0305     QMapIterator<QString, QString> it(_metaData);
0306     while (it.hasNext()) {
0307         it.next();
0308         if (it.key().startsWith(QLatin1String("{internal~"), Qt::CaseInsensitive)) {
0309             d->m_internalMetaData.insert(it.key(), it.value());
0310         } else {
0311             d->m_incomingMetaData.insert(it.key(), it.value());
0312         }
0313     }
0314 
0315     // Update the internal meta-data values as soon as possible. Waiting until
0316     // the KIO worker is finished has unintended consequences if the client starts
0317     // a new connection without waiting for the KIO worker to finish.
0318     if (!d->m_internalMetaData.isEmpty()) {
0319         Scheduler::updateInternalMetaData(this);
0320     }
0321 }
0322 
0323 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101)
0324 void SimpleJob::storeSSLSessionFromJob(const QUrl &redirectionURL)
0325 {
0326     Q_UNUSED(redirectionURL);
0327 }
0328 #endif
0329 
0330 void SimpleJobPrivate::slotPrivilegeOperationRequested()
0331 {
0332     m_slave->send(MSG_PRIVILEGE_EXEC, privilegeOperationData());
0333 }
0334 
0335 //////////
0336 SimpleJob *KIO::rmdir(const QUrl &url)
0337 {
0338     // qDebug() << "rmdir " << url;
0339     KIO_ARGS << url << qint8(false); // isFile is false
0340     return SimpleJobPrivate::newJob(url, CMD_DEL, packedArgs);
0341 }
0342 
0343 SimpleJob *KIO::chmod(const QUrl &url, int permissions)
0344 {
0345     // qDebug() << "chmod " << url;
0346     KIO_ARGS << url << permissions;
0347     return SimpleJobPrivate::newJob(url, CMD_CHMOD, packedArgs);
0348 }
0349 
0350 SimpleJob *KIO::chown(const QUrl &url, const QString &owner, const QString &group)
0351 {
0352     KIO_ARGS << url << owner << group;
0353     return SimpleJobPrivate::newJob(url, CMD_CHOWN, packedArgs);
0354 }
0355 
0356 SimpleJob *KIO::setModificationTime(const QUrl &url, const QDateTime &mtime)
0357 {
0358     // qDebug() << "setModificationTime " << url << " " << mtime;
0359     KIO_ARGS << url << mtime;
0360     return SimpleJobPrivate::newJobNoUi(url, CMD_SETMODIFICATIONTIME, packedArgs);
0361 }
0362 
0363 SimpleJob *KIO::rename(const QUrl &src, const QUrl &dest, JobFlags flags)
0364 {
0365     // qDebug() << "rename " << src << " " << dest;
0366     KIO_ARGS << src << dest << (qint8)(flags & Overwrite);
0367     return SimpleJobPrivate::newJob(src, CMD_RENAME, packedArgs, flags);
0368 }
0369 
0370 SimpleJob *KIO::symlink(const QString &target, const QUrl &dest, JobFlags flags)
0371 {
0372     // qDebug() << "symlink target=" << target << " " << dest;
0373     KIO_ARGS << target << dest << (qint8)(flags & Overwrite);
0374     return SimpleJobPrivate::newJob(dest, CMD_SYMLINK, packedArgs, flags);
0375 }
0376 
0377 SimpleJob *KIO::special(const QUrl &url, const QByteArray &data, JobFlags flags)
0378 {
0379     // qDebug() << "special " << url;
0380     return SimpleJobPrivate::newJob(url, CMD_SPECIAL, data, flags);
0381 }
0382 
0383 SimpleJob *KIO::mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags)
0384 {
0385     KIO_ARGS << int(1) << qint8(ro ? 1 : 0) << QString::fromLatin1(fstype) << dev << point;
0386     SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags);
0387     if (!(flags & HideProgressInfo)) {
0388         KIO::JobPrivate::emitMounting(job, dev, point);
0389     }
0390     return job;
0391 }
0392 
0393 SimpleJob *KIO::unmount(const QString &point, JobFlags flags)
0394 {
0395     KIO_ARGS << int(2) << point;
0396     SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags);
0397     if (!(flags & HideProgressInfo)) {
0398         KIO::JobPrivate::emitUnmounting(job, point);
0399     }
0400     return job;
0401 }
0402 
0403 //////////
0404 
0405 SimpleJob *KIO::http_update_cache(const QUrl &url, bool no_cache, const QDateTime &expireDate)
0406 {
0407     Q_ASSERT(url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https"));
0408     // Send http update_cache command (2)
0409     KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate.toMSecsSinceEpoch() / 1000);
0410     return SimpleJobPrivate::newJob(url, CMD_SPECIAL, packedArgs);
0411 }
0412 
0413 #include "moc_simplejob.cpp"