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"