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"