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