File indexing completed on 2024-05-19 13:25:27

0001 /*
0002     SPDX-FileCopyrightText: 2006 Aaron Seigo <aseigo@kde.org>
0003     SPDX-FileCopyrightText: 2016 Kai Uwe Broulik <kde@privat.broulik.de>
0004     SPDX-FileCopyrightText: 2020-2021 Alexander Lohnau <alexander.lonau@gmx.de>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 */
0008 
0009 #include "shellrunner.h"
0010 
0011 #include <KAuthorized>
0012 #include <KLocalizedString>
0013 #include <KNotificationJobUiDelegate>
0014 #include <KShell>
0015 #include <KTerminalLauncherJob>
0016 #include <QAction>
0017 #include <QProcessEnvironment>
0018 #include <QRegularExpression>
0019 #include <QStandardPaths>
0020 
0021 #include <KIO/CommandLauncherJob>
0022 
0023 K_PLUGIN_CLASS_WITH_JSON(ShellRunner, "plasma-runner-shell.json")
0024 
0025 ShellRunner::ShellRunner(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args)
0026     : Plasma::AbstractRunner(parent, metaData, args)
0027 {
0028     setObjectName(QStringLiteral("Command"));
0029     // The results from the services runner are preferred, consequently we set a low priority
0030     setPriority(AbstractRunner::LowestPriority);
0031     // If the runner is not authorized we can suspend it
0032     bool enabled = KAuthorized::authorize(QStringLiteral("run_command")) && KAuthorized::authorize(KAuthorized::SHELL_ACCESS);
0033     suspendMatching(!enabled);
0034 
0035     addSyntax(Plasma::RunnerSyntax(QStringLiteral(":q:"), i18n("Finds commands that match :q:, using common shell syntax")));
0036     m_actionList = {new QAction(QIcon::fromTheme(QStringLiteral("utilities-terminal")), i18n("Run in Terminal Window"), this)};
0037     m_matchIcon = QIcon::fromTheme(QStringLiteral("system-run"));
0038 }
0039 
0040 ShellRunner::~ShellRunner()
0041 {
0042 }
0043 
0044 void ShellRunner::match(Plasma::RunnerContext &context)
0045 {
0046     QStringList envs;
0047     std::optional<QString> parsingResult = parseShellCommand(context.query(), envs);
0048     if (parsingResult.has_value()) {
0049         const QString command = parsingResult.value();
0050         Plasma::QueryMatch match(this);
0051         match.setId(QStringLiteral("exec://") + command);
0052         match.setType(Plasma::QueryMatch::ExactMatch);
0053         match.setIcon(m_matchIcon);
0054         match.setText(i18n("Run %1", context.query()));
0055         match.setData(QVariantList({command, envs}));
0056         match.setRelevance(0.7);
0057         match.setActions(m_actionList);
0058         context.addMatch(match);
0059     }
0060 }
0061 
0062 void ShellRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match)
0063 {
0064     if (match.selectedAction()) {
0065         const QVariantList data = match.data().toList();
0066         const QStringList list = data.at(1).toStringList();
0067         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
0068         for (const auto &str : list) {
0069             const int pos = str.indexOf('=');
0070             env.insert(str.left(pos), str.mid(pos + 1));
0071         }
0072         auto job = new KTerminalLauncherJob(data.at(0).toString());
0073         job->setProcessEnvironment(env);
0074         job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
0075         job->start();
0076         return;
0077     }
0078 
0079     auto *job = new KIO::CommandLauncherJob(context.query()); // The job can handle the env parameters
0080     job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
0081     job->start();
0082 }
0083 
0084 std::optional<QString> ShellRunner::parseShellCommand(const QString &query, QStringList &envs)
0085 {
0086     const static QRegularExpression envRegex = QRegularExpression(QStringLiteral("^.+=.+$"));
0087     const QStringList split = KShell::splitArgs(query);
0088     for (const auto &entry : split) {
0089         const QString executablePath = QStandardPaths::findExecutable(KShell::tildeExpand(entry));
0090         if (!executablePath.isEmpty()) {
0091             QStringList executableParts{executablePath};
0092             executableParts << split.mid(split.indexOf(entry) + 1);
0093             return KShell::joinArgs(executableParts);
0094         } else if (envRegex.match(entry).hasMatch()) {
0095             envs.append(entry);
0096         } else {
0097             return std::nullopt;
0098         }
0099     }
0100     return std::nullopt;
0101 }
0102 
0103 #include "shellrunner.moc"