/* This file is part of the KDE libraries SPDX-FileCopyrightText: 2000 Stephan Kulow SPDX-FileCopyrightText: 2000-2013 David Faure SPDX-License-Identifier: LGPL-2.0-or-later */ #include "simplejob.h" #include "job_p.h" #include "kprotocolinfo.h" #include "scheduler.h" #include "worker_p.h" #include #include #include using namespace KIO; SimpleJob::SimpleJob(SimpleJobPrivate &dd) : Job(dd) { d_func()->simpleJobInit(); } void SimpleJobPrivate::simpleJobInit() { Q_Q(SimpleJob); if (!m_url.isValid() || m_url.scheme().isEmpty()) { qCWarning(KIO_CORE) << "Invalid URL:" << m_url; q->setError(ERR_MALFORMED_URL); q->setErrorText(m_url.toString()); QTimer::singleShot(0, q, &SimpleJob::slotFinished); return; } Scheduler::doJob(q); } bool SimpleJob::doKill() { Q_D(SimpleJob); if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) { d->m_extraFlags |= JobPrivate::EF_KillCalled; Scheduler::cancelJob(this); // deletes the worker if not 0 } else { qCWarning(KIO_CORE) << this << "killed twice, this is overkill"; } return Job::doKill(); } bool SimpleJob::doSuspend() { Q_D(SimpleJob); if (d->m_worker) { d->m_worker->suspend(); } return Job::doSuspend(); } bool SimpleJob::doResume() { Q_D(SimpleJob); if (d->m_worker) { d->m_worker->resume(); } return Job::doResume(); } const QUrl &SimpleJob::url() const { return d_func()->m_url; } void SimpleJob::putOnHold() { Q_D(SimpleJob); Q_ASSERT(d->m_worker); if (d->m_worker) { Scheduler::putWorkerOnHold(this, d->m_url); } // we should now be disassociated from the worker Q_ASSERT(!d->m_worker); kill(Quietly); } void SimpleJob::removeOnHold() { Scheduler::removeWorkerOnHold(); } bool SimpleJob::isRedirectionHandlingEnabled() const { return d_func()->m_redirectionHandlingEnabled; } void SimpleJob::setRedirectionHandlingEnabled(bool handle) { Q_D(SimpleJob); d->m_redirectionHandlingEnabled = handle; } SimpleJob::~SimpleJob() { Q_D(SimpleJob); // last chance to remove this job from the scheduler! if (d->m_schedSerial) { // qDebug() << "Killing job" << this << "in destructor!"/* << qBacktrace()*/; Scheduler::cancelJob(this); } } void SimpleJobPrivate::start(Worker *worker) { Q_Q(SimpleJob); m_worker = worker; // Worker::setJob can send us SSL metadata if there is a persistent connection QObject::connect(worker, &Worker::metaData, q, &SimpleJob::slotMetaData); worker->setJob(q); QObject::connect(worker, &Worker::error, q, &SimpleJob::slotError); QObject::connect(worker, &Worker::warning, q, &SimpleJob::slotWarning); QObject::connect(worker, &Worker::finished, q, &SimpleJob::slotFinished); QObject::connect(worker, &Worker::infoMessage, q, [this](const QString &message) { _k_slotWorkerInfoMessage(message); }); QObject::connect(worker, &Worker::connected, q, [this]() { slotConnected(); }); QObject::connect(worker, &Worker::privilegeOperationRequested, q, [this]() { slotPrivilegeOperationRequested(); }); if ((m_extraFlags & EF_TransferJobDataSent) == 0) { // this is a "get" job QObject::connect(worker, &Worker::totalSize, q, [this](KIO::filesize_t size) { slotTotalSize(size); }); QObject::connect(worker, &Worker::processedSize, q, [this](KIO::filesize_t size) { slotProcessedSize(size); }); QObject::connect(worker, &Worker::speed, q, [this](ulong speed) { slotSpeed(speed); }); } const QVariant windowIdProp = q->property("window-id"); // see KJobWidgets::setWindow if (windowIdProp.isValid()) { m_outgoingMetaData.insert(QStringLiteral("window-id"), QString::number(windowIdProp.toULongLong())); } const QVariant userTimestampProp = q->property("userTimestamp"); // see KJobWidgets::updateUserTimestamp if (userTimestampProp.isValid()) { m_outgoingMetaData.insert(QStringLiteral("user-timestamp"), QString::number(userTimestampProp.toULongLong())); } if (q->uiDelegate() == nullptr) { // not interactive m_outgoingMetaData.insert(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); } if (!m_outgoingMetaData.isEmpty()) { KIO_ARGS << m_outgoingMetaData; worker->send(CMD_META_DATA, packedArgs); } worker->send(m_command, m_packedArgs); if (q->isSuspended()) { worker->suspend(); } } void SimpleJobPrivate::workerDone() { Q_Q(SimpleJob); if (m_worker) { if (m_command == CMD_OPEN) { m_worker->send(CMD_CLOSE); } q->disconnect(m_worker); // Remove all signals between worker and job } // only finish a job once; Scheduler::jobFinished() resets schedSerial to zero. if (m_schedSerial) { Scheduler::jobFinished(q, m_worker); } } void SimpleJob::slotFinished() { Q_D(SimpleJob); // Return worker to the scheduler d->workerDone(); if (!hasSubjobs()) { if (!error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME)) { if (d->m_command == CMD_MKDIR) { const QUrl urlDir = url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); #ifdef WITH_QTDBUS org::kde::KDirNotify::emitFilesAdded(urlDir); #endif } else { /*if ( m_command == CMD_RENAME )*/ QUrl src; QUrl dst; QDataStream str(d->m_packedArgs); str >> src >> dst; if (src.adjusted(QUrl::RemoveFilename) == dst.adjusted(QUrl::RemoveFilename) // For the user, moving isn't // renaming. Only renaming is. ) { #ifdef WITH_QTDBUS org::kde::KDirNotify::emitFileRenamed(src, dst); #endif } #ifdef WITH_QTDBUS org::kde::KDirNotify::emitFileMoved(src, dst); #endif if (d->m_uiDelegateExtension) { d->m_uiDelegateExtension->updateUrlInClipboard(src, dst); } } } emitResult(); } } void SimpleJob::slotError(int err, const QString &errorText) { Q_D(SimpleJob); setError(err); setErrorText(errorText); if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty()) { setErrorText(QString()); } // error terminates the job slotFinished(); } void SimpleJob::slotWarning(const QString &errorText) { Q_EMIT warning(this, errorText); } void SimpleJobPrivate::_k_slotWorkerInfoMessage(const QString &msg) { Q_EMIT q_func()->infoMessage(q_func(), msg); } void SimpleJobPrivate::slotConnected() { Q_EMIT q_func()->connected(q_func()); } void SimpleJobPrivate::slotTotalSize(KIO::filesize_t size) { Q_Q(SimpleJob); if (size != q->totalAmount(KJob::Bytes)) { q->setTotalAmount(KJob::Bytes, size); } } void SimpleJobPrivate::slotProcessedSize(KIO::filesize_t size) { Q_Q(SimpleJob); // qDebug() << KIO::number(size); q->setProcessedAmount(KJob::Bytes, size); } void SimpleJobPrivate::slotSpeed(unsigned long speed) { // qDebug() << speed; q_func()->emitSpeed(speed); } void SimpleJobPrivate::restartAfterRedirection(QUrl *redirectionUrl) { Q_Q(SimpleJob); // Return worker to the scheduler while we still have the old URL in place; the scheduler // requires a job URL to stay invariant while the job is running. workerDone(); m_url = *redirectionUrl; redirectionUrl->clear(); if ((m_extraFlags & EF_KillCalled) == 0) { Scheduler::doJob(q); } } void SimpleJob::slotMetaData(const KIO::MetaData &_metaData) { Q_D(SimpleJob); QMapIterator it(_metaData); while (it.hasNext()) { it.next(); if (it.key().startsWith(QLatin1String("{internal~"), Qt::CaseInsensitive)) { d->m_internalMetaData.insert(it.key(), it.value()); } else { d->m_incomingMetaData.insert(it.key(), it.value()); } } // Update the internal meta-data values as soon as possible. Waiting until // the KIO worker is finished has unintended consequences if the client starts // a new connection without waiting for the KIO worker to finish. if (!d->m_internalMetaData.isEmpty()) { Scheduler::updateInternalMetaData(this); } } void SimpleJobPrivate::slotPrivilegeOperationRequested() { m_worker->send(MSG_PRIVILEGE_EXEC, privilegeOperationData()); } ////////// SimpleJob *KIO::rmdir(const QUrl &url) { // qDebug() << "rmdir " << url; KIO_ARGS << url << qint8(false); // isFile is false return SimpleJobPrivate::newJob(url, CMD_DEL, packedArgs); } SimpleJob *KIO::chmod(const QUrl &url, int permissions) { // qDebug() << "chmod " << url; KIO_ARGS << url << permissions; return SimpleJobPrivate::newJob(url, CMD_CHMOD, packedArgs); } SimpleJob *KIO::chown(const QUrl &url, const QString &owner, const QString &group) { KIO_ARGS << url << owner << group; return SimpleJobPrivate::newJob(url, CMD_CHOWN, packedArgs); } SimpleJob *KIO::setModificationTime(const QUrl &url, const QDateTime &mtime) { // qDebug() << "setModificationTime " << url << " " << mtime; KIO_ARGS << url << mtime; return SimpleJobPrivate::newJobNoUi(url, CMD_SETMODIFICATIONTIME, packedArgs); } SimpleJob *KIO::rename(const QUrl &src, const QUrl &dest, JobFlags flags) { // qDebug() << "rename " << src << " " << dest; KIO_ARGS << src << dest << (qint8)(flags & Overwrite); return SimpleJobPrivate::newJob(src, CMD_RENAME, packedArgs, flags); } SimpleJob *KIO::symlink(const QString &target, const QUrl &dest, JobFlags flags) { // qDebug() << "symlink target=" << target << " " << dest; KIO_ARGS << target << dest << (qint8)(flags & Overwrite); return SimpleJobPrivate::newJob(dest, CMD_SYMLINK, packedArgs, flags); } SimpleJob *KIO::special(const QUrl &url, const QByteArray &data, JobFlags flags) { // qDebug() << "special " << url; return SimpleJobPrivate::newJob(url, CMD_SPECIAL, data, flags); } SimpleJob *KIO::mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags) { KIO_ARGS << int(1) << qint8(ro ? 1 : 0) << QString::fromLatin1(fstype) << dev << point; SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags); if (!(flags & HideProgressInfo)) { KIO::JobPrivate::emitMounting(job, dev, point); } return job; } SimpleJob *KIO::unmount(const QString &point, JobFlags flags) { KIO_ARGS << int(2) << point; SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags); if (!(flags & HideProgressInfo)) { KIO::JobPrivate::emitUnmounting(job, point); } return job; } ////////// SimpleJob *KIO::http_update_cache(const QUrl &url, bool no_cache, const QDateTime &expireDate) { Q_ASSERT(url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")); // Send http update_cache command (2) KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate.toMSecsSinceEpoch() / 1000); return SimpleJobPrivate::newJob(url, CMD_SPECIAL, packedArgs); } #include "moc_simplejob.cpp"