File indexing completed on 2024-05-12 05:38:21

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)
0026     : KRunner::AbstractRunner(parent, metaData)
0027     , m_actionList{KRunner::Action(QStringLiteral("run-in-terminal"), QStringLiteral("utilities-terminal"), i18n("Run in Terminal Window"))}
0028     , m_matchIcon(QIcon::fromTheme(QStringLiteral("system-run")))
0029 {
0030     // If the runner is not authorized we can suspend it
0031     bool enabled = KAuthorized::authorize(QStringLiteral("run_command")) && KAuthorized::authorize(KAuthorized::SHELL_ACCESS);
0032     suspendMatching(!enabled);
0033 
0034     addSyntax(QStringLiteral(":q:"), i18n("Finds commands that match :q:, using common shell syntax"));
0035 }
0036 
0037 void ShellRunner::match(KRunner::RunnerContext &context)
0038 {
0039     QStringList envs;
0040     std::optional<QString> parsingResult = parseShellCommand(context.query(), envs);
0041     if (parsingResult.has_value()) {
0042         const QString command = parsingResult.value();
0043         KRunner::QueryMatch match(this);
0044         match.setId(QStringLiteral("exec://") + command);
0045         match.setCategoryRelevance(KRunner::QueryMatch::CategoryRelevance::Highest);
0046         match.setIcon(m_matchIcon);
0047         match.setText(i18n("Run %1", context.query()));
0048         match.setData(QVariantList({command, envs}));
0049         match.setRelevance(0.7);
0050         match.setActions(m_actionList);
0051         context.addMatch(match);
0052     }
0053 }
0054 
0055 void ShellRunner::run(const KRunner::RunnerContext &context, const KRunner::QueryMatch &match)
0056 {
0057     if (match.selectedAction()) {
0058         const QVariantList data = match.data().toList();
0059         const QStringList list = data.at(1).toStringList();
0060         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
0061         for (const QString &str : list) {
0062             const int pos = str.indexOf('=');
0063             env.insert(str.left(pos), str.mid(pos + 1));
0064         }
0065         auto job = new KTerminalLauncherJob(data.at(0).toString());
0066         job->setProcessEnvironment(env);
0067         job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
0068         job->start();
0069         return;
0070     }
0071 
0072     auto *job = new KIO::CommandLauncherJob(context.query()); // The job can handle the env parameters
0073     job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled));
0074     job->start();
0075 }
0076 
0077 std::optional<QString> ShellRunner::parseShellCommand(const QString &query, QStringList &envs)
0078 {
0079     const static QRegularExpression envRegex = QRegularExpression(QStringLiteral("^.+=.+$"));
0080     const QStringList split = KShell::splitArgs(query);
0081     for (const auto &entry : split) {
0082         const QString executablePath = QStandardPaths::findExecutable(KShell::tildeExpand(entry));
0083         if (!executablePath.isEmpty()) {
0084             QStringList executableParts{executablePath};
0085             executableParts << split.mid(split.indexOf(entry) + 1);
0086             return KShell::joinArgs(executableParts);
0087         } else if (envRegex.match(entry).hasMatch()) {
0088             envs.append(entry);
0089         } else {
0090             return std::nullopt;
0091         }
0092     }
0093     return std::nullopt;
0094 }
0095 
0096 #include "shellrunner.moc"