File indexing completed on 2024-05-19 03:59:08

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 "guiactivateevent.h"
0015 #include "navigationextension.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, const KPluginMetaData &data)
0030     : Part(*new ReadOnlyPartPrivate(this, data), 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 void ReadOnlyPart::setProgressInfoEnabled(bool show)
0080 {
0081     Q_D(ReadOnlyPart);
0082 
0083     d->m_showProgressInfo = show;
0084 }
0085 
0086 bool ReadOnlyPart::isProgressInfoEnabled() const
0087 {
0088     Q_D(const ReadOnlyPart);
0089 
0090     return d->m_showProgressInfo;
0091 }
0092 
0093 bool ReadOnlyPart::openUrl(const QUrl &url)
0094 {
0095     Q_D(ReadOnlyPart);
0096 
0097     if (!url.isValid()) {
0098         return false;
0099     }
0100     if (d->m_bAutoDetectedMime) {
0101         d->m_arguments.setMimeType(QString());
0102         d->m_bAutoDetectedMime = false;
0103     }
0104     OpenUrlArguments args = d->m_arguments;
0105     d->m_closeUrlFromOpenUrl = true;
0106     const bool closed = closeUrl();
0107     d->m_closeUrlFromOpenUrl = false;
0108     if (!closed) {
0109         return false;
0110     }
0111     d->m_arguments = args;
0112     setUrl(url);
0113 
0114     d->m_file.clear();
0115 
0116     if (d->m_url.isLocalFile()) {
0117         d->m_file = d->m_url.toLocalFile();
0118         return d->openLocalFile();
0119     } else if (KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) {
0120         // Maybe we can use a "local path", to avoid a temp copy?
0121         KIO::JobFlags flags = d->m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
0122         d->m_statJob = KIO::mostLocalUrl(d->m_url, flags);
0123         KJobWidgets::setWindow(d->m_statJob, widget());
0124         connect(d->m_statJob, &KJob::result, this, [d](KJob *job) {
0125             d->slotStatJobFinished(job);
0126         });
0127         return true;
0128     } else {
0129         d->openRemoteFile();
0130         return true;
0131     }
0132 }
0133 
0134 bool ReadOnlyPart::openFile()
0135 {
0136     qCWarning(KPARTSLOG) << "Default implementation of ReadOnlyPart::openFile called!" << metaObject()->className()
0137                          << "should reimplement either openUrl or openFile.";
0138     return false;
0139 }
0140 
0141 bool ReadOnlyPartPrivate::openLocalFile()
0142 {
0143     Q_Q(ReadOnlyPart);
0144     Q_EMIT q->started(nullptr);
0145     m_bTemp = false;
0146     // set the mimetype only if it was not already set (for example, by the host application)
0147     if (m_arguments.mimeType().isEmpty()) {
0148         // get the mimetype of the file
0149         // using findByUrl() to avoid another string -> url conversion
0150         QMimeDatabase db;
0151         QMimeType mime = db.mimeTypeForUrl(m_url);
0152         if (!mime.isDefault()) {
0153             m_arguments.setMimeType(mime.name());
0154             m_bAutoDetectedMime = true;
0155         }
0156     }
0157     const bool ret = q->openFile();
0158     if (ret) {
0159         Q_EMIT q->setWindowCaption(m_url.toDisplayString());
0160         Q_EMIT q->completed();
0161     } else {
0162         Q_EMIT q->canceled(QString());
0163     }
0164     return ret;
0165 }
0166 
0167 void ReadOnlyPartPrivate::openRemoteFile()
0168 {
0169     Q_Q(ReadOnlyPart);
0170     m_bTemp = true;
0171     // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice)
0172     QString fileName = m_url.fileName();
0173     QFileInfo fileInfo(fileName);
0174     QString ext = fileInfo.completeSuffix();
0175     QString extension;
0176     if (!ext.isEmpty() && !m_url.hasQuery()) { // not if the URL has a query, e.g. cgi.pl?something
0177         extension = QLatin1Char('.') + ext; // keep the '.'
0178     }
0179     QTemporaryFile tempFile(QDir::tempPath() + QLatin1Char('/') + m_metaData.pluginId() + QLatin1String("XXXXXX") + extension);
0180     tempFile.setAutoRemove(false);
0181     tempFile.open();
0182     m_file = tempFile.fileName();
0183 
0184     QUrl destURL = QUrl::fromLocalFile(m_file);
0185     KIO::JobFlags flags = m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
0186     flags |= KIO::Overwrite;
0187     m_job = KIO::file_copy(m_url, destURL, 0600, flags);
0188     m_job->setFinishedNotificationHidden(true);
0189     KJobWidgets::setWindow(m_job, q->widget());
0190     Q_EMIT q->started(m_job);
0191 
0192     QObject::connect(m_job, &KJob::result, q, [this](KJob *job) {
0193         slotJobFinished(job);
0194     });
0195     QObject::connect(m_job, &KIO::FileCopyJob::mimeTypeFound, q, [this](KIO::Job *job, const QString &mimeType) {
0196         slotGotMimeType(job, mimeType);
0197     });
0198 }
0199 
0200 void ReadOnlyPart::abortLoad()
0201 {
0202     Q_D(ReadOnlyPart);
0203 
0204     if (d->m_statJob) {
0205         // qDebug() << "Aborting job" << d->m_statJob;
0206         d->m_statJob->kill();
0207         d->m_statJob = nullptr;
0208     }
0209     if (d->m_job) {
0210         // qDebug() << "Aborting job" << d->m_job;
0211         d->m_job->kill();
0212         d->m_job = nullptr;
0213     }
0214 }
0215 
0216 bool ReadOnlyPart::closeUrl()
0217 {
0218     Q_D(ReadOnlyPart);
0219 
0220     abortLoad(); // just in case
0221 
0222     d->m_arguments = KParts::OpenUrlArguments();
0223     if (!d->m_closeUrlFromOpenUrl) {
0224         setUrl(QUrl());
0225     }
0226 
0227     if (d->m_bTemp) {
0228         QFile::remove(d->m_file);
0229         d->m_bTemp = false;
0230     }
0231     // It always succeeds for a read-only part,
0232     // but the return value exists for reimplementations
0233     // (e.g. pressing cancel for a modified read-write part)
0234     return true;
0235 }
0236 
0237 void ReadOnlyPartPrivate::slotStatJobFinished(KJob *job)
0238 {
0239     Q_ASSERT(job == m_statJob);
0240     m_statJob = nullptr;
0241 
0242     // We could emit canceled on error, but we haven't even emitted started yet,
0243     // this could maybe confuse some apps? So for now we'll just fallback to KIO::get
0244     // and error again. Well, maybe this even helps with wrong stat results.
0245     if (!job->error()) {
0246         const QUrl localUrl = static_cast<KIO::StatJob *>(job)->mostLocalUrl();
0247         if (localUrl.isLocalFile()) {
0248             m_file = localUrl.toLocalFile();
0249             (void)openLocalFile();
0250             return;
0251         }
0252     }
0253     openRemoteFile();
0254 }
0255 
0256 void ReadOnlyPartPrivate::slotJobFinished(KJob *job)
0257 {
0258     Q_Q(ReadOnlyPart);
0259 
0260     Q_ASSERT(job == m_job);
0261     m_job = nullptr;
0262     if (job->error()) {
0263         Q_EMIT q->canceled(job->errorString());
0264     } else {
0265         if (q->openFile()) {
0266             Q_EMIT q->setWindowCaption(m_url.toDisplayString());
0267             Q_EMIT q->completed();
0268         } else {
0269             Q_EMIT q->canceled(QString());
0270         }
0271     }
0272 }
0273 
0274 void ReadOnlyPartPrivate::slotGotMimeType(KIO::Job *job, const QString &mime)
0275 {
0276     // qDebug() << mime;
0277     Q_ASSERT(job == m_job);
0278     Q_UNUSED(job)
0279     // set the mimetype only if it was not already set (for example, by the host application)
0280     if (m_arguments.mimeType().isEmpty()) {
0281         m_arguments.setMimeType(mime);
0282         m_bAutoDetectedMime = true;
0283     }
0284 }
0285 
0286 void ReadOnlyPart::guiActivateEvent(GUIActivateEvent *event)
0287 {
0288     Q_D(ReadOnlyPart);
0289 
0290     if (event->activated()) {
0291         if (!d->m_url.isEmpty()) {
0292             // qDebug() << d->m_url;
0293             Q_EMIT setWindowCaption(d->m_url.toDisplayString());
0294         } else {
0295             Q_EMIT setWindowCaption(QString());
0296         }
0297     }
0298 }
0299 
0300 bool ReadOnlyPart::openStream(const QString &mimeType, const QUrl &url)
0301 {
0302     Q_D(ReadOnlyPart);
0303 
0304     OpenUrlArguments args = d->m_arguments;
0305     if (!closeUrl()) {
0306         return false;
0307     }
0308     d->m_arguments = args;
0309     setUrl(url);
0310     return doOpenStream(mimeType);
0311 }
0312 
0313 bool ReadOnlyPart::writeStream(const QByteArray &data)
0314 {
0315     return doWriteStream(data);
0316 }
0317 
0318 bool ReadOnlyPart::closeStream()
0319 {
0320     return doCloseStream();
0321 }
0322 
0323 NavigationExtension *ReadOnlyPart::navigationExtension() const
0324 {
0325     return findChild<KParts::NavigationExtension *>();
0326 }
0327 
0328 void KParts::ReadOnlyPart::setArguments(const OpenUrlArguments &arguments)
0329 {
0330     Q_D(ReadOnlyPart);
0331     d->m_arguments = arguments;
0332     d->m_bAutoDetectedMime = arguments.mimeType().isEmpty();
0333 }
0334 
0335 OpenUrlArguments KParts::ReadOnlyPart::arguments() const
0336 {
0337     Q_D(const ReadOnlyPart);
0338     return d->m_arguments;
0339 }
0340 
0341 #include "moc_readonlypart.cpp"