File indexing completed on 2024-04-28 15:29:23

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 1999 Simon Hausmann <hausmann@kde.org>
0004     SPDX-FileCopyrightText: 1999-2005 David Faure <faure@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "readonlypart.h"
0010 #include "readonlypart_p.h"
0011 
0012 #include "kparts_logging.h"
0013 
0014 #include "browserextension.h"
0015 #include "guiactivateevent.h"
0016 
0017 #include <KIO/FileCopyJob>
0018 #include <KIO/StatJob>
0019 #include <KJobWidgets>
0020 #include <KProtocolInfo>
0021 
0022 #include <QDir>
0023 #include <QFileInfo>
0024 #include <QMimeDatabase>
0025 #include <QTemporaryFile>
0026 
0027 using namespace KParts;
0028 
0029 ReadOnlyPart::ReadOnlyPart(QObject *parent)
0030     : Part(*new ReadOnlyPartPrivate(this), parent)
0031 {
0032 }
0033 
0034 ReadOnlyPart::ReadOnlyPart(ReadOnlyPartPrivate &dd, QObject *parent)
0035     : Part(dd, parent)
0036 {
0037 }
0038 
0039 ReadOnlyPart::~ReadOnlyPart()
0040 {
0041     Q_D(ReadOnlyPart);
0042     d->m_closeUrlFromDestructor = true;
0043     ReadOnlyPart::closeUrl();
0044 }
0045 
0046 QUrl ReadOnlyPart::url() const
0047 {
0048     Q_D(const ReadOnlyPart);
0049 
0050     return d->m_url;
0051 }
0052 
0053 void ReadOnlyPart::setUrl(const QUrl &url)
0054 {
0055     Q_D(ReadOnlyPart);
0056 
0057     if (d->m_url != url) {
0058         d->m_url = url;
0059         if (!d->m_closeUrlFromDestructor) {
0060             Q_EMIT urlChanged(url);
0061         }
0062     }
0063 }
0064 
0065 QString ReadOnlyPart::localFilePath() const
0066 {
0067     Q_D(const ReadOnlyPart);
0068 
0069     return d->m_file;
0070 }
0071 
0072 void ReadOnlyPart::setLocalFilePath(const QString &localFilePath)
0073 {
0074     Q_D(ReadOnlyPart);
0075 
0076     d->m_file = localFilePath;
0077 }
0078 
0079 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 0)
0080 bool ReadOnlyPart::isLocalFileTemporary() const
0081 {
0082     Q_D(const ReadOnlyPart);
0083 
0084     return d->m_bTemp;
0085 }
0086 #endif
0087 
0088 #if KPARTS_BUILD_DEPRECATED_SINCE(5, 0)
0089 void ReadOnlyPart::setLocalFileTemporary(bool temp)
0090 {
0091     Q_D(ReadOnlyPart);
0092 
0093     d->m_bTemp = temp;
0094 }
0095 #endif
0096 
0097 void ReadOnlyPart::setProgressInfoEnabled(bool show)
0098 {
0099     Q_D(ReadOnlyPart);
0100 
0101     d->m_showProgressInfo = show;
0102 }
0103 
0104 bool ReadOnlyPart::isProgressInfoEnabled() const
0105 {
0106     Q_D(const ReadOnlyPart);
0107 
0108     return d->m_showProgressInfo;
0109 }
0110 
0111 #if KPARTS_BUILD_DEPRECATED_SINCE(3, 0)
0112 void ReadOnlyPart::showProgressInfo(bool show)
0113 {
0114     Q_D(ReadOnlyPart);
0115 
0116     d->m_showProgressInfo = show;
0117 }
0118 #endif
0119 
0120 bool ReadOnlyPart::openUrl(const QUrl &url)
0121 {
0122     Q_D(ReadOnlyPart);
0123 
0124     if (!url.isValid()) {
0125         return false;
0126     }
0127     if (d->m_bAutoDetectedMime) {
0128         d->m_arguments.setMimeType(QString());
0129         d->m_bAutoDetectedMime = false;
0130     }
0131     OpenUrlArguments args = d->m_arguments;
0132     d->m_closeUrlFromOpenUrl = true;
0133     const bool closed = closeUrl();
0134     d->m_closeUrlFromOpenUrl = false;
0135     if (!closed) {
0136         return false;
0137     }
0138     d->m_arguments = args;
0139     setUrl(url);
0140 
0141     d->m_file.clear();
0142 
0143     if (d->m_url.isLocalFile()) {
0144         d->m_file = d->m_url.toLocalFile();
0145         return d->openLocalFile();
0146     } else if (KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) {
0147         // Maybe we can use a "local path", to avoid a temp copy?
0148         KIO::JobFlags flags = d->m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
0149         d->m_statJob = KIO::mostLocalUrl(d->m_url, flags);
0150         KJobWidgets::setWindow(d->m_statJob, widget());
0151         connect(d->m_statJob, &KJob::result, this, [d](KJob *job) {
0152             d->slotStatJobFinished(job);
0153         });
0154         return true;
0155     } else {
0156         d->openRemoteFile();
0157         return true;
0158     }
0159 }
0160 
0161 bool ReadOnlyPart::openFile()
0162 {
0163     qCWarning(KPARTSLOG) << "Default implementation of ReadOnlyPart::openFile called!" << metaObject()->className()
0164                          << "should reimplement either openUrl or openFile.";
0165     return false;
0166 }
0167 
0168 bool ReadOnlyPartPrivate::openLocalFile()
0169 {
0170     Q_Q(ReadOnlyPart);
0171     Q_EMIT q->started(nullptr);
0172     m_bTemp = false;
0173     // set the mimetype only if it was not already set (for example, by the host application)
0174     if (m_arguments.mimeType().isEmpty()) {
0175         // get the mimetype of the file
0176         // using findByUrl() to avoid another string -> url conversion
0177         QMimeDatabase db;
0178         QMimeType mime = db.mimeTypeForUrl(m_url);
0179         if (!mime.isDefault()) {
0180             m_arguments.setMimeType(mime.name());
0181             m_bAutoDetectedMime = true;
0182         }
0183     }
0184     const bool ret = q->openFile();
0185     if (ret) {
0186         Q_EMIT q->setWindowCaption(m_url.toDisplayString());
0187         Q_EMIT q->completed();
0188     } else {
0189         Q_EMIT q->canceled(QString());
0190     }
0191     return ret;
0192 }
0193 
0194 void ReadOnlyPartPrivate::openRemoteFile()
0195 {
0196     Q_Q(ReadOnlyPart);
0197     m_bTemp = true;
0198     // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice)
0199     QString fileName = m_url.fileName();
0200     QFileInfo fileInfo(fileName);
0201     QString ext = fileInfo.completeSuffix();
0202     QString extension;
0203     if (!ext.isEmpty() && !m_url.hasQuery()) { // not if the URL has a query, e.g. cgi.pl?something
0204         extension = QLatin1Char('.') + ext; // keep the '.'
0205     }
0206     QTemporaryFile tempFile(QDir::tempPath() + QLatin1Char('/') + m_metaData.pluginId() + QLatin1String("XXXXXX") + extension);
0207     tempFile.setAutoRemove(false);
0208     tempFile.open();
0209     m_file = tempFile.fileName();
0210 
0211     QUrl destURL = QUrl::fromLocalFile(m_file);
0212     KIO::JobFlags flags = m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
0213     flags |= KIO::Overwrite;
0214     m_job = KIO::file_copy(m_url, destURL, 0600, flags);
0215     m_job->setFinishedNotificationHidden(true);
0216     KJobWidgets::setWindow(m_job, q->widget());
0217     Q_EMIT q->started(m_job);
0218 
0219     QObject::connect(m_job, &KJob::result, q, [this](KJob *job) {
0220         slotJobFinished(job);
0221     });
0222     QObject::connect(m_job, &KIO::FileCopyJob::mimeTypeFound, q, [this](KIO::Job *job, const QString &mimeType) {
0223         slotGotMimeType(job, mimeType);
0224     });
0225 }
0226 
0227 void ReadOnlyPart::abortLoad()
0228 {
0229     Q_D(ReadOnlyPart);
0230 
0231     if (d->m_statJob) {
0232         // qDebug() << "Aborting job" << d->m_statJob;
0233         d->m_statJob->kill();
0234         d->m_statJob = nullptr;
0235     }
0236     if (d->m_job) {
0237         // qDebug() << "Aborting job" << d->m_job;
0238         d->m_job->kill();
0239         d->m_job = nullptr;
0240     }
0241 }
0242 
0243 bool ReadOnlyPart::closeUrl()
0244 {
0245     Q_D(ReadOnlyPart);
0246 
0247     abortLoad(); // just in case
0248 
0249     d->m_arguments = KParts::OpenUrlArguments();
0250     if (!d->m_closeUrlFromOpenUrl) {
0251         setUrl(QUrl());
0252     }
0253 
0254     if (d->m_bTemp) {
0255         QFile::remove(d->m_file);
0256         d->m_bTemp = false;
0257     }
0258     // It always succeeds for a read-only part,
0259     // but the return value exists for reimplementations
0260     // (e.g. pressing cancel for a modified read-write part)
0261     return true;
0262 }
0263 
0264 void ReadOnlyPartPrivate::slotStatJobFinished(KJob *job)
0265 {
0266     Q_ASSERT(job == m_statJob);
0267     m_statJob = nullptr;
0268 
0269     // We could emit canceled on error, but we haven't even emitted started yet,
0270     // this could maybe confuse some apps? So for now we'll just fallback to KIO::get
0271     // and error again. Well, maybe this even helps with wrong stat results.
0272     if (!job->error()) {
0273         const QUrl localUrl = static_cast<KIO::StatJob *>(job)->mostLocalUrl();
0274         if (localUrl.isLocalFile()) {
0275             m_file = localUrl.toLocalFile();
0276             (void)openLocalFile();
0277             return;
0278         }
0279     }
0280     openRemoteFile();
0281 }
0282 
0283 void ReadOnlyPartPrivate::slotJobFinished(KJob *job)
0284 {
0285     Q_Q(ReadOnlyPart);
0286 
0287     Q_ASSERT(job == m_job);
0288     m_job = nullptr;
0289     if (job->error()) {
0290         Q_EMIT q->canceled(job->errorString());
0291     } else {
0292         if (q->openFile()) {
0293             Q_EMIT q->setWindowCaption(m_url.toDisplayString());
0294             Q_EMIT q->completed();
0295         } else {
0296             Q_EMIT q->canceled(QString());
0297         }
0298     }
0299 }
0300 
0301 void ReadOnlyPartPrivate::slotGotMimeType(KIO::Job *job, const QString &mime)
0302 {
0303     // qDebug() << mime;
0304     Q_ASSERT(job == m_job);
0305     Q_UNUSED(job)
0306     // set the mimetype only if it was not already set (for example, by the host application)
0307     if (m_arguments.mimeType().isEmpty()) {
0308         m_arguments.setMimeType(mime);
0309         m_bAutoDetectedMime = true;
0310     }
0311 }
0312 
0313 void ReadOnlyPart::guiActivateEvent(GUIActivateEvent *event)
0314 {
0315     Q_D(ReadOnlyPart);
0316 
0317     if (event->activated()) {
0318         if (!d->m_url.isEmpty()) {
0319             // qDebug() << d->m_url;
0320             Q_EMIT setWindowCaption(d->m_url.toDisplayString());
0321         } else {
0322             Q_EMIT setWindowCaption(QString());
0323         }
0324     }
0325 }
0326 
0327 bool ReadOnlyPart::openStream(const QString &mimeType, const QUrl &url)
0328 {
0329     Q_D(ReadOnlyPart);
0330 
0331     OpenUrlArguments args = d->m_arguments;
0332     if (!closeUrl()) {
0333         return false;
0334     }
0335     d->m_arguments = args;
0336     setUrl(url);
0337     return doOpenStream(mimeType);
0338 }
0339 
0340 bool ReadOnlyPart::writeStream(const QByteArray &data)
0341 {
0342     return doWriteStream(data);
0343 }
0344 
0345 bool ReadOnlyPart::closeStream()
0346 {
0347     return doCloseStream();
0348 }
0349 
0350 BrowserExtension *ReadOnlyPart::browserExtension() const
0351 {
0352     return findChild<KParts::BrowserExtension *>();
0353 }
0354 
0355 void KParts::ReadOnlyPart::setArguments(const OpenUrlArguments &arguments)
0356 {
0357     Q_D(ReadOnlyPart);
0358     d->m_arguments = arguments;
0359     d->m_bAutoDetectedMime = arguments.mimeType().isEmpty();
0360 }
0361 
0362 OpenUrlArguments KParts::ReadOnlyPart::arguments() const
0363 {
0364     Q_D(const ReadOnlyPart);
0365     return d->m_arguments;
0366 }
0367 
0368 #include "moc_readonlypart.cpp"