File indexing completed on 2023-09-24 04:08:40
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "mimetypefinderjob.h" 0009 0010 #include "global.h" 0011 #include "job.h" // for buildErrorString 0012 #include "kiocoredebug.h" 0013 #include "statjob.h" 0014 0015 #include <KLocalizedString> 0016 #include <KProtocolManager> 0017 0018 #include <QMimeDatabase> 0019 #include <QTimer> 0020 #include <QUrl> 0021 0022 class KIO::MimeTypeFinderJobPrivate 0023 { 0024 public: 0025 explicit MimeTypeFinderJobPrivate(const QUrl &url, MimeTypeFinderJob *qq) 0026 : m_url(url) 0027 , q(qq) 0028 { 0029 q->setCapabilities(KJob::Killable); 0030 } 0031 0032 void statFile(); 0033 void scanFileWithGet(); 0034 0035 QUrl m_url; 0036 KIO::MimeTypeFinderJob *const q; 0037 QString m_mimeTypeName; 0038 QString m_suggestedFileName; 0039 bool m_followRedirections = true; 0040 bool m_authPrompts = true; 0041 }; 0042 0043 KIO::MimeTypeFinderJob::MimeTypeFinderJob(const QUrl &url, QObject *parent) 0044 : KCompositeJob(parent) 0045 , d(new MimeTypeFinderJobPrivate(url, this)) 0046 { 0047 } 0048 0049 KIO::MimeTypeFinderJob::~MimeTypeFinderJob() = default; 0050 0051 void KIO::MimeTypeFinderJob::start() 0052 { 0053 if (!d->m_url.isValid() || d->m_url.scheme().isEmpty()) { 0054 const QString error = !d->m_url.isValid() ? d->m_url.errorString() : d->m_url.toDisplayString(); 0055 setError(KIO::ERR_MALFORMED_URL); 0056 setErrorText(i18n("Malformed URL\n%1", error)); 0057 emitResult(); 0058 return; 0059 } 0060 0061 if (!KProtocolManager::supportsListing(d->m_url)) { 0062 // No support for listing => it can't be a directory (example: http) 0063 d->scanFileWithGet(); 0064 return; 0065 } 0066 0067 // It may be a directory or a file, let's use stat to find out 0068 d->statFile(); 0069 } 0070 0071 void KIO::MimeTypeFinderJob::setFollowRedirections(bool b) 0072 { 0073 d->m_followRedirections = b; 0074 } 0075 0076 void KIO::MimeTypeFinderJob::setSuggestedFileName(const QString &suggestedFileName) 0077 { 0078 d->m_suggestedFileName = suggestedFileName; 0079 } 0080 0081 QString KIO::MimeTypeFinderJob::suggestedFileName() const 0082 { 0083 return d->m_suggestedFileName; 0084 } 0085 0086 QString KIO::MimeTypeFinderJob::mimeType() const 0087 { 0088 return d->m_mimeTypeName; 0089 } 0090 0091 void KIO::MimeTypeFinderJob::setAuthenticationPromptEnabled(bool enable) 0092 { 0093 d->m_authPrompts = enable; 0094 } 0095 0096 bool KIO::MimeTypeFinderJob::isAuthenticationPromptEnabled() const 0097 { 0098 return d->m_authPrompts; 0099 } 0100 0101 bool KIO::MimeTypeFinderJob::doKill() 0102 { 0103 // This should really be in KCompositeJob... 0104 const QList<KJob *> jobs = subjobs(); 0105 for (KJob *job : jobs) { 0106 job->kill(); // ret val ignored, see below 0107 } 0108 // Even if for some reason killing a subjob fails, 0109 // we can still consider this job as killed. 0110 // The stat() or get() subjob has no side effects. 0111 return true; 0112 } 0113 0114 void KIO::MimeTypeFinderJob::slotResult(KJob *job) 0115 { 0116 // We do the error handling elsewhere, just do the bookkeeping here 0117 removeSubjob(job); 0118 } 0119 0120 void KIO::MimeTypeFinderJobPrivate::statFile() 0121 { 0122 Q_ASSERT(m_mimeTypeName.isEmpty()); 0123 0124 constexpr auto statFlags = KIO::StatBasic | KIO::StatResolveSymlink | KIO::StatMimeType; 0125 0126 KIO::StatJob *job = KIO::statDetails(m_url, KIO::StatJob::SourceSide, statFlags, KIO::HideProgressInfo); 0127 if (!m_authPrompts) { 0128 job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); 0129 } 0130 job->setUiDelegate(nullptr); 0131 q->addSubjob(job); 0132 QObject::connect(job, &KJob::result, q, [=]() { 0133 const int errCode = job->error(); 0134 if (errCode) { 0135 // ERR_NO_CONTENT is not an error, but an indication no further 0136 // actions need to be taken. 0137 if (errCode != KIO::ERR_NO_CONTENT) { 0138 q->setError(errCode); 0139 // We're a KJob, not a KIO::Job, so build the error string here 0140 q->setErrorText(KIO::buildErrorString(errCode, job->errorText())); 0141 } 0142 q->emitResult(); 0143 return; 0144 } 0145 if (m_followRedirections) { // Update our URL in case of a redirection 0146 m_url = job->url(); 0147 } 0148 0149 const KIO::UDSEntry entry = job->statResult(); 0150 0151 qCDebug(KIO_CORE) << "UDSEntry from StatJob in MimeTypeFinderJob" << entry; 0152 0153 const QString localPath = entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH); 0154 if (!localPath.isEmpty()) { 0155 m_url = QUrl::fromLocalFile(localPath); 0156 } 0157 0158 // MIME type already known? (e.g. print:/manager) 0159 m_mimeTypeName = entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE); 0160 if (!m_mimeTypeName.isEmpty()) { 0161 q->emitResult(); 0162 return; 0163 } 0164 0165 if (entry.isDir()) { 0166 m_mimeTypeName = QStringLiteral("inode/directory"); 0167 q->emitResult(); 0168 } else { // It's a file 0169 // Start the timer. Once we get the timer event this 0170 // protocol server is back in the pool and we can reuse it. 0171 // This gives better performance than starting a new worker 0172 QTimer::singleShot(0, q, [this] { 0173 scanFileWithGet(); 0174 }); 0175 } 0176 }); 0177 } 0178 0179 static QMimeType fixupMimeType(const QString &mimeType, const QString &fileName) 0180 { 0181 QMimeDatabase db; 0182 QMimeType mime = db.mimeTypeForName(mimeType); 0183 if ((!mime.isValid() || mime.isDefault()) && !fileName.isEmpty()) { 0184 mime = db.mimeTypeForFile(fileName, QMimeDatabase::MatchExtension); 0185 } 0186 return mime; 0187 } 0188 0189 void KIO::MimeTypeFinderJobPrivate::scanFileWithGet() 0190 { 0191 Q_ASSERT(m_mimeTypeName.isEmpty()); 0192 0193 if (!KProtocolManager::supportsReading(m_url)) { 0194 qCDebug(KIO_CORE) << "No support for reading from" << m_url.scheme(); 0195 q->setError(KIO::ERR_CANNOT_READ); 0196 // We're a KJob, not a KIO::Job, so build the error string here 0197 q->setErrorText(KIO::buildErrorString(q->error(), m_url.toDisplayString())); 0198 q->emitResult(); 0199 return; 0200 } 0201 // qDebug() << this << "Scanning file" << url; 0202 0203 KIO::TransferJob *job = KIO::get(m_url, KIO::NoReload /*reload*/, KIO::HideProgressInfo); 0204 if (!m_authPrompts) { 0205 job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true")); 0206 } 0207 job->setUiDelegate(nullptr); 0208 q->addSubjob(job); 0209 QObject::connect(job, &KJob::result, q, [=]() { 0210 const int errCode = job->error(); 0211 if (errCode) { 0212 // ERR_NO_CONTENT is not an error, but an indication no further 0213 // actions need to be taken. 0214 if (errCode != KIO::ERR_NO_CONTENT) { 0215 q->setError(errCode); 0216 q->setErrorText(job->errorString()); 0217 } 0218 q->emitResult(); 0219 } 0220 // if the job succeeded, we certainly hope it emitted mimeTypeFound()... 0221 if (m_mimeTypeName.isEmpty()) { 0222 qCWarning(KIO_CORE) << "KIO::get didn't emit a mimetype! Please fix the KIO worker for URL" << m_url; 0223 q->setError(KIO::ERR_INTERNAL); 0224 q->setErrorText(i18n("Unable to determine the type of file for %1", m_url.toDisplayString())); 0225 q->emitResult(); 0226 } 0227 }); 0228 QObject::connect(job, &KIO::TransferJob::mimeTypeFound, q, [=](KIO::Job *, const QString &mimetype) { 0229 if (m_followRedirections) { // Update our URL in case of a redirection 0230 m_url = job->url(); 0231 } 0232 if (mimetype.isEmpty()) { 0233 qCWarning(KIO_CORE) << "get() didn't emit a MIME type! Probably a KIO worker bug, please check the implementation of" << m_url.scheme(); 0234 } 0235 m_mimeTypeName = mimetype; 0236 0237 // If the current MIME type is the default MIME type, then attempt to 0238 // determine the "real" MIME type from the file name (bug #279675) 0239 const QMimeType mime = fixupMimeType(m_mimeTypeName, m_suggestedFileName.isEmpty() ? m_url.fileName() : m_suggestedFileName); 0240 if (mime.isValid()) { 0241 m_mimeTypeName = mime.name(); 0242 } 0243 0244 if (m_suggestedFileName.isEmpty()) { 0245 m_suggestedFileName = job->queryMetaData(QStringLiteral("content-disposition-filename")); 0246 } 0247 0248 q->emitResult(); 0249 }); 0250 } 0251 0252 #include "moc_mimetypefinderjob.cpp"