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"