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"