File indexing completed on 2024-04-14 04:53:19
0001 /* 0002 This file is part of the KDE project. 0003 0004 SPDX-FileCopyrightText: 2020 Stefano Crocco <stefano.crocco@alice.it> 0005 0006 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0007 */ 0008 0009 #ifndef URLLLOADER_H 0010 #define URLLLOADER_H 0011 0012 #include "konqopenurlrequest.h" 0013 #include "browseropenorsavequestion.h" 0014 0015 #include <QObject> 0016 #include <QUrl> 0017 #include <QPointer> 0018 0019 #include <KService> 0020 #include <KJob> 0021 0022 namespace KParts { 0023 class ReadOnlyPart; 0024 }; 0025 0026 class KJob; 0027 namespace KIO { 0028 class OpenUrlJob; 0029 class ApplicationLauncherJob; 0030 class MimeTypeFinderJob; 0031 } 0032 namespace KonqInterfaces { 0033 class DownloaderExtension; 0034 class DownloaderJob; 0035 } 0036 class KonqMainWindow; 0037 class KonqView; 0038 0039 0040 /** 0041 * @brief Class which takes care of finding out what to do with an URL and carries out the chosen action 0042 * 0043 * Depending on whether the mimetype of the URL is already known and on whether the URL is a local or remote 0044 * file, this class can work in a synchronous or asynchronous way. This should be mostly transparent to the user. 0045 * 0046 * This class is meant to be used in the following way: 0047 * - create an instance, passing it the known information about the URL to load 0048 * - connect to the finished() signal to be notified when the URL has been loaded 0049 * - call start(): this will attempt to determine the synchronously determine mimetype and, if successful, will 0050 * decide what to do with it 0051 * - call viewToUse() to find out where the URL should be opened. If needed, create a new view and call setView() 0052 * passing the new view 0053 * - call goOn(): this will asynchronously determine the mimetype and the action to carry out, if not already done, 0054 * and perform the action itself. 0055 * 0056 * @internal 0057 * For remote files, what happens after goOn depends on whether the metadata associated with the request to open the 0058 * URL (`m_request.args.metaData()`) contains the key DownloaderInterface::requestDownloadByPartKey(): 0059 * - if the key doesn't exist, this class will determine the mimetype of the URL (if needed), then embed, open or save it 0060 * depending on the mimetype and the user's previous choices 0061 * - if the key exists, (after determining the mimetype of the URL, if not already known), this class will check whether 0062 * the requesting part implements the DownloaderInterface. If so, it'll let the job returned by DownloaderInterface::downloadJob() 0063 * to download the URL, then it will update #m_url so that it contains the path of the downloaded file. It then proceeds as 0064 * in the above case. If the file should be saved, it'll simply moved from the download location to the location chosen by the user 0065 */ 0066 class UrlLoader : public QObject 0067 { 0068 Q_OBJECT 0069 0070 public: 0071 /** 0072 * Constructor 0073 * 0074 * @param mainWindow the KonqMainWindow which asked to load the URL 0075 * @param view the view which asked to open the URL. It can be `nullptr` 0076 * @param url the URL to load 0077 * @param mimeType the mimetype of the URL or an empty string if not known 0078 * @param req the object containing information about the URL loading request 0079 * @param trustedSource whether the source of the URL is trusted 0080 * @param forceOpen tells never to embed the URL 0081 */ 0082 UrlLoader(KonqMainWindow* mainWindow, KonqView* view, const QUrl& url, const QString& mimeType, const KonqOpenURLRequest& req, bool trustedSource, bool dontEmbed = false); 0083 ~UrlLoader(); 0084 0085 0086 /** @brief Enum describing the possible actions to be taken*/ 0087 enum class OpenUrlAction{ 0088 UnknwonAction, /**< The action hasn't been decided yet */ 0089 DoNothing, /**< No action should be taken */ 0090 Save, /**< Save the URL */ 0091 Embed, /**< Display the URL in an embedded viewer */ 0092 Open, /**< Display the URL in a separate viewer*/ 0093 Execute /**< Execute the URL */ 0094 }; 0095 0096 /** @brief Enum describing the view to use to embed an URL*/ 0097 enum class ViewToUse{ 0098 View, /**< Use the view passed as argument to the constructor */ 0099 CurrentView, /**< Use the current view */ 0100 NewTab /**< Create a new tab and use its view */ 0101 }; 0102 0103 /** 0104 * @brief Determines what to do with the URL if its mimetype can be determined without using an `OpenUrlJob`. 0105 * 0106 * When the mimetype can be determined without using an `OpenUrlJob`, this function calls decideAction() to 0107 * determine what should be done with the URL. In this case, subsequent calls to isReady() will return `true`. 0108 * If the mimetype can't be determined without using an `OpenUrlJob`, calls to isReady() will return `false`, 0109 * because `OpenUrlJob` works asynchronously. 0110 * 0111 * The mimetype can be determined without using an `OpenUrlJob` in the following situations: 0112 * - a mimetype different from `application/octet-stream` is passed to the constructor 0113 * - the URL is a local file 0114 * - the URL scheme is `http` and the URL hasn't yet been processed by the default HTML engine (in this case, 0115 * a fake `text/html` mimetype will be used and the HTML engine will take care of determining the mimetype) 0116 * 0117 * @note This function *doesn't* create or start the `OpenUrlJob`, even if it will be needed. 0118 */ 0119 void start(); 0120 0121 /** 0122 * @brief Performs the required action on the URL, using an `OpenUrlJob` to determine its mimetype if needed. 0123 * 0124 * If start() had been able to determine the action to carry out, this function simply calls performAction() 0125 * to perform the chosen action. In all other cases (that is, if the mimetype is still unknown), it launches 0126 * an `OpenUrlJob` to determine the mimetype. When the job has determined the mimetype, this function will 0127 * call decideAction() to decide what to do with the URL and then call performAction() to carry out the chosen 0128 * action. 0129 */ 0130 void goOn(); 0131 0132 /** 0133 * @brief Carries out the requested action 0134 */ 0135 void performAction(); 0136 0137 void abort(); 0138 0139 QString mimeType() const; 0140 bool isReady() const {return m_ready;} 0141 ViewToUse viewToUse() const; 0142 QUrl url() const {return m_url;} 0143 KonqOpenURLRequest request() const {return m_request;} 0144 KonqView* view() const {return m_view;} 0145 void setView(KonqView *view); 0146 bool isAsync() const {return m_isAsync;} 0147 void setOldLocationBarUrl(const QString &old); 0148 // QString oldLocationBarUrl() const {return m_oldLocationBarUrl;} 0149 bool hasError() const {return m_jobErrorCode;} 0150 void setNewTab(bool newTab); 0151 0152 // /** 0153 // * @brief whether a given mimetype refers to an executable program instead of a data file 0154 // * @param mimeTypeName the mimetype to test 0155 // * @return `true` if @p mimeTypeName refers to an executable and `false` otherwise 0156 // */ 0157 // static bool isExecutable(const QString &mimeTypeName); 0158 QString suggestedFileName() const {return m_request.suggestedFileName;} 0159 0160 /** 0161 * @brief The @c ID of the part to use to open a local file 0162 * 0163 * @param path the file path 0164 * @return the plugin id for the preferred part for @p file (as returned by @c KPluginMetaData::pluginId() or 0165 * an empty string if no part could be found 0166 */ 0167 static QString partForLocalFile(const QString &path); 0168 0169 signals: 0170 void finished(UrlLoader *self); 0171 0172 private slots: 0173 0174 void mimetypeDeterminedByJob(); 0175 void jobFinished(KJob* job); 0176 void done(KJob *job=nullptr); 0177 0178 /** 0179 * @brief Slot called when a part which has asked to download itself the URL has finished doing so 0180 * 0181 * @param job the DownloaderJob used by the part to download the URL 0182 */ 0183 void downloadForEmbeddingOrOpeningDone(KonqInterfaces::DownloaderJob *job, const QUrl &url); 0184 0185 private: 0186 0187 void embed(); 0188 void open(); 0189 void execute(); 0190 void save(); 0191 0192 bool shouldEmbedThis() const; 0193 void performSave(const QUrl &orig, const QUrl &dest); 0194 void detectArchiveSettings(); 0195 void detectSettingsForLocalFiles(); 0196 void detectSettingsForRemoteFiles(); 0197 void launchMimeTypeFinderJob(); 0198 static bool isTextExecutable(const QString &mimeType); 0199 void openExternally(); 0200 void killOpenUrlJob(); 0201 static bool serviceIsKonqueror(KService::Ptr service); 0202 static bool isMimeTypeKnown(const QString &mimeType); 0203 bool decideEmbedOrSave(); 0204 void decideOpenOrSave(); 0205 bool embedWithoutAskingToSave(const QString &mimeType); 0206 bool shouldUseDefaultHttpMimeype() const; 0207 void decideAction(); 0208 bool isViewLocked() const; 0209 0210 /** 0211 * @brief Whether the URL we are loading is executable 0212 * 0213 * To be executable, all the following must be true: 0214 * - the URL must be local (we don't want to execute files from the web) 0215 * - the mimetype should be in a list of known executable types (desktop files, executable files, libraries, 0216 * shell scripts) 0217 * - the executable bit must be set 0218 * @return `true` if the file is executable and `false` otherwise 0219 */ 0220 bool isUrlExecutable() const; 0221 0222 /** 0223 * @brief Checks whether a file downloaded by a part really has the mime type described by the HTML header 0224 * 0225 * This function is called when the part requesting to download an URL wants to perform the download itself, 0226 * after the download has finished. It attempts to determine the mime type both using the file contents and the 0227 * file extension. The algorithm used is the following: 0228 * - determine the mime type using the contents 0229 * - determine the mime type using the extension 0230 * - if the mime type determined from the extension inherits the one determined by content, use the former, otherwise 0231 * use the latter. 0232 * This way to determine the mimetype is different from that used by `QMimeDataBase::mimeTypeForFile` when called 0233 * with `QMimeDatabase::MatchDefault`, which only checks the content as a fallback. 0234 * 0235 * If the mime type is different from the original one, it replaces the original one and attempts to determine 0236 * again what to do with the file. If the determined mime type is `application/octet-stream`, the original mime type 0237 * is kept. 0238 * 0239 * This function is only called if the user had decided to embed or open the file; it isn't called if the user 0240 * decided to save it (saving doesn't depend on the mime type). 0241 * 0242 * @note This function is needed because sometimes the mime type in the HTTP header is different from the actual 0243 * mimetype of the file. 0244 */ 0245 void checkDownloadedMimetype(); 0246 0247 /** 0248 * @brief Finds the part to use to embed the URL 0249 * 0250 * The part is found according to the mimetype and the user preferences. If #m_dontPassToWebEnginePart is `true` 0251 * `webenginepart` won't be returned (even if no other parts are available). 0252 * 0253 * If the `serviceName` variable of #m_request is not empty, that part will be used, regardless of other user 0254 * preferences. If @p forceServiceName is `false`, that part will only be used if it actually supports the mimetype 0255 * @param forceServiceName whether to force the use of the part whose name is in `m_request.serviceName`, even if 0256 * it doesn't support the mimetype 0257 * @return the metadata representing the chosen part or an empty `KPluginMetaData` if no suitable part can be found 0258 * @see m_dontPassToWebEnginePart 0259 */ 0260 KPluginMetaData findEmbeddingPart(bool forceServiceName=true) const; 0261 0262 /** 0263 * @brief Determines #m_mimeType in the constructor 0264 * 0265 * The algorithm used is the following: 0266 * - if #m_mimeType isn't empty or a default mimetype, use it 0267 * - if #m_request.args.mimeType() isn't empty or a default mimetype, use it 0268 * - otherwise use an empty string 0269 */ 0270 void determineStartingMimetype(); 0271 0272 /** 0273 * @brief Casts the requesting part or one of its children to a DownloaderInterface*, if possible 0274 * @note Since this uses DownloaderInterface::interface(), it's slower than a simple cast 0275 * @return the requesting part or one of its children casted to a DownloaderInterface* or `nullptr` if 0276 * the requesting part is `nullptr` or neither it nor its children implement DownloaderInterface 0277 */ 0278 KonqInterfaces::DownloaderExtension* downloaderInterface() const; 0279 0280 /** 0281 * @brief Retrieves from the requesting part a DownloaderJob to download the URL 0282 * 0283 * The retrieved job is stored in #m_partDownloaderJob. If downloaderInterface() 0284 * returns `nullptr`, #m_partDownloaderJob is set to `nullptr` 0285 */ 0286 void getDownloaderJobFromPart(); 0287 0288 /** 0289 * @brief Downloads the URL using the job provided by the requesting part, if any 0290 * 0291 * If #m_partDownloaderJob is `nullptr`, nothing is done 0292 */ 0293 void downloadForEmbeddingOrOpening(); 0294 0295 /** 0296 * @brief Checks whether the given file can be read and, in case of a directory, entered into 0297 * 0298 * If the @p path can't be read, according to `QFileInfo::isReadable()`, it attempts to determine the reason: 0299 * - if the parent directory can be entered (according to `QFileInfo::isExecutable()`) and `QFileInfo::exists() returns `false`, 0300 * it assumes the file actually doesn't exist 0301 * - if the parent directory can't be entered (according to `QFileInfo::isExecutable()`), it assumes that the permissions don't 0302 * allow reading for the user. 0303 * 0304 * @return a `KIO::Error` value if @p can't be read or 0 if it can be read and, in case it's a directory, entered. In particular, 0305 * it returns 0306 * - `KIO::ERR_DOES_NOT_EXIST` if @p path doesn't exist 0307 * - `KIO::ERR_CANNOT_OPEN_FOR_READING` if @p path exists but can't be read 0308 * - `ERR_CANNOT_ENTER_DIRECTORY` if @p path is a directory which can't be entered 0309 */ 0310 static int checkAccessToLocalFile(const QString &path); 0311 0312 typedef QPair<OpenUrlAction, KService::Ptr> OpenSaveAnswer; 0313 0314 enum class OpenEmbedMode{Open, Embed}; 0315 0316 /** 0317 * @brief Asks the user whether he wants to save the url or open/embed it 0318 * 0319 * This will display the user a `KParts::BrowserOpenOrSaveQuestion` dialog, in open or embed mode depending on the argument. 0320 * When displaying the dialog in "open" mode, it also allows to choose the application to use. 0321 * 0322 * @param mode tells the kind of dialog to show the user: and "Embed or save" dialog if this is OpenEmbedMode::Embed or an "Open 0323 * or save" dialog if it is OpenEmbedMode::Open 0324 * @return an object describing the user's choices. The first element is the action the user has chosen and can only be OpenUrlAction::Save, 0325 * OpenUrlAction::Open, OpenUrlAction::Embed, OpenUrlAction::DoNothing. The second element is the service chosen by the user if @p mode is 0326 * OpenEmbedMode::Open and `nullptr` otherwise. 0327 * @note If @p mode is OpenEmbedMode::Open and the second element of the returned value is `nullptr`, it may mean that the user has chosen the 0328 * "Open with..." entry from the menu displayed by the "Open with..." button. The caller shouldn't try to decide itself which service to use; it 0329 * should instead pass `nullptr` as argument to the KIO::ApplicationLauncherJob constructor, which will take care to display the "Open with..." 0330 * dialog to the user. 0331 */ 0332 OpenSaveAnswer askSaveOrOpen(OpenEmbedMode mode) const; 0333 OpenUrlAction decideExecute() const; 0334 0335 private: 0336 QPointer<KonqMainWindow> m_mainWindow; 0337 QUrl m_url; 0338 0339 /** 0340 * @brief The mimetype of the URL 0341 * 0342 * If this is empty, it means that the mimetype isn't known and must be determined somehow. 0343 * If this is not empty, it is assumed that its value is the correct mimetype for the URL and 0344 * no other attempts to determine the mimetype will be done (even if this is the default mimetype). 0345 * @see launchMimeTypeFinderJob() 0346 * @see mimetypeDeterminedByJob() 0347 */ 0348 QString m_mimeType; 0349 KonqOpenURLRequest m_request; 0350 KonqView *m_view; 0351 bool m_trustedSource; 0352 bool m_dontEmbed; 0353 bool m_ready = false; 0354 bool m_isAsync = false; 0355 OpenUrlAction m_action = OpenUrlAction::UnknwonAction; 0356 KPluginMetaData m_part; 0357 KService::Ptr m_service; 0358 QPointer<KIO::OpenUrlJob> m_openUrlJob; 0359 QPointer<KIO::ApplicationLauncherJob> m_applicationLauncherJob; 0360 QPointer<KIO::MimeTypeFinderJob> m_mimeTypeFinderJob; 0361 QPointer<KonqInterfaces::DownloaderJob> m_partDownloaderJob; 0362 QString m_oldLocationBarUrl; 0363 int m_jobErrorCode = 0; 0364 bool m_dontPassToWebEnginePart; 0365 bool m_protocolAllowsReading; 0366 bool m_letRequestingPartDownloadUrl = false; ///<Whether the URL should be downloaded by the part before opening/embedding/saving it 0367 /** 0368 * @brief Whether only the `embed` action is allowed 0369 * 0370 * If `true`, the only action performed will be to embed the URL. If that action can't be performed for any reason (including 0371 * not having an available part able to display the URL or an error happening), nothing will be done (including displaying error messages). 0372 * 0373 * This variable is set if the metadata in the `KonqOpenURLRequest` passed to the constructor contains the `"EmbedOrNothing"` entry. 0374 * 0375 * @note This is a workaround to allow WebEnginePart to display a downloaded file without risking asking again the user to save 0376 * 0377 * @todo Remove this after a better way to allow the user to quickly display a file after downloading it has been implemented. See comment 0378 * for WebEnginePage::saveUrlToDiskAndDisplay 0379 */ 0380 bool m_embedOrNothing = false; 0381 0382 /** 0383 * @brief The URL that the UrlLoader has been asked to open, in case the requesting parts wants to download it itself 0384 * 0385 * If the requesting part doesn't want to download the URL itself, this is empty. Otherwise, when the UrlLoader is created, 0386 * this is the same as #m_url. After the part has finished downloading the URL, #m_url will contain the temporary local file 0387 * the URL has been downloaded to, while #m_originalUrl will not be changed 0388 */ 0389 QUrl m_originalUrl; 0390 }; 0391 0392 QDebug operator<<(QDebug dbg, UrlLoader::OpenUrlAction action); 0393 0394 #endif // URLLLOADER_H