File indexing completed on 2023-09-24 04:08:42
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"