File indexing completed on 2025-02-16 06:50:26
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"