File indexing completed on 2024-04-28 15:26:35

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
0004     SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "statjob.h"
0010 
0011 #include "job_p.h"
0012 #include "scheduler.h"
0013 #include "slave.h"
0014 #include <KProtocolInfo>
0015 #include <QTimer>
0016 #include <kurlauthorized.h>
0017 
0018 using namespace KIO;
0019 
0020 class KIO::StatJobPrivate : public SimpleJobPrivate
0021 {
0022 public:
0023     inline StatJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs)
0024         : SimpleJobPrivate(url, command, packedArgs)
0025         , m_bSource(true)
0026         , m_details(KIO::StatDefaultDetails)
0027     {
0028     }
0029 
0030     UDSEntry m_statResult;
0031     QUrl m_redirectionURL;
0032     bool m_bSource;
0033     KIO::StatDetails m_details;
0034     void slotStatEntry(const KIO::UDSEntry &entry);
0035     void slotRedirection(const QUrl &url);
0036 
0037     /**
0038      * @internal
0039      * Called by the scheduler when a @p slave gets to
0040      * work on this job.
0041      * @param slave the slave that starts working on this job
0042      */
0043     void start(Slave *slave) override;
0044 
0045     Q_DECLARE_PUBLIC(StatJob)
0046 
0047     static inline StatJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, JobFlags flags)
0048     {
0049         StatJob *job = new StatJob(*new StatJobPrivate(url, command, packedArgs));
0050         job->setUiDelegate(KIO::createDefaultJobUiDelegate());
0051         if (!(flags & HideProgressInfo)) {
0052             job->setFinishedNotificationHidden();
0053             KIO::getJobTracker()->registerJob(job);
0054             emitStating(job, url);
0055         }
0056         return job;
0057     }
0058 };
0059 
0060 StatJob::StatJob(StatJobPrivate &dd)
0061     : SimpleJob(dd)
0062 {
0063     setTotalAmount(Items, 1);
0064 }
0065 
0066 StatJob::~StatJob()
0067 {
0068 }
0069 
0070 #if KIOCORE_BUILD_DEPRECATED_SINCE(4, 0)
0071 void StatJob::setSide(bool source)
0072 {
0073     d_func()->m_bSource = source;
0074 }
0075 #endif
0076 
0077 void StatJob::setSide(StatSide side)
0078 {
0079     d_func()->m_bSource = side == SourceSide;
0080 }
0081 
0082 void StatJob::setDetails(KIO::StatDetails details)
0083 {
0084     d_func()->m_details = details;
0085 }
0086 
0087 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 69)
0088 void StatJob::setDetails(short int details)
0089 {
0090     // for backward compatibility
0091     d_func()->m_details = detailsToStatDetails(details);
0092 }
0093 
0094 void StatJob::setDetails(KIO::StatDetail detail)
0095 {
0096     d_func()->m_details = detail;
0097 }
0098 
0099 KIO::StatDetails KIO::detailsToStatDetails(int details)
0100 {
0101     KIO::StatDetails detailsFlag = KIO::StatBasic;
0102     if (details > 0) {
0103         detailsFlag |= KIO::StatUser | KIO::StatTime;
0104     }
0105     if (details > 1) {
0106         detailsFlag |= KIO::StatResolveSymlink | KIO::StatAcl;
0107     }
0108     if (details > 2) {
0109         detailsFlag |= KIO::StatInode;
0110     }
0111     return detailsFlag;
0112 }
0113 #endif
0114 
0115 const UDSEntry &StatJob::statResult() const
0116 {
0117     return d_func()->m_statResult;
0118 }
0119 
0120 QUrl StatJob::mostLocalUrl() const
0121 {
0122     const QUrl _url = url();
0123 
0124     if (_url.isLocalFile()) {
0125         return _url;
0126     }
0127 
0128     const UDSEntry &udsEntry = d_func()->m_statResult;
0129     const QString path = udsEntry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH);
0130 
0131     if (path.isEmpty()) { // Return url as-is
0132         return _url;
0133     }
0134 
0135     const QString protoClass = KProtocolInfo::protocolClass(_url.scheme());
0136     if (protoClass != QLatin1String(":local")) { // UDS_LOCAL_PATH was set but wrong Class
0137         qCWarning(KIO_CORE) << "The protocol Class of the url that was being stat'ed" << _url << ", is" << protoClass
0138                             << ", however UDS_LOCAL_PATH was set; if you use UDS_LOCAL_PATH, the protocolClass"
0139                                " should be :local, see KProtocolInfo API docs for more details.";
0140         return _url;
0141     }
0142 
0143     return QUrl::fromLocalFile(path);
0144 }
0145 
0146 void StatJobPrivate::start(Slave *slave)
0147 {
0148     Q_Q(StatJob);
0149     m_outgoingMetaData.insert(QStringLiteral("statSide"), m_bSource ? QStringLiteral("source") : QStringLiteral("dest"));
0150     m_outgoingMetaData.insert(QStringLiteral("statDetails"), QString::number(m_details));
0151 
0152     q->connect(slave, &KIO::SlaveInterface::statEntry, q, [this](const KIO::UDSEntry &entry) {
0153         slotStatEntry(entry);
0154     });
0155     q->connect(slave, &KIO::SlaveInterface::redirection, q, [this](const QUrl &url) {
0156         slotRedirection(url);
0157     });
0158 
0159     SimpleJobPrivate::start(slave);
0160 }
0161 
0162 void StatJobPrivate::slotStatEntry(const KIO::UDSEntry &entry)
0163 {
0164     // qCDebug(KIO_CORE);
0165     m_statResult = entry;
0166 }
0167 
0168 // Worker got a redirection request
0169 void StatJobPrivate::slotRedirection(const QUrl &url)
0170 {
0171     Q_Q(StatJob);
0172     // qCDebug(KIO_CORE) << m_url << "->" << url;
0173     if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("redirect"), m_url, url)) {
0174         qCWarning(KIO_CORE) << "Redirection from" << m_url << "to" << url << "REJECTED!";
0175         q->setError(ERR_ACCESS_DENIED);
0176         q->setErrorText(url.toDisplayString());
0177         return;
0178     }
0179     m_redirectionURL = url; // We'll remember that when the job finishes
0180     // Tell the user that we haven't finished yet
0181     Q_EMIT q->redirection(q, m_redirectionURL);
0182 }
0183 
0184 void StatJob::slotFinished()
0185 {
0186     Q_D(StatJob);
0187 
0188     if (!d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid()) {
0189         // qCDebug(KIO_CORE) << "StatJob: Redirection to " << m_redirectionURL;
0190         if (queryMetaData(QStringLiteral("permanent-redirect")) == QLatin1String("true")) {
0191             Q_EMIT permanentRedirection(this, d->m_url, d->m_redirectionURL);
0192         }
0193 
0194         if (d->m_redirectionHandlingEnabled) {
0195             d->m_packedArgs.truncate(0);
0196             QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly);
0197             stream << d->m_redirectionURL;
0198 
0199             d->restartAfterRedirection(&d->m_redirectionURL);
0200             return;
0201         }
0202     }
0203 
0204     // Return worker to the scheduler
0205     SimpleJob::slotFinished();
0206 }
0207 
0208 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101)
0209 void StatJob::slotMetaData(const KIO::MetaData &_metaData)
0210 {
0211     SimpleJob::slotMetaData(_metaData);
0212 }
0213 #endif
0214 
0215 StatJob *KIO::stat(const QUrl &url, JobFlags flags)
0216 {
0217     // Assume sideIsSource. Gets are more common than puts.
0218     return statDetails(url, StatJob::SourceSide, KIO::StatDefaultDetails, flags);
0219 }
0220 
0221 static bool isUrlValid(const QUrl &url)
0222 {
0223     if (!url.isValid()) {
0224         qCWarning(KIO_CORE) << "Invalid url:" << url << ", cancelling job.";
0225         return false;
0226     }
0227 
0228     if (url.isLocalFile()) {
0229         qCWarning(KIO_CORE) << "Url" << url << "already represents a local file, cancelling job.";
0230         return false;
0231     }
0232 
0233     if (KProtocolInfo::protocolClass(url.scheme()) != QLatin1String(":local")) {
0234         qCWarning(KIO_CORE) << "Protocol Class of url" << url << ", isn't ':local', cancelling job.";
0235         return false;
0236     }
0237 
0238     return true;
0239 }
0240 
0241 StatJob *KIO::mostLocalUrl(const QUrl &url, JobFlags flags)
0242 {
0243     StatJob *job = statDetails(url, StatJob::SourceSide, KIO::StatDefaultDetails, flags);
0244     if (!isUrlValid(url)) {
0245         QTimer::singleShot(0, job, &StatJob::slotFinished);
0246         Scheduler::cancelJob(job); // deletes the worker if not 0
0247     }
0248     return job;
0249 }
0250 
0251 #if KIOCORE_BUILD_DEPRECATED_SINCE(4, 0)
0252 StatJob *KIO::stat(const QUrl &url, bool sideIsSource, short int details, JobFlags flags)
0253 {
0254     // qCDebug(KIO_CORE) << "stat" << url;
0255     KIO_ARGS << url;
0256     StatJob *job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags);
0257     job->setSide(sideIsSource ? StatJob::SourceSide : StatJob::DestinationSide);
0258     job->setDetails(details);
0259     return job;
0260 }
0261 #endif
0262 
0263 StatJob *KIO::statDetails(const QUrl &url, KIO::StatJob::StatSide side, KIO::StatDetails details, JobFlags flags)
0264 {
0265     // TODO KF6: rename to stat
0266     // qCDebug(KIO_CORE) << "stat" << url;
0267     KIO_ARGS << url;
0268     StatJob *job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags);
0269     job->setSide(side);
0270     job->setDetails(details);
0271     return job;
0272 }
0273 
0274 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 69)
0275 StatJob *KIO::stat(const QUrl &url, KIO::StatJob::StatSide side, short int details, JobFlags flags)
0276 {
0277     // qCDebug(KIO_CORE) << "stat" << url;
0278     KIO_ARGS << url;
0279     StatJob *job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags);
0280     job->setSide(side);
0281     job->setDetails(details);
0282     return job;
0283 }
0284 #endif
0285 
0286 #include "moc_statjob.cpp"