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