File indexing completed on 2023-10-03 03:19:55
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2004 Kevin Ottens <ervin@ipsquad.net> 0004 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "forwardingworkerbase.h" 0010 #include "../utils_p.h" 0011 0012 #include "deletejob.h" 0013 #include "job.h" 0014 #include "kiocoredebug.h" 0015 #include "mkdirjob.h" 0016 0017 #include <QEventLoop> 0018 #include <QMimeDatabase> 0019 0020 namespace KIO 0021 { 0022 class ForwardingWorkerBasePrivate 0023 { 0024 public: 0025 ForwardingWorkerBasePrivate(const QByteArray &protocol, QObject *eventLoopParent, ForwardingWorkerBase *qq) 0026 : q(qq) 0027 , m_protocol(QString::fromUtf8(protocol)) 0028 , eventLoop(eventLoopParent) 0029 { 0030 } 0031 ForwardingWorkerBase *const q; 0032 0033 const QString m_protocol; 0034 QUrl m_processedURL; 0035 QUrl m_requestedURL; 0036 0037 bool internalRewriteUrl(const QUrl &url, QUrl &newURL); 0038 0039 void connectJob(Job *job); 0040 void connectSimpleJob(SimpleJob *job); 0041 void connectListJob(ListJob *job); 0042 void connectTransferJob(TransferJob *job); 0043 0044 void _k_slotResult(KJob *job); 0045 void _k_slotWarning(KJob *job, const QString &msg) const; 0046 void _k_slotInfoMessage(KJob *job, const QString &msg) const; 0047 void _k_slotTotalSize(KJob *job, qulonglong size) const; 0048 void _k_slotProcessedSize(KJob *job, qulonglong size) const; 0049 void _k_slotSpeed(KJob *job, unsigned long bytesPerSecond) const; 0050 0051 // KIO::SimpleJob subclasses 0052 void _k_slotRedirection(KIO::Job *job, const QUrl &url); 0053 0054 // KIO::ListJob 0055 void _k_slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries) const; 0056 0057 // KIO::TransferJob 0058 void _k_slotData(KIO::Job *job, const QByteArray &data) const; 0059 void _k_slotDataReq(KIO::Job *job, QByteArray &data) const; 0060 void _k_slotMimetype(KIO::Job *job, const QString &type) const; 0061 void _k_slotCanResume(KIO::Job *job, KIO::filesize_t offset) const; 0062 0063 [[nodiscard]] WorkerResult loopResult() 0064 { 0065 eventLoop.exec(); 0066 return m_pendingResult; 0067 } 0068 0069 private: 0070 // These are intentionally private to force us to go through [[nodiscard]] helper functions, lest we forget to retrieve the result. 0071 QEventLoop eventLoop; 0072 WorkerResult m_pendingResult = WorkerResult::pass(); 0073 }; 0074 0075 ForwardingWorkerBase::ForwardingWorkerBase(const QByteArray &protocol, const QByteArray &poolSocket, const QByteArray &appSocket) 0076 : WorkerBase(protocol, poolSocket, appSocket) 0077 , d(new ForwardingWorkerBasePrivate(protocol, this, this)) 0078 { 0079 } 0080 0081 ForwardingWorkerBase::~ForwardingWorkerBase() = default; 0082 0083 bool ForwardingWorkerBasePrivate::internalRewriteUrl(const QUrl &url, QUrl &newURL) 0084 { 0085 bool result = true; 0086 0087 if (url.scheme() == m_protocol) { 0088 result = q->rewriteUrl(url, newURL); 0089 } else { 0090 newURL = url; 0091 } 0092 0093 m_processedURL = newURL; 0094 m_requestedURL = url; 0095 return result; 0096 } 0097 0098 void ForwardingWorkerBase::adjustUDSEntry(KIO::UDSEntry &entry, UDSEntryCreationMode creationMode) const 0099 { 0100 const bool listing = (creationMode == UDSEntryCreationInListDir); 0101 // qDebug() << "listing==" << listing; 0102 0103 const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); 0104 QString mimetype = entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE); 0105 QUrl url; 0106 const QString urlStr = entry.stringValue(KIO::UDSEntry::UDS_URL); 0107 const bool url_found = !urlStr.isEmpty(); 0108 if (url_found) { 0109 url = QUrl(urlStr); 0110 QUrl new_url(d->m_requestedURL); 0111 if (listing) { 0112 new_url.setPath(Utils::concatPaths(new_url.path(), url.fileName())); 0113 } 0114 // ## Didn't find a way to use an iterator instead of re-doing a key lookup 0115 entry.replace(KIO::UDSEntry::UDS_URL, new_url.toString()); 0116 // qDebug() << "URL =" << url; 0117 // qDebug() << "New URL =" << new_url; 0118 } 0119 0120 if (mimetype.isEmpty()) { 0121 QUrl new_url(d->m_processedURL); 0122 if (url_found && listing) { 0123 new_url.setPath(Utils::concatPaths(new_url.path(), url.fileName())); 0124 } else if (listing) { 0125 new_url.setPath(Utils::concatPaths(new_url.path(), name)); 0126 } 0127 0128 QMimeDatabase db; 0129 mimetype = db.mimeTypeForUrl(new_url).name(); 0130 0131 entry.replace(KIO::UDSEntry::UDS_MIME_TYPE, mimetype); 0132 0133 // qDebug() << "New MIME type = " << mimetype; 0134 } 0135 0136 if (d->m_processedURL.isLocalFile()) { 0137 QUrl new_url(d->m_processedURL); 0138 if (listing) { 0139 new_url.setPath(Utils::concatPaths(new_url.path(), name)); 0140 } 0141 0142 entry.replace(KIO::UDSEntry::UDS_LOCAL_PATH, new_url.toLocalFile()); 0143 } 0144 } 0145 0146 QUrl ForwardingWorkerBase::processedUrl() const 0147 { 0148 return d->m_processedURL; 0149 } 0150 0151 QUrl ForwardingWorkerBase::requestedUrl() const 0152 { 0153 return d->m_requestedURL; 0154 } 0155 0156 WorkerResult ForwardingWorkerBase::get(const QUrl &url) 0157 { 0158 QUrl new_url; 0159 if (d->internalRewriteUrl(url, new_url)) { 0160 KIO::TransferJob *job = KIO::get(new_url, NoReload, HideProgressInfo); 0161 d->connectTransferJob(job); 0162 0163 return d->loopResult(); 0164 } 0165 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0166 } 0167 0168 WorkerResult ForwardingWorkerBase::put(const QUrl &url, int permissions, JobFlags flags) 0169 { 0170 QUrl new_url; 0171 if (d->internalRewriteUrl(url, new_url)) { 0172 KIO::TransferJob *job = KIO::put(new_url, permissions, flags | HideProgressInfo); 0173 d->connectTransferJob(job); 0174 0175 return d->loopResult(); 0176 } 0177 return WorkerResult::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString()); 0178 } 0179 0180 WorkerResult ForwardingWorkerBase::stat(const QUrl &url) 0181 { 0182 QUrl new_url; 0183 if (d->internalRewriteUrl(url, new_url)) { 0184 KIO::SimpleJob *job = KIO::stat(new_url, KIO::HideProgressInfo); 0185 d->connectSimpleJob(job); 0186 0187 return d->loopResult(); 0188 } 0189 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0190 } 0191 0192 WorkerResult ForwardingWorkerBase::mimetype(const QUrl &url) 0193 { 0194 QUrl new_url; 0195 if (d->internalRewriteUrl(url, new_url)) { 0196 KIO::TransferJob *job = KIO::mimetype(new_url, KIO::HideProgressInfo); 0197 d->connectTransferJob(job); 0198 0199 return d->loopResult(); 0200 } 0201 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0202 } 0203 0204 WorkerResult ForwardingWorkerBase::listDir(const QUrl &url) 0205 { 0206 QUrl new_url; 0207 if (d->internalRewriteUrl(url, new_url)) { 0208 KIO::ListJob *job = KIO::listDir(new_url, KIO::HideProgressInfo); 0209 d->connectListJob(job); 0210 0211 return d->loopResult(); 0212 } 0213 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0214 } 0215 0216 WorkerResult ForwardingWorkerBase::mkdir(const QUrl &url, int permissions) 0217 { 0218 QUrl new_url; 0219 if (d->internalRewriteUrl(url, new_url)) { 0220 KIO::SimpleJob *job = KIO::mkdir(new_url, permissions); 0221 d->connectSimpleJob(job); 0222 0223 return d->loopResult(); 0224 } 0225 return WorkerResult::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString()); 0226 } 0227 0228 WorkerResult ForwardingWorkerBase::rename(const QUrl &src, const QUrl &dest, JobFlags flags) 0229 { 0230 qCDebug(KIO_CORE) << "rename" << src << dest; 0231 0232 QUrl new_src; 0233 QUrl new_dest; 0234 if (!d->internalRewriteUrl(src, new_src)) { 0235 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString()); 0236 } 0237 if (d->internalRewriteUrl(dest, new_dest)) { 0238 KIO::Job *job = KIO::rename(new_src, new_dest, flags); 0239 d->connectJob(job); 0240 0241 return d->loopResult(); 0242 } 0243 return WorkerResult::fail(KIO::ERR_MALFORMED_URL, dest.toDisplayString()); 0244 } 0245 0246 WorkerResult ForwardingWorkerBase::symlink(const QString &target, const QUrl &dest, JobFlags flags) 0247 { 0248 qCDebug(KIO_CORE) << "symlink" << target << dest; 0249 0250 QUrl new_dest; 0251 if (d->internalRewriteUrl(dest, new_dest)) { 0252 KIO::SimpleJob *job = KIO::symlink(target, new_dest, flags | HideProgressInfo); 0253 d->connectSimpleJob(job); 0254 0255 return d->loopResult(); 0256 } 0257 return WorkerResult::fail(KIO::ERR_MALFORMED_URL, dest.toDisplayString()); 0258 } 0259 0260 WorkerResult ForwardingWorkerBase::chmod(const QUrl &url, int permissions) 0261 { 0262 QUrl new_url; 0263 if (d->internalRewriteUrl(url, new_url)) { 0264 KIO::SimpleJob *job = KIO::chmod(new_url, permissions); 0265 d->connectSimpleJob(job); 0266 0267 return d->loopResult(); 0268 } 0269 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0270 } 0271 0272 WorkerResult ForwardingWorkerBase::setModificationTime(const QUrl &url, const QDateTime &mtime) 0273 { 0274 QUrl new_url; 0275 if (d->internalRewriteUrl(url, new_url)) { 0276 KIO::SimpleJob *job = KIO::setModificationTime(new_url, mtime); 0277 d->connectSimpleJob(job); 0278 0279 return d->loopResult(); 0280 } 0281 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0282 } 0283 0284 WorkerResult ForwardingWorkerBase::copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) 0285 { 0286 qCDebug(KIO_CORE) << "copy" << src << dest; 0287 0288 QUrl new_src; 0289 QUrl new_dest; 0290 if (!d->internalRewriteUrl(src, new_src)) { 0291 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString()); 0292 } 0293 if (d->internalRewriteUrl(dest, new_dest)) { 0294 KIO::Job *job = KIO::file_copy(new_src, new_dest, permissions, flags | HideProgressInfo); 0295 d->connectJob(job); 0296 0297 return d->loopResult(); 0298 } 0299 return WorkerResult::fail(KIO::ERR_MALFORMED_URL, dest.toDisplayString()); 0300 } 0301 0302 WorkerResult ForwardingWorkerBase::del(const QUrl &url, bool isfile) 0303 { 0304 QUrl new_url; 0305 if (d->internalRewriteUrl(url, new_url)) { 0306 if (isfile) { 0307 KIO::DeleteJob *job = KIO::del(new_url, HideProgressInfo); 0308 d->connectJob(job); 0309 } else { 0310 KIO::SimpleJob *job = KIO::rmdir(new_url); 0311 d->connectSimpleJob(job); 0312 } 0313 0314 return d->loopResult(); 0315 } 0316 return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); 0317 } 0318 0319 ////////////////////////////////////////////////////////////////////////////// 0320 0321 void ForwardingWorkerBasePrivate::connectJob(KIO::Job *job) 0322 { 0323 // We will forward the warning message, no need to let the job 0324 // display it itself 0325 job->setUiDelegate(nullptr); 0326 0327 // Forward metadata (e.g. modification time for put()) 0328 job->setMetaData(q->allMetaData()); 0329 0330 q->connect(job, &KJob::result, q, [this](KJob *job) { 0331 _k_slotResult(job); 0332 }); 0333 q->connect(job, &KJob::warning, q, [this](KJob *job, const QString &text) { 0334 _k_slotWarning(job, text); 0335 }); 0336 q->connect(job, &KJob::infoMessage, q, [this](KJob *job, const QString &info) { 0337 _k_slotInfoMessage(job, info); 0338 }); 0339 q->connect(job, &KJob::totalSize, q, [this](KJob *job, qulonglong size) { 0340 _k_slotTotalSize(job, size); 0341 }); 0342 q->connect(job, &KJob::processedSize, q, [this](KJob *job, qulonglong size) { 0343 _k_slotProcessedSize(job, size); 0344 }); 0345 q->connect(job, &KJob::speed, q, [this](KJob *job, ulong speed) { 0346 _k_slotSpeed(job, speed); 0347 }); 0348 } 0349 0350 void ForwardingWorkerBasePrivate::connectSimpleJob(KIO::SimpleJob *job) 0351 { 0352 connectJob(job); 0353 if (job->metaObject()->indexOfSignal("redirection(KIO::Job*,QUrl)") > -1) { 0354 q->connect(job, SIGNAL(redirection(KIO::Job *, QUrl)), SLOT(_k_slotRedirection(KIO::Job *, QUrl))); 0355 } 0356 } 0357 0358 void ForwardingWorkerBasePrivate::connectListJob(KIO::ListJob *job) 0359 { 0360 connectSimpleJob(job); 0361 q->connect(job, &KIO::ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &entries) { 0362 _k_slotEntries(job, entries); 0363 }); 0364 } 0365 0366 void ForwardingWorkerBasePrivate::connectTransferJob(KIO::TransferJob *job) 0367 { 0368 connectSimpleJob(job); 0369 q->connect(job, &KIO::TransferJob::data, q, [this](KIO::Job *job, const QByteArray &data) { 0370 _k_slotData(job, data); 0371 }); 0372 q->connect(job, &KIO::TransferJob::dataReq, q, [this](KIO::Job *job, QByteArray &data) { 0373 _k_slotDataReq(job, data); 0374 }); 0375 q->connect(job, &KIO::TransferJob::mimeTypeFound, q, [this](KIO::Job *job, const QString &mimeType) { 0376 _k_slotMimetype(job, mimeType); 0377 }); 0378 q->connect(job, &KIO::TransferJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) { 0379 _k_slotCanResume(job, offset); 0380 }); 0381 } 0382 0383 ////////////////////////////////////////////////////////////////////////////// 0384 0385 void ForwardingWorkerBasePrivate::_k_slotResult(KJob *job) 0386 { 0387 if (job->error() != 0) { 0388 m_pendingResult = WorkerResult::fail(job->error(), job->errorText()); 0389 } else { 0390 if (auto stat_job = qobject_cast<KIO::StatJob *>(job)) { 0391 KIO::UDSEntry entry = stat_job->statResult(); 0392 q->adjustUDSEntry(entry, ForwardingWorkerBase::UDSEntryCreationInStat); 0393 q->statEntry(entry); 0394 } 0395 m_pendingResult = WorkerResult::pass(); 0396 } 0397 0398 eventLoop.exit(); 0399 } 0400 0401 void ForwardingWorkerBasePrivate::_k_slotWarning(KJob * /*job*/, const QString &msg) const 0402 { 0403 q->warning(msg); 0404 } 0405 0406 void ForwardingWorkerBasePrivate::_k_slotInfoMessage(KJob * /*job*/, const QString &msg) const 0407 { 0408 q->infoMessage(msg); 0409 } 0410 0411 void ForwardingWorkerBasePrivate::_k_slotTotalSize(KJob * /*job*/, qulonglong size) const 0412 { 0413 q->totalSize(size); 0414 } 0415 0416 void ForwardingWorkerBasePrivate::_k_slotProcessedSize(KJob * /*job*/, qulonglong size) const 0417 { 0418 q->processedSize(size); 0419 } 0420 0421 void ForwardingWorkerBasePrivate::_k_slotSpeed(KJob * /*job*/, unsigned long bytesPerSecond) const 0422 { 0423 q->speed(bytesPerSecond); 0424 } 0425 0426 void ForwardingWorkerBasePrivate::_k_slotRedirection(KIO::Job *job, const QUrl &url) 0427 { 0428 q->redirection(url); 0429 0430 // We've been redirected stop everything. 0431 job->kill(KJob::Quietly); 0432 m_pendingResult = WorkerResult::pass(); 0433 0434 eventLoop.exit(); 0435 } 0436 0437 void ForwardingWorkerBasePrivate::_k_slotEntries(KIO::Job * /*job*/, const KIO::UDSEntryList &entries) const 0438 { 0439 KIO::UDSEntryList final_entries = entries; 0440 0441 for (auto &entry : final_entries) { 0442 q->adjustUDSEntry(entry, ForwardingWorkerBase::UDSEntryCreationInListDir); 0443 } 0444 0445 q->listEntries(final_entries); 0446 } 0447 0448 void ForwardingWorkerBasePrivate::_k_slotData(KIO::Job * /*job*/, const QByteArray &_data) const 0449 { 0450 q->data(_data); 0451 } 0452 0453 void ForwardingWorkerBasePrivate::_k_slotDataReq(KIO::Job * /*job*/, QByteArray &data) const 0454 { 0455 q->dataReq(); 0456 q->readData(data); 0457 } 0458 0459 void ForwardingWorkerBasePrivate::_k_slotMimetype(KIO::Job * /*job*/, const QString &type) const 0460 { 0461 q->mimeType(type); 0462 } 0463 0464 void ForwardingWorkerBasePrivate::_k_slotCanResume(KIO::Job * /*job*/, KIO::filesize_t offset) const 0465 { 0466 q->canResume(offset); 0467 } 0468 0469 } // namespace KIO 0470 0471 #include "moc_forwardingworkerbase.cpp"