KIO

mimetypefinderjob.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "mimetypefinderjob.h"
9
10#include "global.h"
11#include "job.h" // for buildErrorString
12#include "kiocoredebug.h"
13#include "statjob.h"
14#include "transferjob.h"
15
16#include <KLocalizedString>
17#include <KProtocolManager>
18
19#include <QMimeDatabase>
20#include <QTimer>
21#include <QUrl>
22
23class KIO::MimeTypeFinderJobPrivate
24{
25public:
26 explicit MimeTypeFinderJobPrivate(const QUrl &url, MimeTypeFinderJob *qq)
27 : m_url(url)
28 , q(qq)
29 {
31 }
32
33 void statFile();
34 void scanFileWithGet();
35
36 QUrl m_url;
38 QString m_mimeTypeName;
39 QString m_suggestedFileName;
40 bool m_followRedirections = true;
41 bool m_authPrompts = true;
42};
43
45 : KCompositeJob(parent)
46 , d(new MimeTypeFinderJobPrivate(url, this))
47{
48}
49
51
53{
54 if (!d->m_url.isValid() || d->m_url.scheme().isEmpty()) {
55 const QString error = !d->m_url.isValid() ? d->m_url.errorString() : d->m_url.toDisplayString();
56 setError(KIO::ERR_MALFORMED_URL);
57 setErrorText(i18n("Malformed URL\n%1", error));
58 emitResult();
59 return;
60 }
61
62 if (!KProtocolManager::supportsListing(d->m_url)) {
63 // No support for listing => it can't be a directory (example: http)
64 d->scanFileWithGet();
65 return;
66 }
67
68 // It may be a directory or a file, let's use stat to find out
69 d->statFile();
70}
71
73{
74 d->m_followRedirections = b;
75}
76
78{
79 d->m_suggestedFileName = suggestedFileName;
80}
81
83{
84 return d->m_suggestedFileName;
85}
86
88{
89 return d->m_mimeTypeName;
90}
91
93{
94 d->m_authPrompts = enable;
95}
96
98{
99 return d->m_authPrompts;
100}
101
102bool KIO::MimeTypeFinderJob::doKill()
103{
104 // This should really be in KCompositeJob...
105 const QList<KJob *> jobs = subjobs();
106 for (KJob *job : jobs) {
107 job->kill(); // ret val ignored, see below
108 }
109 // Even if for some reason killing a subjob fails,
110 // we can still consider this job as killed.
111 // The stat() or get() subjob has no side effects.
112 return true;
113}
114
115void KIO::MimeTypeFinderJob::slotResult(KJob *job)
116{
117 // We do the error handling elsewhere, just do the bookkeeping here
118 removeSubjob(job);
119}
120
121void KIO::MimeTypeFinderJobPrivate::statFile()
122{
123 Q_ASSERT(m_mimeTypeName.isEmpty());
124
125 constexpr auto statFlags = KIO::StatBasic | KIO::StatResolveSymlink | KIO::StatMimeType;
126
127 KIO::StatJob *job = KIO::stat(m_url, KIO::StatJob::SourceSide, statFlags, KIO::HideProgressInfo);
128 if (!m_authPrompts) {
129 job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
130 }
131 q->addSubjob(job);
132 QObject::connect(job, &KJob::result, q, [=, this]() {
133 const int errCode = job->error();
134 if (errCode) {
135 // ERR_NO_CONTENT is not an error, but an indication no further
136 // actions need to be taken.
137 if (errCode != KIO::ERR_NO_CONTENT) {
138 q->setError(errCode);
139 // We're a KJob, not a KIO::Job, so build the error string here
140 q->setErrorText(KIO::buildErrorString(errCode, job->errorText()));
141 }
142 q->emitResult();
143 return;
144 }
145 if (m_followRedirections) { // Update our URL in case of a redirection
146 m_url = job->url();
147 }
148
149 const KIO::UDSEntry entry = job->statResult();
150
151 qCDebug(KIO_CORE) << "UDSEntry from StatJob in MimeTypeFinderJob" << entry;
152
153 const QString localPath = entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH);
154 if (!localPath.isEmpty()) {
155 m_url = QUrl::fromLocalFile(localPath);
156 }
157
158 // MIME type already known? (e.g. print:/manager)
159 m_mimeTypeName = entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE);
160 if (!m_mimeTypeName.isEmpty()) {
161 q->emitResult();
162 return;
163 }
164
165 if (entry.isDir()) {
166 m_mimeTypeName = QStringLiteral("inode/directory");
167 q->emitResult();
168 } else { // It's a file
169 // Start the timer. Once we get the timer event this
170 // protocol server is back in the pool and we can reuse it.
171 // This gives better performance than starting a new worker
172 QTimer::singleShot(0, q, [this] {
173 scanFileWithGet();
174 });
175 }
176 });
177}
178
179static QMimeType fixupMimeType(const QString &mimeType, const QString &fileName)
180{
181 QMimeDatabase db;
182 QMimeType mime = db.mimeTypeForName(mimeType);
183 if ((!mime.isValid() || mime.isDefault()) && !fileName.isEmpty()) {
185 }
186 return mime;
187}
188
189void KIO::MimeTypeFinderJobPrivate::scanFileWithGet()
190{
191 Q_ASSERT(m_mimeTypeName.isEmpty());
192
194 qCDebug(KIO_CORE) << "No support for reading from" << m_url.scheme();
195 q->setError(KIO::ERR_CANNOT_READ);
196 // We're a KJob, not a KIO::Job, so build the error string here
197 q->setErrorText(KIO::buildErrorString(q->error(), m_url.toDisplayString()));
198 q->emitResult();
199 return;
200 }
201 // qDebug() << this << "Scanning file" << url;
202
203 KIO::TransferJob *job = KIO::get(m_url, KIO::NoReload /*reload*/, KIO::HideProgressInfo);
204 if (!m_authPrompts) {
205 job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
206 }
207 q->addSubjob(job);
208 QObject::connect(job, &KJob::result, q, [=, this]() {
209 const int errCode = job->error();
210 if (errCode) {
211 // ERR_NO_CONTENT is not an error, but an indication no further
212 // actions need to be taken.
213 if (errCode != KIO::ERR_NO_CONTENT) {
214 q->setError(errCode);
215 q->setErrorText(job->errorString());
216 }
217 q->emitResult();
218 }
219 // if the job succeeded, we certainly hope it emitted mimeTypeFound()...
220 if (m_mimeTypeName.isEmpty()) {
221 qCWarning(KIO_CORE) << "KIO::get didn't emit a mimetype! Please fix the KIO worker for URL" << m_url;
222 q->setError(KIO::ERR_INTERNAL);
223 q->setErrorText(i18n("Unable to determine the type of file for %1", m_url.toDisplayString()));
224 q->emitResult();
225 }
226 });
228 if (m_followRedirections) { // Update our URL in case of a redirection
229 m_url = job->url();
230 }
231 if (mimetype.isEmpty()) {
232 qCWarning(KIO_CORE) << "get() didn't emit a MIME type! Probably a KIO worker bug, please check the implementation of" << m_url.scheme();
233 }
234 m_mimeTypeName = mimetype;
235
236 // If the current MIME type is the default MIME type, then attempt to
237 // determine the "real" MIME type from the file name (bug #279675)
238 const QMimeType mime = fixupMimeType(m_mimeTypeName, m_suggestedFileName.isEmpty() ? m_url.fileName() : m_suggestedFileName);
239 if (mime.isValid()) {
240 m_mimeTypeName = mime.name();
241 }
242
243 if (m_suggestedFileName.isEmpty()) {
244 m_suggestedFileName = job->queryMetaData(QStringLiteral("content-disposition-filename"));
245 }
246
247 q->emitResult();
248 });
249}
250
251#include "moc_mimetypefinderjob.cpp"
The base class for all jobs.
QString errorString() const override
Converts an error code and a non-i18n error message into an error message in the current language.
Definition job_error.cpp:26
QString queryMetaData(const QString &key)
Query meta data received from the worker.
Definition job.cpp:210
void addMetaData(const QString &key, const QString &value)
Add key/value pair to the meta data that is sent to the worker.
Definition job.cpp:221
MimeTypeFinderJob finds out the MIME type of a URL.
QString suggestedFileName() const
Returns the suggested filename, either set by setSuggestedFileName or returned by the KIO::get job.
void setSuggestedFileName(const QString &suggestedFileName)
Sets the file name to use in the case of downloading the file to a tempfile, in order to give it to a...
~MimeTypeFinderJob() override
Destructor.
MimeTypeFinderJob(const QUrl &url, QObject *parent=nullptr)
Creates an MimeTypeFinderJob for a URL.
void start() override
Starts the job.
bool isAuthenticationPromptEnabled() const
Returns where authentication prompts are enabled or disabled.
void setAuthenticationPromptEnabled(bool enable)
Enable/disable authentication prompt, if the URL requires one.
void setFollowRedirections(bool b)
Sets whether the job should follow URL redirections.
const QUrl & url() const
Returns the SimpleJob's URL.
Definition simplejob.cpp:70
A KIO job that retrieves information about a file or directory.
const UDSEntry & statResult() const
Result of the stat operation.
Definition statjob.cpp:80
The transfer job pumps data into and/or out of a KIO worker.
void mimeTypeFound(KIO::Job *job, const QString &mimeType)
MIME type determined.
Universal Directory Service.
QString stringValue(uint field) const
Definition udsentry.cpp:365
@ UDS_MIME_TYPE
A MIME type; the KIO worker should set it if it's known.
Definition udsentry.h:253
@ UDS_LOCAL_PATH
A local file path if the KIO worker display files sitting on the local filesystem (but in another hie...
Definition udsentry.h:227
bool isDir() const
Definition udsentry.cpp:375
int error() const
void result(KJob *job)
void setCapabilities(Capabilities capabilities)
QString errorText() const
static bool supportsReading(const QUrl &url)
Returns whether the protocol can retrieve data from URLs.
static bool supportsListing(const QUrl &url)
Returns whether the protocol can list files/objects.
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT StatJob * stat(const QUrl &url, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
Definition statjob.cpp:203
KIOCORE_EXPORT QString buildErrorString(int errorCode, const QString &errorText)
Returns a translated error message for errorCode using the additional error information provided by e...
Definition job_error.cpp:31
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
Get (means: read).
KIOCORE_EXPORT MimetypeJob * mimetype(const QUrl &url, JobFlags flags=DefaultFlags)
Find MIME type for one file or directory.
@ HideProgressInfo
Hide progress information dialog, i.e. don't show a GUI.
Definition job_base.h:251
@ StatBasic
Filename, access, type, size, linkdest.
Definition global.h:255
@ StatResolveSymlink
Resolve symlinks.
Definition global.h:261
@ StatMimeType
MIME type.
Definition global.h:271
@ ERR_NO_CONTENT
Action succeeded but no content will follow.
Definition global.h:172
QMimeType mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const const
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
bool isValid() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool isEmpty() const const
QUrl fromLocalFile(const QString &localFile)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:58:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.