1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
4 SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "readonlypart.h"
10#include "readonlypart_p.h"
11
12#include "kparts_logging.h"
13
14#include "guiactivateevent.h"
15#include "navigationextension.h"
16
17#include <KIO/FileCopyJob>
18#include <KIO/StatJob>
19#include <KJobWidgets>
20#include <KProtocolInfo>
21
22#include <QDir>
23#include <QFileInfo>
24#include <QMimeDatabase>
25#include <QTemporaryFile>
26
27using namespace KParts;
28
29ReadOnlyPart::ReadOnlyPart(QObject *parent, const KPluginMetaData &data)
30 : Part(*new ReadOnlyPartPrivate(this, data), parent)
31{
32}
33
34ReadOnlyPart::ReadOnlyPart(ReadOnlyPartPrivate &dd, QObject *parent)
35 : Part(dd, parent)
36{
37}
38
39ReadOnlyPart::~ReadOnlyPart()
40{
41 Q_D(ReadOnlyPart);
42 d->m_closeUrlFromDestructor = true;
43 ReadOnlyPart::closeUrl();
44}
45
46QUrl ReadOnlyPart::url() const
47{
48 Q_D(const ReadOnlyPart);
49
50 return d->m_url;
51}
52
53void ReadOnlyPart::setUrl(const QUrl &url)
54{
55 Q_D(ReadOnlyPart);
56
57 if (d->m_url != url) {
58 d->m_url = url;
59 if (!d->m_closeUrlFromDestructor) {
60 Q_EMIT urlChanged(url);
61 }
62 }
63}
64
65QString ReadOnlyPart::localFilePath() const
66{
67 Q_D(const ReadOnlyPart);
68
69 return d->m_file;
70}
71
72void ReadOnlyPart::setLocalFilePath(const QString &localFilePath)
73{
74 Q_D(ReadOnlyPart);
75
76 d->m_file = localFilePath;
77}
78
79void ReadOnlyPart::setProgressInfoEnabled(bool show)
80{
81 Q_D(ReadOnlyPart);
82
83 d->m_showProgressInfo = show;
84}
85
86bool ReadOnlyPart::isProgressInfoEnabled() const
87{
88 Q_D(const ReadOnlyPart);
89
90 return d->m_showProgressInfo;
91}
92
93bool ReadOnlyPart::openUrl(const QUrl &url)
94{
95 Q_D(ReadOnlyPart);
96
97 if (!url.isValid()) {
98 return false;
99 }
100 if (d->m_bAutoDetectedMime) {
101 d->m_arguments.setMimeType(QString());
102 d->m_bAutoDetectedMime = false;
103 }
104 OpenUrlArguments args = d->m_arguments;
105 d->m_closeUrlFromOpenUrl = true;
106 const bool closed = closeUrl();
107 d->m_closeUrlFromOpenUrl = false;
108 if (!closed) {
109 return false;
110 }
111 d->m_arguments = args;
112 setUrl(url);
113
114 d->m_file.clear();
115
116 if (d->m_url.isLocalFile()) {
117 d->m_file = d->m_url.toLocalFile();
118 return d->openLocalFile();
119 } else if (KProtocolInfo::protocolClass(protocol: url.scheme()) == QLatin1String(":local")) {
120 // Maybe we can use a "local path", to avoid a temp copy?
121 KIO::JobFlags flags = d->m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
122 d->m_statJob = KIO::mostLocalUrl(url: d->m_url, flags);
123 KJobWidgets::setWindow(job: d->m_statJob, widget: widget());
124 connect(sender: d->m_statJob, signal: &KJob::result, context: this, slot: [d](KJob *job) {
125 d->slotStatJobFinished(job);
126 });
127 return true;
128 } else {
129 d->openRemoteFile();
130 return true;
131 }
132}
133
134bool ReadOnlyPart::openFile()
135{
136 qCWarning(KPARTSLOG) << "Default implementation of ReadOnlyPart::openFile called!" << metaObject()->className()
137 << "should reimplement either openUrl or openFile.";
138 return false;
139}
140
141bool ReadOnlyPartPrivate::openLocalFile()
142{
143 Q_Q(ReadOnlyPart);
144 Q_EMIT q->started(job: nullptr);
145 m_bTemp = false;
146 // set the mimetype only if it was not already set (for example, by the host application)
147 if (m_arguments.mimeType().isEmpty()) {
148 // get the mimetype of the file
149 // using findByUrl() to avoid another string -> url conversion
150 QMimeDatabase db;
151 QMimeType mime = db.mimeTypeForUrl(url: m_url);
152 if (!mime.isDefault()) {
153 m_arguments.setMimeType(mime.name());
154 m_bAutoDetectedMime = true;
155 }
156 }
157 const bool ret = q->openFile();
158 if (ret) {
159 Q_EMIT q->setWindowCaption(m_url.toDisplayString());
160 Q_EMIT q->completed();
161 } else {
162 Q_EMIT q->canceled(errMsg: QString());
163 }
164 return ret;
165}
166
167void ReadOnlyPartPrivate::openRemoteFile()
168{
169 Q_Q(ReadOnlyPart);
170 m_bTemp = true;
171 // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice)
172 QString fileName = m_url.fileName();
173 QFileInfo fileInfo(fileName);
174 QString ext = fileInfo.completeSuffix();
175 QString extension;
176 if (!ext.isEmpty() && !m_url.hasQuery()) { // not if the URL has a query, e.g. cgi.pl?something
177 extension = QLatin1Char('.') + ext; // keep the '.'
178 }
179 QTemporaryFile tempFile(QDir::tempPath() + QLatin1Char('/') + m_metaData.pluginId() + QLatin1String("XXXXXX") + extension);
180 tempFile.setAutoRemove(false);
181 tempFile.open();
182 m_file = tempFile.fileName();
183
184 QUrl destURL = QUrl::fromLocalFile(localfile: m_file);
185 KIO::JobFlags flags = m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
186 flags |= KIO::Overwrite;
187 m_job = KIO::file_copy(src: m_url, dest: destURL, permissions: 0600, flags);
188 m_job->setFinishedNotificationHidden(true);
189 KJobWidgets::setWindow(job: m_job, widget: q->widget());
190 Q_EMIT q->started(job: m_job);
191
192 QObject::connect(sender: m_job, signal: &KJob::result, context: q, slot: [this](KJob *job) {
193 slotJobFinished(job);
194 });
195 QObject::connect(sender: m_job, signal: &KIO::FileCopyJob::mimeTypeFound, context: q, slot: [this](KIO::Job *job, const QString &mimeType) {
196 slotGotMimeType(job, mime: mimeType);
197 });
198}
199
200void ReadOnlyPart::abortLoad()
201{
202 Q_D(ReadOnlyPart);
203
204 if (d->m_statJob) {
205 // qDebug() << "Aborting job" << d->m_statJob;
206 d->m_statJob->kill();
207 d->m_statJob = nullptr;
208 }
209 if (d->m_job) {
210 // qDebug() << "Aborting job" << d->m_job;
211 d->m_job->kill();
212 d->m_job = nullptr;
213 }
214}
215
216bool ReadOnlyPart::closeUrl()
217{
218 Q_D(ReadOnlyPart);
219
220 abortLoad(); // just in case
221
222 d->m_arguments = KParts::OpenUrlArguments();
223 if (!d->m_closeUrlFromOpenUrl) {
224 setUrl(QUrl());
225 }
226
227 if (d->m_bTemp) {
228 QFile::remove(fileName: d->m_file);
229 d->m_bTemp = false;
230 }
231 // It always succeeds for a read-only part,
232 // but the return value exists for reimplementations
233 // (e.g. pressing cancel for a modified read-write part)
234 return true;
235}
236
237void ReadOnlyPartPrivate::slotStatJobFinished(KJob *job)
238{
239 Q_ASSERT(job == m_statJob);
240 m_statJob = nullptr;
241
242 // We could emit canceled on error, but we haven't even emitted started yet,
243 // this could maybe confuse some apps? So for now we'll just fallback to KIO::get
244 // and error again. Well, maybe this even helps with wrong stat results.
245 if (!job->error()) {
246 const QUrl localUrl = static_cast<KIO::StatJob *>(job)->mostLocalUrl();
247 if (localUrl.isLocalFile()) {
248 m_file = localUrl.toLocalFile();
249 (void)openLocalFile();
250 return;
251 }
252 }
253 openRemoteFile();
254}
255
256void ReadOnlyPartPrivate::slotJobFinished(KJob *job)
257{
258 Q_Q(ReadOnlyPart);
259
260 Q_ASSERT(job == m_job);
261 m_job = nullptr;
262 if (job->error()) {
263 Q_EMIT q->canceled(errMsg: job->errorString());
264 } else {
265 if (q->openFile()) {
266 Q_EMIT q->setWindowCaption(m_url.toDisplayString());
267 Q_EMIT q->completed();
268 } else {
269 Q_EMIT q->canceled(errMsg: QString());
270 }
271 }
272}
273
274void ReadOnlyPartPrivate::slotGotMimeType(KIO::Job *job, const QString &mime)
275{
276 // qDebug() << mime;
277 Q_ASSERT(job == m_job);
278 Q_UNUSED(job)
279 // set the mimetype only if it was not already set (for example, by the host application)
280 if (m_arguments.mimeType().isEmpty()) {
281 m_arguments.setMimeType(mime);
282 m_bAutoDetectedMime = true;
283 }
284}
285
286void ReadOnlyPart::guiActivateEvent(GUIActivateEvent *event)
287{
288 Q_D(ReadOnlyPart);
289
290 if (event->activated()) {
291 if (!d->m_url.isEmpty()) {
292 // qDebug() << d->m_url;
293 Q_EMIT setWindowCaption(d->m_url.toDisplayString());
294 } else {
295 Q_EMIT setWindowCaption(QString());
296 }
297 }
298}
299
300bool ReadOnlyPart::openStream(const QString &mimeType, const QUrl &url)
301{
302 Q_D(ReadOnlyPart);
303
304 OpenUrlArguments args = d->m_arguments;
305 if (!closeUrl()) {
306 return false;
307 }
308 d->m_arguments = args;
309 setUrl(url);
310 return doOpenStream(mimeType);
311}
312
313bool ReadOnlyPart::writeStream(const QByteArray &data)
314{
315 return doWriteStream(data);
316}
317
318bool ReadOnlyPart::closeStream()
319{
320 return doCloseStream();
321}
322
323NavigationExtension *ReadOnlyPart::navigationExtension() const
324{
325 return findChild<KParts::NavigationExtension *>();
326}
327
328void KParts::ReadOnlyPart::setArguments(const OpenUrlArguments &arguments)
329{
330 Q_D(ReadOnlyPart);
331 d->m_arguments = arguments;
332 d->m_bAutoDetectedMime = arguments.mimeType().isEmpty();
333}
334
335OpenUrlArguments KParts::ReadOnlyPart::arguments() const
336{
337 Q_D(const ReadOnlyPart);
338 return d->m_arguments;
339}
340
341#include "moc_readonlypart.cpp"
342

source code of kparts/src/readonlypart.cpp