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"