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 job->setUiDelegate(nullptr);
132 q->addSubjob(job);
133 QObject::connect(job, &KJob::result, q, [=]() {
134 const int errCode = job->error();
135 if (errCode) {
136 // ERR_NO_CONTENT is not an error, but an indication no further
137 // actions need to be taken.
138 if (errCode != KIO::ERR_NO_CONTENT) {
139 q->setError(errCode);
140 // We're a KJob, not a KIO::Job, so build the error string here
141 q->setErrorText(KIO::buildErrorString(errCode, job->errorText()));
142 }
143 q->emitResult();
144 return;
145 }
146 if (m_followRedirections) { // Update our URL in case of a redirection
147 m_url = job->url();
148 }
149
150 const KIO::UDSEntry entry = job->statResult();
151
152 qCDebug(KIO_CORE) << "UDSEntry from StatJob in MimeTypeFinderJob" << entry;
153
154 const QString localPath = entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH);
155 if (!localPath.isEmpty()) {
156 m_url = QUrl::fromLocalFile(localPath);
157 }
158
159 // MIME type already known? (e.g. print:/manager)
160 m_mimeTypeName = entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE);
161 if (!m_mimeTypeName.isEmpty()) {
162 q->emitResult();
163 return;
164 }
165
166 if (entry.isDir()) {
167 m_mimeTypeName = QStringLiteral("inode/directory");
168 q->emitResult();
169 } else { // It's a file
170 // Start the timer. Once we get the timer event this
171 // protocol server is back in the pool and we can reuse it.
172 // This gives better performance than starting a new worker
173 QTimer::singleShot(0, q, [this] {
174 scanFileWithGet();
175 });
176 }
177 });
178}
179
180static QMimeType fixupMimeType(const QString &mimeType, const QString &fileName)
181{
182 QMimeDatabase db;
183 QMimeType mime = db.mimeTypeForName(mimeType);
184 if ((!mime.isValid() || mime.isDefault()) && !fileName.isEmpty()) {
186 }
187 return mime;
188}
189
190void KIO::MimeTypeFinderJobPrivate::scanFileWithGet()
191{
192 Q_ASSERT(m_mimeTypeName.isEmpty());
193
195 qCDebug(KIO_CORE) << "No support for reading from" << m_url.scheme();
196 q->setError(KIO::ERR_CANNOT_READ);
197 // We're a KJob, not a KIO::Job, so build the error string here
198 q->setErrorText(KIO::buildErrorString(q->error(), m_url.toDisplayString()));
199 q->emitResult();
200 return;
201 }
202 // qDebug() << this << "Scanning file" << url;
203
204 KIO::TransferJob *job = KIO::get(m_url, KIO::NoReload /*reload*/, KIO::HideProgressInfo);
205 if (!m_authPrompts) {
206 job->addMetaData(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
207 }
208 job->setUiDelegate(nullptr);
209 q->addSubjob(job);
210 QObject::connect(job, &KJob::result, q, [=]() {
211 const int errCode = job->error();
212 if (errCode) {
213 // ERR_NO_CONTENT is not an error, but an indication no further
214 // actions need to be taken.
215 if (errCode != KIO::ERR_NO_CONTENT) {
216 q->setError(errCode);
217 q->setErrorText(job->errorString());
218 }
219 q->emitResult();
220 }
221 // if the job succeeded, we certainly hope it emitted mimeTypeFound()...
222 if (m_mimeTypeName.isEmpty()) {
223 qCWarning(KIO_CORE) << "KIO::get didn't emit a mimetype! Please fix the KIO worker for URL" << m_url;
224 q->setError(KIO::ERR_INTERNAL);
225 q->setErrorText(i18n("Unable to determine the type of file for %1", m_url.toDisplayString()));
226 q->emitResult();
227 }
228 });
230 if (m_followRedirections) { // Update our URL in case of a redirection
231 m_url = job->url();
232 }
233 if (mimetype.isEmpty()) {
234 qCWarning(KIO_CORE) << "get() didn't emit a MIME type! Probably a KIO worker bug, please check the implementation of" << m_url.scheme();
235 }
236 m_mimeTypeName = mimetype;
237
238 // If the current MIME type is the default MIME type, then attempt to
239 // determine the "real" MIME type from the file name (bug #279675)
240 const QMimeType mime = fixupMimeType(m_mimeTypeName, m_suggestedFileName.isEmpty() ? m_url.fileName() : m_suggestedFileName);
241 if (mime.isValid()) {
242 m_mimeTypeName = mime.name();
243 }
244
245 if (m_suggestedFileName.isEmpty()) {
246 m_suggestedFileName = job->queryMetaData(QStringLiteral("content-disposition-filename"));
247 }
248
249 q->emitResult();
250 });
251}
252
253#include "moc_mimetypefinderjob.cpp"
The base class for all jobs.
Definition job_base.h:45
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.
Definition statjob.h:26
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.
Definition transferjob.h:26
void mimeTypeFound(KIO::Job *job, const QString &mimeType)
MIME type determined.
Universal Directory Service.
Definition udsentry.h:78
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
void setUiDelegate(KJobUiDelegate *delegate)
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:251
@ StatResolveSymlink
Resolve symlinks.
Definition global.h:257
@ StatMimeType
MIME type.
Definition global.h:267
@ ERR_NO_CONTENT
Action succeeded but no content will follow.
Definition global.h:172
QMimeType mimeTypeForFile(const QString &fileName, QMimeDatabase::MatchMode mode) const const
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
bool isValid() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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 Sun Feb 25 2024 18:45:23 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.