X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/b2d4e3322acc962f2d488519956758dc4f1a2327..3e1cb2c7fb41d20f19bb039c77714e8128bf5e00:/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp diff --git a/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp b/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp index e3866a6ae..89f2545f8 100644 --- a/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp +++ b/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp @@ -20,25 +20,42 @@ #include #include +#include #include #include #include #include #include - +#include +#include #include +#include + +#include "../../../config-packagekit.h" + +const static QStringList binaryPackages = {QStringLiteral("application/vnd.debian.binary-package"), + QStringLiteral("application/x-rpm"), + QStringLiteral("application/x-xz"), + QStringLiteral("application/zstd")}; +enum PackageOperation { + Install, + Uninstall +}; + +#ifdef HAVE_PACKAGEKIT +#include +#include +#include +#else +#include +#endif // @param msg Error that gets logged to CLI Q_NORETURN void fail(const QString &str) { qCritical() << str; - - QProcess process; - auto args = QStringList{"--passivepopup", i18n("Dolphin service menu installation failed"), "15"}; - process.start("kdialog", args, QIODevice::ReadOnly); - if (!process.waitForStarted()) { - qFatal("Failed to run kdialog"); - } + const QStringList args = {"--detailederror" ,i18n("Dolphin service menu installation failed"), str}; + QProcess::startDetached("kdialog", args); exit(1); } @@ -49,6 +66,84 @@ QString getServiceMenusDir() return QDir(dataLocation).absoluteFilePath("kservices5/ServiceMenus"); } +#ifdef HAVE_PACKAGEKIT +void packageKitInstall(const QString &fileName) +{ + PackageKit::Transaction *transaction = PackageKit::Daemon::installFile(fileName); + + const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) { + fail(details); + }; + + QObject::connect(transaction, &PackageKit::Transaction::finished, + [=](PackageKit::Transaction::Exit status, uint) { + if (status == PackageKit::Transaction::ExitSuccess) { + exit(0); + } + // Fallback error handling + QTimer::singleShot(500, [=](){ + fail(i18n("Failed to install \"%1\", exited with status \"%2\"", + fileName, QVariant::fromValue(status).toString())); + }); + }); + QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); +} + +void packageKitUninstall(const QString &fileName) +{ + const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) { + fail(details); + }; + const auto uninstallLambda = [=](PackageKit::Transaction::Exit status, uint) { + if (status == PackageKit::Transaction::ExitSuccess) { + exit(0); + } + }; + + PackageKit::Transaction *transaction = PackageKit::Daemon::getDetailsLocal(fileName); + QObject::connect(transaction, &PackageKit::Transaction::details, + [=](const PackageKit::Details &details) { + PackageKit::Transaction *transaction = PackageKit::Daemon::removePackage(details.packageId()); + QObject::connect(transaction, &PackageKit::Transaction::finished, uninstallLambda); + QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); + }); + + QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); + // Fallback error handling + QObject::connect(transaction, &PackageKit::Transaction::finished, + [=](PackageKit::Transaction::Exit status, uint) { + if (status != PackageKit::Transaction::ExitSuccess) { + QTimer::singleShot(500, [=]() { + fail(i18n("Failed to uninstall \"%1\", exited with status \"%2\"", + fileName, QVariant::fromValue(status).toString())); + }); + } + }); + } +#endif + +Q_NORETURN void packageKit(PackageOperation operation, const QString &fileName) +{ +#ifdef HAVE_PACKAGEKIT + QFileInfo fileInfo(fileName); + if (!fileInfo.exists()) { + fail(i18n("The file does not exist!")); + } + const QString absPath = fileInfo.absoluteFilePath(); + if (operation == PackageOperation::Install) { + packageKitInstall(absPath); + } else { + packageKitUninstall(absPath); + } + QGuiApplication::exec(); // For event handling, no return after signals finish + fail(i18n("Unknown error when installing package")); +#else + Q_UNUSED(operation) + QDesktopServices::openUrl(QUrl(fileName)); + exit(0); +#endif +} + struct UncompressCommand { QString command; @@ -56,34 +151,39 @@ struct UncompressCommand QStringList args2; }; -void runUncompress(const QString &inputPath, const QString &outputPath) { +enum ScriptExecution{ + Process, + Konsole +}; + +void runUncompress(const QString &inputPath, const QString &outputPath) +{ QVector> mimeTypeToCommand; - mimeTypeToCommand.append({QStringList{"application/x-tar", "application/tar", "application/x-gtar", - "multipart/x-tar"}, - UncompressCommand{"tar", QStringList() << "-xf", QStringList() << "-C"}}); - mimeTypeToCommand.append({QStringList{"application/x-gzip", "application/gzip", - "application/x-gzip-compressed-tar", "application/gzip-compressed-tar", - "application/x-gzip-compressed", "application/gzip-compressed", - "application/tgz", "application/x-compressed-tar", - "application/x-compressed-gtar", "file/tgz", - "multipart/x-tar-gz", "application/x-gunzip", "application/gzipped", - "gzip/document"}, - UncompressCommand{"tar", QStringList{"-zxf"}, QStringList{"-C"}}}); - mimeTypeToCommand.append({QStringList{"application/bzip", "application/bzip2", "application/x-bzip", - "application/x-bzip2", "application/bzip-compressed", - "application/bzip2-compressed", "application/x-bzip-compressed", - "application/x-bzip2-compressed", "application/bzip-compressed-tar", - "application/bzip2-compressed-tar", "application/x-bzip-compressed-tar", - "application/x-bzip2-compressed-tar", "application/x-bz2"}, - UncompressCommand{"tar", QStringList{"-jxf"}, QStringList{"-C"}}}); - mimeTypeToCommand.append({QStringList{"application/zip", "application/x-zip", "application/x-zip-compressed", - "multipart/x-zip"}, - UncompressCommand{"unzip", QStringList{}, QStringList{"-d"}}}); + mimeTypeToCommand.append({{"application/x-tar", "application/tar", "application/x-gtar", "multipart/x-tar"}, + UncompressCommand({"tar", {"-xf"}, {"-C"}})}); + mimeTypeToCommand.append({{"application/x-gzip", "application/gzip", + "application/x-gzip-compressed-tar", "application/gzip-compressed-tar", + "application/x-gzip-compressed", "application/gzip-compressed", + "application/tgz", "application/x-compressed-tar", + "application/x-compressed-gtar", "file/tgz", + "multipart/x-tar-gz", "application/x-gunzip", "application/gzipped", + "gzip/document"}, + UncompressCommand({"tar", {"-zxf"}, {"-C"}})}); + mimeTypeToCommand.append({{"application/bzip", "application/bzip2", "application/x-bzip", + "application/x-bzip2", "application/bzip-compressed", + "application/bzip2-compressed", "application/x-bzip-compressed", + "application/x-bzip2-compressed", "application/bzip-compressed-tar", + "application/bzip2-compressed-tar", "application/x-bzip-compressed-tar", + "application/x-bzip2-compressed-tar", "application/x-bz2"}, + UncompressCommand({"tar", {"-jxf"}, {"-C"}})}); + mimeTypeToCommand.append({{"application/zip", "application/x-zip", "application/x-zip-compressed", + "multipart/x-zip"}, + UncompressCommand({"unzip", {}, {"-d"}})}); const auto mime = QMimeDatabase().mimeTypeForFile(inputPath).name(); UncompressCommand command{}; - for (const auto &pair : mimeTypeToCommand) { + for (const auto &pair : qAsConst(mimeTypeToCommand)) { if (pair.first.contains(mime)) { command = pair.second; break; @@ -123,12 +223,24 @@ QString findRecursive(const QString &dir, const QString &basename) return QString(); } -bool runInstallerScriptOnce(const QString &path, const QStringList &args) +bool runScriptOnce(const QString &path, const QStringList &args, ScriptExecution execution) { QProcess process; process.setWorkingDirectory(QFileInfo(path).absolutePath()); - process.start(path, args, QIODevice::NotOpen); + const static bool konsoleAvailable = !QStandardPaths::findExecutable("konsole").isEmpty(); + if (konsoleAvailable && execution == ScriptExecution::Konsole) { + QString bashCommand = KShell::quoteArg(path) + ' '; + if (!args.isEmpty()) { + bashCommand.append(args.join(' ')); + } + bashCommand.append("|| $SHELL"); + // If the install script fails a shell opens and the user can fix the problem + // without an error konsole closes + process.start("konsole", QStringList() << "-e" << "bash" << "-c" << bashCommand, QIODevice::NotOpen); + } else { + process.start(path, args, QIODevice::NotOpen); + } if (!process.waitForStarted()) { fail(i18n("Failed to run installer script %1", path)); } @@ -149,7 +261,7 @@ bool runInstallerScriptOnce(const QString &path, const QStringList &args) // If hasArgVariants is true, run "path". // If hasArgVariants is false, run "path argVariants[i]" until successful. -bool runInstallerScript(const QString &path, bool hasArgVariants, const QStringList &argVariants, QString &errorText) +bool runScriptVariants(const QString &path, bool hasArgVariants, const QStringList &argVariants, QString &errorText) { QFile file(path); if (!file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)) { @@ -160,14 +272,12 @@ bool runInstallerScript(const QString &path, bool hasArgVariants, const QStringL qInfo() << "[servicemenuinstaller]: Trying to run installer/uninstaller" << path; if (hasArgVariants) { for (const auto &arg : argVariants) { - if (runInstallerScriptOnce(path, QStringList{arg})) { + if (runScriptOnce(path, {arg}, ScriptExecution::Process)) { return true; } } - } else { - if (runInstallerScriptOnce(path, QStringList{})) { - return true; - } + } else if (runScriptOnce(path, {}, ScriptExecution::Konsole)) { + return true; } errorText = i18nc( @@ -201,6 +311,9 @@ bool cmdInstall(const QString &archive, QString &errorText) return false; } } else { + if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) { + packageKit(PackageOperation::Install, archive); + } const QString dir = generateDirPath(archive); if (QFile::exists(dir)) { if (!QDir(dir).removeRecursively()) { @@ -217,8 +330,8 @@ bool cmdInstall(const QString &archive, QString &errorText) // Try "install-it" first QString installItPath; - const auto basenames1 = QStringList{"install-it.sh", "install-it"}; - for (const auto &basename : qAsConst(basenames1)) { + const QStringList basenames1 = {"install-it.sh", "install-it"}; + for (const auto &basename : basenames1) { const auto path = findRecursive(dir, basename); if (!path.isEmpty()) { installItPath = path; @@ -227,13 +340,13 @@ bool cmdInstall(const QString &archive, QString &errorText) } if (!installItPath.isEmpty()) { - return runInstallerScript(installItPath, false, QStringList{}, errorText); + return runScriptVariants(installItPath, false, QStringList{}, errorText); } // If "install-it" is missing, try "install" QString installerPath; - const auto basenames2 = QStringList{"installKDE4.sh", "installKDE4", "install.sh", "install"}; - for (const auto &basename : qAsConst(basenames2)) { + const QStringList basenames2 = {"installKDE4.sh", "installKDE4", "install.sh", "install"}; + for (const auto &basename : basenames2) { const auto path = findRecursive(dir, basename); if (!path.isEmpty()) { installerPath = path; @@ -242,7 +355,11 @@ bool cmdInstall(const QString &archive, QString &errorText) } if (!installerPath.isEmpty()) { - return runInstallerScript(installerPath, true, QStringList{"--local", "--local-install", "--install"}, errorText); + // Try to run script without variants first + if (!runScriptVariants(installerPath, false, {}, errorText)) { + return runScriptVariants(installerPath, true, {"--local", "--local-install", "--install"}, errorText); + } + return true; } fail(i18n("Failed to find an installation script in %1", dir)); @@ -263,12 +380,15 @@ bool cmdUninstall(const QString &archive, QString &errorText) return false; } } else { + if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) { + packageKit(PackageOperation::Uninstall, archive); + } const QString dir = generateDirPath(archive); // Try "deinstall" first QString deinstallPath; - const auto basenames1 = QStringList{"deinstall.sh", "deinstall"}; - for (const auto &basename : qAsConst(basenames1)) { + const QStringList basenames1 = {"uninstall.sh", "uninstal", "deinstall.sh", "deinstall"}; + for (const auto &basename : basenames1) { const auto path = findRecursive(dir, basename); if (!path.isEmpty()) { deinstallPath = path; @@ -277,17 +397,16 @@ bool cmdUninstall(const QString &archive, QString &errorText) } if (!deinstallPath.isEmpty()) { - bool ok = runInstallerScript(deinstallPath, false, QStringList{}, errorText); + const bool ok = runScriptVariants(deinstallPath, false, {}, errorText); if (!ok) { return ok; } } else { // If "deinstall" is missing, try "install --uninstall" - QString installerPath; - const auto basenames2 = QStringList{"install-it.sh", "install-it", "installKDE4.sh", - "installKDE4", "install.sh", "install"}; - for (const auto &basename : qAsConst(basenames2)) { + const QStringList basenames2 = {"install-it.sh", "install-it", "installKDE4.sh", + "installKDE4", "install.sh", "install"}; + for (const auto &basename : basenames2) { const auto path = findRecursive(dir, basename); if (!path.isEmpty()) { installerPath = path; @@ -296,8 +415,8 @@ bool cmdUninstall(const QString &archive, QString &errorText) } if (!installerPath.isEmpty()) { - bool ok = runInstallerScript( - installerPath, true, QStringList{"--remove", "--delete", "--uninstall", "--deinstall"}, errorText); + const bool ok = runScriptVariants(installerPath, true, + {"--remove", "--delete", "--uninstall", "--deinstall"}, errorText); if (!ok) { return ok; } @@ -318,7 +437,7 @@ bool cmdUninstall(const QString &archive, QString &errorText) int main(int argc, char *argv[]) { - QCoreApplication app(argc, argv); + QGuiApplication app(argc, argv); QCommandLineParser parser; parser.addPositionalArgument(QStringLiteral("command"), i18nc("@info:shell", "Command to execute: install or uninstall.")); @@ -337,11 +456,11 @@ int main(int argc, char *argv[]) const QString archive = args[1]; QString errorText; - if (cmd == "install") { + if (cmd == QLatin1String("install")) { if (!cmdInstall(archive, errorText)) { fail(errorText); } - } else if (cmd == "uninstall") { + } else if (cmd == QLatin1String("uninstall")) { if (!cmdUninstall(archive, errorText)) { fail(errorText); }