File indexing completed on 2023-10-03 03:20:03
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 "listjob.h" 0010 #include "../utils_p.h" 0011 #include "job_p.h" 0012 #include "slave.h" 0013 #include <QTimer> 0014 #include <kurlauthorized.h> 0015 0016 #include <QDebug> 0017 0018 using namespace KIO; 0019 0020 class KIO::ListJobPrivate : public KIO::SimpleJobPrivate 0021 { 0022 public: 0023 ListJobPrivate(const QUrl &url, bool _recursive, const QString &prefix, const QString &displayPrefix, bool _includeHidden) 0024 : SimpleJobPrivate(url, CMD_LISTDIR, QByteArray()) 0025 , recursive(_recursive) 0026 , includeHidden(_includeHidden) 0027 , m_prefix(prefix) 0028 , m_displayPrefix(displayPrefix) 0029 , m_processedEntries(0) 0030 { 0031 } 0032 bool recursive; 0033 bool includeHidden; 0034 QString m_prefix; 0035 QString m_displayPrefix; 0036 unsigned long m_processedEntries; 0037 QUrl m_redirectionURL; 0038 0039 /** 0040 * @internal 0041 * Called by the scheduler when a @p slave gets to 0042 * work on this job. 0043 * @param slave the slave that starts working on this job 0044 */ 0045 void start(Slave *slave) override; 0046 0047 void slotListEntries(const KIO::UDSEntryList &list); 0048 void slotRedirection(const QUrl &url); 0049 void gotEntries(KIO::Job *subjob, const KIO::UDSEntryList &list); 0050 void slotSubError(ListJob *job, ListJob *subJob); 0051 0052 Q_DECLARE_PUBLIC(ListJob) 0053 0054 static inline ListJob * 0055 newJob(const QUrl &u, bool _recursive, const QString &prefix, const QString &displayPrefix, bool _includeHidden, JobFlags flags = HideProgressInfo) 0056 { 0057 ListJob *job = new ListJob(*new ListJobPrivate(u, _recursive, prefix, displayPrefix, _includeHidden)); 0058 job->setUiDelegate(KIO::createDefaultJobUiDelegate()); 0059 if (!(flags & HideProgressInfo)) { 0060 KIO::getJobTracker()->registerJob(job); 0061 } 0062 return job; 0063 } 0064 static inline ListJob *newJobNoUi(const QUrl &u, bool _recursive, const QString &prefix, const QString &displayPrefix, bool _includeHidden) 0065 { 0066 return new ListJob(*new ListJobPrivate(u, _recursive, prefix, displayPrefix, _includeHidden)); 0067 } 0068 }; 0069 0070 ListJob::ListJob(ListJobPrivate &dd) 0071 : SimpleJob(dd) 0072 { 0073 Q_D(ListJob); 0074 // We couldn't set the args when calling the parent constructor, 0075 // so do it now. 0076 QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly); 0077 stream << d->m_url; 0078 } 0079 0080 ListJob::~ListJob() 0081 { 0082 } 0083 0084 void ListJobPrivate::slotListEntries(const KIO::UDSEntryList &list) 0085 { 0086 Q_Q(ListJob); 0087 // Emit progress info (takes care of emit processedSize and percent) 0088 m_processedEntries += list.count(); 0089 slotProcessedSize(m_processedEntries); 0090 0091 if (recursive) { 0092 UDSEntryList::ConstIterator it = list.begin(); 0093 const UDSEntryList::ConstIterator end = list.end(); 0094 0095 for (; it != end; ++it) { 0096 const UDSEntry &entry = *it; 0097 0098 QUrl itemURL; 0099 const QString udsUrl = entry.stringValue(KIO::UDSEntry::UDS_URL); 0100 QString filename; 0101 if (!udsUrl.isEmpty()) { 0102 itemURL = QUrl(udsUrl); 0103 filename = itemURL.fileName(); 0104 } else { // no URL, use the name 0105 itemURL = q->url(); 0106 filename = entry.stringValue(KIO::UDSEntry::UDS_NAME); 0107 Q_ASSERT(!filename.isEmpty()); // we'll recurse forever otherwise :) 0108 itemURL.setPath(Utils::concatPaths(itemURL.path(), filename)); 0109 } 0110 0111 if (entry.isDir() && !entry.isLink()) { 0112 Q_ASSERT(!filename.isEmpty()); 0113 QString displayName = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); 0114 if (displayName.isEmpty()) { 0115 displayName = filename; 0116 } 0117 // skip hidden dirs when listing if requested 0118 if (filename != QLatin1String("..") && filename != QLatin1String(".") && (includeHidden || filename[0] != QLatin1Char('.'))) { 0119 ListJob *job = ListJobPrivate::newJobNoUi(itemURL, 0120 true /*recursive*/, 0121 m_prefix + filename + QLatin1Char('/'), 0122 m_displayPrefix + displayName + QLatin1Char('/'), 0123 includeHidden); 0124 QObject::connect(job, &ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &list) { 0125 gotEntries(job, list); 0126 }); 0127 QObject::connect(job, &ListJob::subError, q, [this](KIO::ListJob *job, KIO::ListJob *ljob) { 0128 slotSubError(job, ljob); 0129 }); 0130 0131 q->addSubjob(job); 0132 } 0133 } 0134 } 0135 } 0136 0137 // Not recursive, or top-level of recursive listing : return now (send . and .. as well) 0138 // exclusion of hidden files also requires the full sweep, but the case for full-listing 0139 // a single dir is probably common enough to justify the shortcut 0140 if (m_prefix.isNull() && includeHidden) { 0141 Q_EMIT q->entries(q, list); 0142 } else { 0143 UDSEntryList newlist = list; 0144 0145 auto removeFunc = [this](const UDSEntry &entry) { 0146 const QString filename = entry.stringValue(KIO::UDSEntry::UDS_NAME); 0147 // Avoid returning entries like subdir/. and subdir/.., but include . and .. for 0148 // the toplevel dir, and skip hidden files/dirs if that was requested 0149 const bool shouldEmit = (m_prefix.isNull() || (filename != QLatin1String("..") && filename != QLatin1String("."))) 0150 && (includeHidden || (filename[0] != QLatin1Char('.'))); 0151 return !shouldEmit; 0152 }; 0153 newlist.erase(std::remove_if(newlist.begin(), newlist.end(), removeFunc), newlist.end()); 0154 0155 for (UDSEntry &newone : newlist) { 0156 // Modify the name in the UDSEntry 0157 const QString filename = newone.stringValue(KIO::UDSEntry::UDS_NAME); 0158 QString displayName = newone.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME); 0159 if (displayName.isEmpty()) { 0160 displayName = filename; 0161 } 0162 0163 // ## Didn't find a way to use the iterator instead of re-doing a key lookup 0164 newone.replace(KIO::UDSEntry::UDS_NAME, m_prefix + filename); 0165 newone.replace(KIO::UDSEntry::UDS_DISPLAY_NAME, m_displayPrefix + displayName); 0166 } 0167 0168 Q_EMIT q->entries(q, newlist); 0169 } 0170 } 0171 0172 void ListJobPrivate::gotEntries(KIO::Job *, const KIO::UDSEntryList &list) 0173 { 0174 // Forward entries received by subjob - faking we received them ourselves 0175 Q_Q(ListJob); 0176 Q_EMIT q->entries(q, list); 0177 } 0178 0179 void ListJobPrivate::slotSubError(KIO::ListJob * /*job*/, KIO::ListJob *subJob) 0180 { 0181 Q_Q(ListJob); 0182 Q_EMIT q->subError(q, subJob); // Let the signal of subError go up 0183 } 0184 0185 void ListJob::slotResult(KJob *job) 0186 { 0187 Q_D(ListJob); 0188 if (job->error()) { 0189 // If we can't list a subdir, the result is still ok 0190 // This is why we override KCompositeJob::slotResult - to not set 0191 // an error on parent job. 0192 // Let's emit a signal about this though 0193 Q_EMIT subError(this, static_cast<KIO::ListJob *>(job)); 0194 } 0195 removeSubjob(job); 0196 if (!hasSubjobs() && !d->m_slave) { // if the main directory listing is still running, it will emit result in SimpleJob::slotFinished() 0197 emitResult(); 0198 } 0199 } 0200 0201 void ListJobPrivate::slotRedirection(const QUrl &url) 0202 { 0203 Q_Q(ListJob); 0204 if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("redirect"), m_url, url)) { 0205 qCWarning(KIO_CORE) << "Redirection from" << m_url << "to" << url << "REJECTED!"; 0206 return; 0207 } 0208 m_redirectionURL = url; // We'll remember that when the job finishes 0209 Q_EMIT q->redirection(q, m_redirectionURL); 0210 } 0211 0212 void ListJob::slotFinished() 0213 { 0214 Q_D(ListJob); 0215 0216 if (!d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error()) { 0217 // qDebug() << "Redirection to " << d->m_redirectionURL; 0218 if (queryMetaData(QStringLiteral("permanent-redirect")) == QLatin1String("true")) { 0219 Q_EMIT permanentRedirection(this, d->m_url, d->m_redirectionURL); 0220 } 0221 0222 if (d->m_redirectionHandlingEnabled) { 0223 d->m_packedArgs.truncate(0); 0224 QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly); 0225 stream << d->m_redirectionURL; 0226 0227 d->restartAfterRedirection(&d->m_redirectionURL); 0228 return; 0229 } 0230 } 0231 0232 // Return slave to the scheduler 0233 SimpleJob::slotFinished(); 0234 } 0235 0236 #if KIOCORE_BUILD_DEPRECATED_SINCE(5, 101) 0237 void ListJob::slotMetaData(const KIO::MetaData &_metaData) 0238 { 0239 SimpleJob::slotMetaData(_metaData); 0240 } 0241 #endif 0242 0243 ListJob *KIO::listDir(const QUrl &url, JobFlags flags, bool includeHidden) 0244 { 0245 return ListJobPrivate::newJob(url, false, QString(), QString(), includeHidden, flags); 0246 } 0247 0248 ListJob *KIO::listRecursive(const QUrl &url, JobFlags flags, bool includeHidden) 0249 { 0250 return ListJobPrivate::newJob(url, true, QString(), QString(), includeHidden, flags); 0251 } 0252 0253 void ListJob::setUnrestricted(bool unrestricted) 0254 { 0255 Q_D(ListJob); 0256 if (unrestricted) { 0257 d->m_extraFlags |= JobPrivate::EF_ListJobUnrestricted; 0258 } else { 0259 d->m_extraFlags &= ~JobPrivate::EF_ListJobUnrestricted; 0260 } 0261 } 0262 0263 void ListJobPrivate::start(Slave *slave) 0264 { 0265 Q_Q(ListJob); 0266 if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("list"), m_url, m_url) && !(m_extraFlags & EF_ListJobUnrestricted)) { 0267 q->setError(ERR_ACCESS_DENIED); 0268 q->setErrorText(m_url.toDisplayString()); 0269 QTimer::singleShot(0, q, &ListJob::slotFinished); 0270 return; 0271 } 0272 QObject::connect(slave, &Slave::listEntries, q, [this](const KIO::UDSEntryList &list) { 0273 slotListEntries(list); 0274 }); 0275 0276 QObject::connect(slave, &Slave::totalSize, q, [this](KIO::filesize_t size) { 0277 slotTotalSize(size); 0278 }); 0279 0280 QObject::connect(slave, &Slave::redirection, q, [this](const QUrl &url) { 0281 slotRedirection(url); 0282 }); 0283 0284 SimpleJobPrivate::start(slave); 0285 } 0286 0287 const QUrl &ListJob::redirectionUrl() const 0288 { 0289 return d_func()->m_redirectionURL; 0290 } 0291 0292 #include "moc_listjob.cpp"