File indexing completed on 2024-04-14 14:24:06

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "openurljobtest.h"
0009 #include "openurljob.h"
0010 #include <KApplicationTrader>
0011 #include <kprocessrunner_p.h>
0012 
0013 #include "kiotesthelper.h" // createTestFile etc.
0014 #include "mockcoredelegateextensions.h"
0015 #include "mockguidelegateextensions.h"
0016 
0017 #include <KConfigGroup>
0018 #include <KDesktopFile>
0019 #include <KJobUiDelegate>
0020 #include <KService>
0021 
0022 #ifdef Q_OS_UNIX
0023 #include <signal.h> // kill
0024 #endif
0025 
0026 #include <KSharedConfig>
0027 #include <QStandardPaths>
0028 #include <QTemporaryFile>
0029 #include <QTest>
0030 
0031 QTEST_GUILESS_MAIN(OpenUrlJobTest)
0032 
0033 extern KSERVICE_EXPORT int ksycoca_ms_between_checks;
0034 extern bool openurljob_force_use_browserapp_kdeglobals; // From openurljob.cpp
0035 
0036 static const char s_tempServiceName[] = "openurljobtest_service.desktop";
0037 
0038 void OpenUrlJobTest::initTestCase()
0039 {
0040     QStandardPaths::setTestModeEnabled(true);
0041 
0042     // Ensure no leftovers from other tests
0043     QDir(QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation)).removeRecursively();
0044     // (including a mimeapps.list file)
0045     // Don't remove ConfigLocation completely, it's useful when enabling debug output with kdebugsettings --test-mode
0046     const QString mimeApps = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1String("/mimeapps.list");
0047     QFile::remove(mimeApps);
0048 
0049     ksycoca_ms_between_checks = 0; // need it to check the ksycoca mtime
0050     m_fakeService = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation) + QLatin1Char('/') + s_tempServiceName;
0051     // not using %d because of remote urls
0052     const QByteArray cmd = QByteArray("echo %u > " + QFile::encodeName(m_tempDir.path()) + "/dest");
0053     writeApplicationDesktopFile(m_fakeService, cmd);
0054     m_fakeService = QFileInfo(m_fakeService).canonicalFilePath();
0055     m_filesToRemove.append(m_fakeService);
0056 
0057     // Ensure our service is the preferred one
0058     KConfig mimeAppsCfg(mimeApps);
0059     KConfigGroup grp = mimeAppsCfg.group("Default Applications");
0060     grp.writeEntry("text/plain", s_tempServiceName);
0061     grp.writeEntry("text/html", s_tempServiceName);
0062     grp.sync();
0063 
0064     // "text/plain" encompasses all scripts (shell, python, perl)
0065     KService::Ptr preferredTextEditor = KApplicationTrader::preferredService(QStringLiteral("text/plain"));
0066     QVERIFY(preferredTextEditor);
0067     QCOMPARE(preferredTextEditor->entryPath(), m_fakeService);
0068 
0069     // As used for preferredService
0070     QVERIFY(KService::serviceByDesktopName("openurljobtest_service"));
0071 
0072     ksycoca_ms_between_checks = 5000; // all done, speed up again
0073 }
0074 
0075 void OpenUrlJobTest::cleanupTestCase()
0076 {
0077     for (const QString &file : std::as_const(m_filesToRemove)) {
0078         QFile::remove(file);
0079     };
0080 }
0081 
0082 void OpenUrlJobTest::init()
0083 {
0084     QFile::remove(m_tempDir.path() + "/dest");
0085 }
0086 
0087 static void createSrcFile(const QString &path)
0088 {
0089     QFile srcFile(path);
0090     QVERIFY2(srcFile.open(QFile::WriteOnly), qPrintable(srcFile.errorString()));
0091     srcFile.write("Hello world\n");
0092 }
0093 
0094 static QString readFile(const QString &path)
0095 {
0096     QFile file(path);
0097     file.open(QIODevice::ReadOnly);
0098     return QString::fromLocal8Bit(file.readAll()).trimmed();
0099 }
0100 
0101 void OpenUrlJobTest::startProcess_data()
0102 {
0103     QTest::addColumn<QString>("mimeType");
0104     QTest::addColumn<QString>("fileName");
0105 
0106     // Known MIME type
0107     QTest::newRow("text_file") << "text/plain"
0108                                << "srcfile.txt";
0109     QTest::newRow("directory_file") << "application/x-desktop"
0110                                     << ".directory";
0111     QTest::newRow("desktop_file_link") << "application/x-desktop"
0112                                        << "srcfile.txt";
0113     QTest::newRow("desktop_file_link_preferred_service") << "application/x-desktop"
0114                                                          << "srcfile.html";
0115     QTest::newRow("non_executable_script_running_not_allowed") << "application/x-shellscript"
0116                                                                << "srcfile.sh";
0117     QTest::newRow("executable_script_running_not_allowed") << "application/x-shellscript"
0118                                                            << "srcfile.sh";
0119 
0120     // Require MIME type determination
0121     QTest::newRow("text_file_no_mimetype") << QString() << "srcfile.txt";
0122     QTest::newRow("directory_file_no_mimetype") << QString() << ".directory";
0123 }
0124 
0125 void OpenUrlJobTest::startProcess()
0126 {
0127     QFETCH(QString, mimeType);
0128     QFETCH(QString, fileName);
0129 
0130     // Given a file to open
0131     QTemporaryDir tempDir;
0132     const QString srcDir = tempDir.path();
0133     const QString srcFile = srcDir + QLatin1Char('/') + fileName;
0134     createSrcFile(srcFile);
0135     QVERIFY(QFile::exists(srcFile));
0136     const bool isLink = QByteArray(QTest::currentDataTag()).startsWith("desktop_file_link");
0137     QUrl url = QUrl::fromLocalFile(srcFile);
0138     if (isLink) {
0139         const QString desktopFilePath = srcDir + QLatin1String("/link.desktop");
0140         KDesktopFile linkDesktopFile(desktopFilePath);
0141         linkDesktopFile.desktopGroup().writeEntry("Type", "Link");
0142         linkDesktopFile.desktopGroup().writeEntry("URL", url);
0143         const bool linkHasPreferredService = QByteArray(QTest::currentDataTag()) == "desktop_file_link_preferred_service";
0144         if (linkHasPreferredService) {
0145             linkDesktopFile.desktopGroup().writeEntry("X-KDE-LastOpenedWith", "openurljobtest_service");
0146         }
0147         url = QUrl::fromLocalFile(desktopFilePath);
0148     }
0149     if (QByteArray(QTest::currentDataTag()).startsWith("executable")) {
0150         QFile file(srcFile);
0151         QVERIFY(file.setPermissions(QFile::ExeUser | file.permissions()));
0152         // Note however that running executables is not allowed by the OpenUrlJob below
0153         // so this will end up opening it as a text file anyway.
0154     }
0155 
0156     // When running a OpenUrlJob
0157     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url, mimeType, this);
0158     QVERIFY2(job->exec(), qPrintable(job->errorString()));
0159 
0160     // Then m_fakeService should be executed, since it's associated with text/plain
0161     // We can find out that it was executed because it writes to "dest".
0162     const QString dest = m_tempDir.path() + "/dest";
0163     QTRY_VERIFY2(QFile::exists(dest), qPrintable(dest));
0164     QCOMPARE(readFile(dest), srcFile);
0165 }
0166 
0167 void OpenUrlJobTest::noServiceNoHandler()
0168 {
0169     QTemporaryFile tempFile;
0170     QVERIFY(tempFile.open());
0171     const QUrl url = QUrl::fromLocalFile(tempFile.fileName());
0172     const QString mimeType = QStringLiteral("application/x-zerosize");
0173     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url, mimeType, this);
0174     // This is going to try QDesktopServices::openUrl which will fail because we are no QGuiApplication, good.
0175     QTest::ignoreMessage(QtWarningMsg, "QDesktopServices::openUrl: Application is not a GUI application");
0176     QVERIFY(!job->exec());
0177     QCOMPARE(job->error(), KJob::UserDefinedError);
0178     QCOMPARE(job->errorString(), QStringLiteral("Failed to open the file."));
0179 }
0180 
0181 void OpenUrlJobTest::invalidUrl()
0182 {
0183     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl(":/"), QStringLiteral("text/plain"), this);
0184     QVERIFY(!job->exec());
0185     QCOMPARE(job->error(), KIO::ERR_MALFORMED_URL);
0186     QCOMPARE(job->errorString(), QStringLiteral("Malformed URL\nRelative URL's path component contains ':' before any '/'; source was \":/\"; path = \":/\""));
0187 
0188     QUrl u;
0189     u.setPath(QStringLiteral("/pathonly"));
0190     KIO::OpenUrlJob *job2 = new KIO::OpenUrlJob(u, QStringLiteral("text/plain"), this);
0191     QVERIFY(!job2->exec());
0192     QCOMPARE(job2->error(), KIO::ERR_MALFORMED_URL);
0193     QCOMPARE(job2->errorString(), QStringLiteral("Malformed URL\n/pathonly"));
0194 }
0195 
0196 void OpenUrlJobTest::refuseRunningLocalBinaries_data()
0197 {
0198     QTest::addColumn<QString>("mimeType");
0199 
0200     // Executables under e.g. /usr/bin/ can be either of these two MIME types
0201     // see https://gitlab.freedesktop.org/xdg/shared-mime-info/-/issues/11
0202     QTest::newRow("x-sharedlib") << "application/x-sharedlib";
0203     QTest::newRow("x-executable") << "application/x-executable";
0204 
0205     QTest::newRow("msdos_executable") << "application/x-ms-dos-executable";
0206 }
0207 
0208 void OpenUrlJobTest::refuseRunningLocalBinaries()
0209 {
0210     QFETCH(QString, mimeType);
0211 
0212     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()), mimeType, this);
0213     QVERIFY(!job->exec());
0214     QCOMPARE(job->error(), KJob::UserDefinedError);
0215     QVERIFY2(job->errorString().contains("For security reasons, launching executables is not allowed in this context."), qPrintable(job->errorString()));
0216 }
0217 
0218 void OpenUrlJobTest::refuseRunningRemoteNativeExecutables_data()
0219 {
0220     QTest::addColumn<QString>("mimeType");
0221     QTest::newRow("x-sharedlib") << "application/x-sharedlib";
0222     QTest::newRow("x-executable") << "application/x-executable";
0223 }
0224 
0225 void OpenUrlJobTest::refuseRunningRemoteNativeExecutables()
0226 {
0227     QFETCH(QString, mimeType);
0228 
0229     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl("protocol://host/path/exe"), mimeType, this);
0230     job->setRunExecutables(true); // even with this enabled, an error will occur
0231     QVERIFY(!job->exec());
0232     QCOMPARE(job->error(), KJob::UserDefinedError);
0233     QVERIFY2(job->errorString().contains("is located on a remote filesystem. For safety reasons it will not be started"), qPrintable(job->errorString()));
0234 }
0235 
0236 KCONFIGCORE_EXPORT void loadUrlActionRestrictions(const KConfigGroup &cg);
0237 
0238 void OpenUrlJobTest::notAuthorized()
0239 {
0240     KConfigGroup cg(KSharedConfig::openConfig(), "KDE URL Restrictions");
0241     cg.writeEntry("rule_count", 1);
0242     cg.writeEntry("rule_1", QStringList{"open", {}, {}, {}, "file", "", "", "false"});
0243     cg.sync();
0244     loadUrlActionRestrictions(cg);
0245 
0246     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl("file:///"), QStringLiteral("text/plain"), this);
0247     QVERIFY(!job->exec());
0248     QCOMPARE(job->error(), KIO::ERR_ACCESS_DENIED);
0249     QCOMPARE(job->errorString(), QStringLiteral("Access denied to file:///."));
0250 
0251     cg.deleteEntry("rule_1");
0252     cg.deleteEntry("rule_count");
0253     cg.sync();
0254     loadUrlActionRestrictions(cg);
0255 }
0256 
0257 void OpenUrlJobTest::runScript_data()
0258 {
0259     QTest::addColumn<QString>("mimeType");
0260 
0261     // All text-based scripts inherit text/plain and application/x-executable, no need to test
0262     // all flavours (python, perl, lua, awk ...etc), this sample should be enough
0263     QTest::newRow("shellscript") << "application/x-shellscript";
0264     QTest::newRow("pythonscript") << "text/x-python";
0265     QTest::newRow("javascript") << "application/javascript";
0266 }
0267 
0268 void OpenUrlJobTest::runScript()
0269 {
0270 #ifdef Q_OS_UNIX
0271     QFETCH(QString, mimeType);
0272 
0273     // Given an executable shell script that copies "src" to "dest"
0274     QTemporaryDir tempDir;
0275     const QString dir = tempDir.path();
0276     createSrcFile(dir + QLatin1String("/src"));
0277     const QString scriptFile = dir + QLatin1String("/script.sh");
0278     QFile file(scriptFile);
0279     QVERIFY(file.open(QIODevice::WriteOnly));
0280     file.write("#!/bin/sh\ncp src dest");
0281     file.close();
0282     QVERIFY(file.setPermissions(QFile::ExeUser | file.permissions()));
0283 
0284     // When using OpenUrlJob to run the script
0285     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(scriptFile), mimeType, this);
0286     job->setRunExecutables(true); // startProcess and refuseRunningLocalBinaries test the case where this isn't set
0287 
0288     // Then it works :-)
0289     QVERIFY2(job->exec(), qPrintable(job->errorString()));
0290     QTRY_VERIFY(QFileInfo::exists(dir + QLatin1String("/dest"))); // TRY because CommandLineLauncherJob finishes immediately
0291 #endif
0292 }
0293 
0294 void OpenUrlJobTest::runNativeExecutable_data()
0295 {
0296     QTest::addColumn<QString>("mimeType");
0297     QTest::addColumn<bool>("withHandler");
0298     QTest::addColumn<bool>("handlerRetVal");
0299 
0300     QTest::newRow("no_handler_x-sharedlib") << "application/x-sharedlib" << false << false;
0301     QTest::newRow("handler_false_x-sharedlib") << "application/x-sharedlib" << true << false;
0302     QTest::newRow("handler_true_x-sharedlib") << "application/x-sharedlib" << true << true;
0303 
0304     QTest::newRow("no_handler_x-executable") << "application/x-executable" << false << false;
0305     QTest::newRow("handler_false_x-executable") << "application/x-executable" << true << false;
0306     QTest::newRow("handler_true_x-executable") << "application/x-executable" << true << true;
0307 }
0308 
0309 void OpenUrlJobTest::runNativeExecutable()
0310 {
0311     QFETCH(QString, mimeType);
0312     QFETCH(bool, withHandler);
0313     QFETCH(bool, handlerRetVal);
0314 
0315 #ifdef Q_OS_UNIX
0316     // Given an executable shell script that copies "src" to "dest" (we'll cheat with the MIME type to treat it like a native binary)
0317     QTemporaryDir tempDir;
0318     const QString dir = tempDir.path();
0319     createSrcFile(dir + QLatin1String("/src"));
0320     const QString scriptFile = dir + QLatin1String("/script.sh");
0321     QFile file(scriptFile);
0322     QVERIFY(file.open(QIODevice::WriteOnly));
0323     file.write("#!/bin/sh\ncp src dest");
0324     file.close();
0325     // Note that it's missing executable permissions
0326 
0327     // When using OpenUrlJob to run the executable
0328     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(scriptFile), mimeType, this);
0329     job->setRunExecutables(true); // startProcess tests the case where this isn't set
0330     job->setUiDelegate(new KJobUiDelegate);
0331 
0332     // Then --- it depends on what the user says via the handler
0333     if (!withHandler) {
0334         QVERIFY(!job->exec());
0335         QCOMPARE((int)job->error(), (int)KJob::UserDefinedError);
0336         QCOMPARE(job->errorString(), QStringLiteral("The program \"%1\" needs to have executable permission before it can be launched.").arg(scriptFile));
0337     } else {
0338         auto *handler = new MockUntrustedProgramHandler(job->uiDelegate());
0339         handler->setRetVal(handlerRetVal);
0340 
0341         const bool success = job->exec();
0342         if (handlerRetVal) {
0343             QVERIFY(success);
0344             QTRY_VERIFY(QFileInfo::exists(dir + QLatin1String("/dest"))); // TRY because CommandLineLauncherJob finishes immediately
0345         } else {
0346             QVERIFY(!success);
0347             QCOMPARE((int)job->error(), (int)KIO::ERR_USER_CANCELED);
0348         }
0349     }
0350 #endif
0351 }
0352 
0353 void OpenUrlJobTest::openOrExecuteScript_data()
0354 {
0355     QTest::addColumn<QString>("dialogResult");
0356 
0357     QTest::newRow("execute_true") << "execute_true";
0358     QTest::newRow("execute_false") << "execute_false";
0359     QTest::newRow("canceled") << "canceled";
0360 }
0361 
0362 void OpenUrlJobTest::openOrExecuteScript()
0363 {
0364 #ifdef Q_OS_UNIX
0365     QFETCH(QString, dialogResult);
0366 
0367     // Given an executable shell script that copies "src" to "dest"
0368     QTemporaryDir tempDir;
0369     const QString dir = tempDir.path();
0370     createSrcFile(dir + QLatin1String("/src"));
0371     const QString scriptFile = dir + QLatin1String("/script.sh");
0372     QFile file(scriptFile);
0373     QVERIFY(file.open(QIODevice::WriteOnly));
0374     file.write("#!/bin/sh\ncp src dest");
0375     file.close();
0376     // Set the executable bit, because OpenUrlJob will always open shell
0377     // scripts that are not executable as text files
0378     QVERIFY(file.setPermissions(QFile::ExeUser | file.permissions()));
0379 
0380     // When using OpenUrlJob to open the script
0381     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(scriptFile), QStringLiteral("application/x-shellscript"), this);
0382     job->setShowOpenOrExecuteDialog(true);
0383     job->setUiDelegate(new KJobUiDelegate);
0384     auto *openOrExecuteFileHandler = new MockOpenOrExecuteHandler(job->uiDelegate());
0385 
0386     // Then --- it depends on what the user says via the handler
0387     if (dialogResult == QLatin1String("execute_true")) {
0388         job->setRunExecutables(false); // Overridden by the user's choice
0389         openOrExecuteFileHandler->setExecuteFile(true);
0390         QVERIFY(job->exec());
0391         // TRY because CommandLineLauncherJob finishes immediately, and tempDir
0392         // will go out of scope and get deleted before the copy operation actually finishes
0393         QTRY_VERIFY(QFileInfo::exists(dir + QLatin1String("/dest")));
0394     } else if (dialogResult == QLatin1String("execute_false")) {
0395         job->setRunExecutables(true); // Overridden by the user's choice
0396         openOrExecuteFileHandler->setExecuteFile(false);
0397         QVERIFY(job->exec());
0398         const QString testOpen = m_tempDir.path() + QLatin1String("/dest"); // see the .desktop file in writeApplicationDesktopFile
0399         QTRY_VERIFY(QFileInfo::exists(testOpen));
0400     } else if (dialogResult == QLatin1String("canceled")) {
0401         openOrExecuteFileHandler->setCanceled();
0402         QVERIFY(!job->exec());
0403         QCOMPARE(job->error(), KIO::ERR_USER_CANCELED);
0404     }
0405 #endif
0406 }
0407 
0408 void OpenUrlJobTest::openOrExecuteDesktop_data()
0409 {
0410     QTest::addColumn<QString>("dialogResult");
0411 
0412     QTest::newRow("execute_true") << "execute_true";
0413     QTest::newRow("execute_false") << "execute_false";
0414     QTest::newRow("canceled") << "canceled";
0415 }
0416 
0417 void OpenUrlJobTest::openOrExecuteDesktop()
0418 {
0419 #ifdef Q_OS_UNIX
0420     QFETCH(QString, dialogResult);
0421 
0422     // Given a .desktop file, with an Exec line that copies "src" to "dest"
0423     QTemporaryDir tempDir;
0424     const QString dir = tempDir.path();
0425     const QString desktopFile = dir + QLatin1String("/testopenorexecute.desktop");
0426     createSrcFile(dir + QLatin1String("/src"));
0427     const QByteArray cmd("cp " + QFile::encodeName(dir) + "/src " + QFile::encodeName(dir) + "/dest-open-or-execute-desktop");
0428     writeApplicationDesktopFile(desktopFile, cmd);
0429     QFile file(desktopFile);
0430     QVERIFY(file.setPermissions(QFile::ExeUser | file.permissions())); // otherwise we'll get the untrusted program warning
0431 
0432     // When using OpenUrlJob to open the .desktop file
0433     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(desktopFile), QStringLiteral("application/x-desktop"), this);
0434     job->setShowOpenOrExecuteDialog(true);
0435     job->setUiDelegate(new KJobUiDelegate);
0436     auto *openOrExecuteFileHandler = new MockOpenOrExecuteHandler(job->uiDelegate());
0437 
0438     // Then --- it depends on what the user says via the handler
0439     if (dialogResult == QLatin1String("execute_true")) {
0440         job->setRunExecutables(false); // Overridden by the user's choice
0441         openOrExecuteFileHandler->setExecuteFile(true);
0442         QVERIFY2(job->exec(), qPrintable(job->errorString()));
0443         // TRY because CommandLineLauncherJob finishes immediately, and tempDir
0444         // will go out of scope and get deleted before the copy operation actually finishes
0445         QTRY_VERIFY(QFileInfo::exists(dir + QLatin1String("/dest-open-or-execute-desktop")));
0446     }
0447     if (dialogResult == QLatin1String("execute_false")) {
0448         job->setRunExecutables(true); // Overridden by the user's choice
0449         openOrExecuteFileHandler->setExecuteFile(false);
0450         QVERIFY2(job->exec(), qPrintable(job->errorString()));
0451         const QString testOpen = m_tempDir.path() + QLatin1String("/dest"); // see the .desktop file in writeApplicationDesktopFile
0452         QTRY_VERIFY(QFileInfo::exists(testOpen));
0453     } else if (dialogResult == QLatin1String("canceled")) {
0454         openOrExecuteFileHandler->setCanceled();
0455         QVERIFY(!job->exec());
0456         QCOMPARE(job->error(), KIO::ERR_USER_CANCELED);
0457     }
0458 #endif
0459 }
0460 
0461 void OpenUrlJobTest::launchExternalBrowser_data()
0462 {
0463     QTest::addColumn<bool>("useBrowserApp");
0464     QTest::addColumn<bool>("useSchemeHandler");
0465 
0466     QTest::newRow("browserapp") << true << false;
0467     QTest::newRow("scheme_handler") << false << true;
0468 }
0469 
0470 void OpenUrlJobTest::launchExternalBrowser()
0471 {
0472 #ifdef Q_OS_UNIX
0473     QFETCH(bool, useBrowserApp);
0474     QFETCH(bool, useSchemeHandler);
0475 
0476     QTemporaryDir tempDir;
0477     const QString dir = tempDir.path();
0478     createSrcFile(dir + QLatin1String("/src"));
0479     const QString scriptFile = dir + QLatin1String("/browser.sh");
0480     QFile file(scriptFile);
0481     QVERIFY(file.open(QIODevice::WriteOnly));
0482     file.write("#!/bin/sh\necho $1 > `dirname $0`/destbrowser");
0483     file.close();
0484     QVERIFY(file.setPermissions(QFile::ExeUser | file.permissions()));
0485 
0486     QUrl remoteImage("http://example.org/image.jpg");
0487     if (useBrowserApp) {
0488         openurljob_force_use_browserapp_kdeglobals = true;
0489         KConfigGroup(KSharedConfig::openConfig(), "General").writeEntry("BrowserApplication", QString(QLatin1Char('!') + scriptFile));
0490     } else if (useSchemeHandler) {
0491         openurljob_force_use_browserapp_kdeglobals = false;
0492         remoteImage.setScheme("scheme");
0493     }
0494 
0495     // When using OpenUrlJob to run the script
0496     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(remoteImage, this);
0497 
0498     // Then it works :-)
0499     QVERIFY2(job->exec(), qPrintable(job->errorString()));
0500     QString dest;
0501     if (useBrowserApp) {
0502         dest = dir + QLatin1String("/destbrowser");
0503     } else if (useSchemeHandler) {
0504         dest = m_tempDir.path() + QLatin1String("/dest"); // see the .desktop file in writeApplicationDesktopFile
0505     }
0506     QTRY_VERIFY(QFileInfo::exists(dest)); // TRY because CommandLineLauncherJob finishes immediately
0507     QCOMPARE(readFile(dest), remoteImage.toString());
0508 
0509     // Restore settings
0510     KConfigGroup(KSharedConfig::openConfig(), "General").deleteEntry("BrowserApplication");
0511 #endif
0512 }
0513 
0514 void OpenUrlJobTest::nonExistingFile()
0515 {
0516     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(QStringLiteral("/does/not/exist")), this);
0517     QVERIFY(!job->exec());
0518     QCOMPARE(job->error(), KIO::ERR_DOES_NOT_EXIST);
0519     QCOMPARE(job->errorString(), "The file or folder /does/not/exist does not exist.");
0520 }
0521 
0522 void OpenUrlJobTest::httpUrlWithKIO()
0523 {
0524     // This tests the scanFileWithGet() code path
0525     const QUrl url(QStringLiteral("http://www.google.com/"));
0526     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url, this);
0527     job->setEnableExternalBrowser(false);
0528     job->setFollowRedirections(false);
0529     QVERIFY2(job->exec(), qPrintable(job->errorString()));
0530 
0531     // Then the service should be executed (which writes to "dest")
0532     const QString dest = m_tempDir.path() + "/dest";
0533     QTRY_VERIFY2(QFile::exists(dest), qPrintable(dest));
0534     QCOMPARE(readFile(dest), url.toString());
0535 }
0536 
0537 void OpenUrlJobTest::ftpUrlWithKIO()
0538 {
0539     // This is just to test the statFile() code at least a bit
0540     const QUrl url(QStringLiteral("ftp://localhost:2")); // unlikely that anything is running on that port
0541     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url, this);
0542     QVERIFY(!job->exec());
0543     QVERIFY(job->errorString() == QLatin1String("Could not connect to host localhost: Connection refused.")
0544             || job->errorString() == QLatin1String("Could not connect to host localhost: Network unreachable."));
0545 }
0546 
0547 void OpenUrlJobTest::takeOverAfterMimeTypeFound()
0548 {
0549     // Given a local image file
0550     QTemporaryDir tempDir;
0551     const QString srcDir = tempDir.path();
0552     const QString srcFile = srcDir + QLatin1String("/image.jpg");
0553     createSrcFile(srcFile);
0554 
0555     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(srcFile), this);
0556     QString foundMime = QStringLiteral("NONE");
0557     connect(job, &KIO::OpenUrlJob::mimeTypeFound, this, [&](const QString &mimeType) {
0558         foundMime = mimeType;
0559         job->kill();
0560     });
0561     QVERIFY(!job->exec());
0562     QCOMPARE(job->error(), KJob::KilledJobError);
0563     QCOMPARE(foundMime, "image/jpeg");
0564 }
0565 
0566 void OpenUrlJobTest::runDesktopFileDirectly()
0567 {
0568     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(m_fakeService), this);
0569     job->setRunExecutables(true);
0570     QVERIFY(job->exec());
0571 
0572     const QString dest = m_tempDir.path() + "/dest";
0573     QTRY_VERIFY2(QFile::exists(dest), qPrintable(dest));
0574     QCOMPARE(readFile(dest), QString{});
0575 }
0576 
0577 void OpenUrlJobTest::writeApplicationDesktopFile(const QString &filePath, const QByteArray &command)
0578 {
0579     KDesktopFile file(filePath);
0580     KConfigGroup group = file.desktopGroup();
0581     group.writeEntry("Name", "KRunUnittestService");
0582     group.writeEntry("MimeType", "text/plain;application/x-shellscript;x-scheme-handler/scheme");
0583     group.writeEntry("Type", "Application");
0584     group.writeEntry("Exec", command);
0585     QVERIFY(file.sync());
0586 }
0587 
0588 #include "moc_openurljobtest.cpp"