File indexing completed on 2024-04-21 03:54:56

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