File indexing completed on 2024-05-05 16:13:10

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"