|
|
|
|
@ -36,16 +36,28 @@ |
|
|
|
|
#include "pass.h" |
|
|
|
|
#include "config.h" |
|
|
|
|
|
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
#include <KRunner/Action> |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
using namespace std; |
|
|
|
|
|
|
|
|
|
K_PLUGIN_CLASS_WITH_JSON(Pass, "pass.json") |
|
|
|
|
|
|
|
|
|
Pass::Pass(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) |
|
|
|
|
: Plasma::AbstractRunner(parent, metaData, args) |
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
: KRunner::AbstractRunner(metaData, parent) |
|
|
|
|
#else |
|
|
|
|
: KRunner::AbstractRunner(parent, metaData) |
|
|
|
|
#endif |
|
|
|
|
{ |
|
|
|
|
Q_UNUSED(args) |
|
|
|
|
|
|
|
|
|
// General runner configuration
|
|
|
|
|
setObjectName(QStringLiteral("Pass")); |
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
setPriority(HighestPriority); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Pass::~Pass() = default; |
|
|
|
|
@ -58,6 +70,9 @@ void Pass::reloadConfiguration() |
|
|
|
|
KConfigGroup cfg = config(); |
|
|
|
|
cfg.config()->reparseConfiguration(); // Just to be sure
|
|
|
|
|
this->showActions = cfg.readEntry(Config::showActions, false); |
|
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
uint32_t actionIdCounter = 0; |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
if (showActions) { |
|
|
|
|
const auto configActions = cfg.group(Config::Group::Actions); |
|
|
|
|
@ -67,10 +82,16 @@ void Pass::reloadConfiguration() |
|
|
|
|
// FIXME how to fallback?
|
|
|
|
|
auto passAction = PassAction::fromConfig(group); |
|
|
|
|
|
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
auto icon = QIcon::fromTheme(passAction.icon, QIcon::fromTheme("object-unlocked")); |
|
|
|
|
//KRunner::Action(actionId, QStringLiteral("process-stop"), i18n("Send SIGKILL")),
|
|
|
|
|
auto *act = new QAction(icon, passAction.name, this); |
|
|
|
|
act->setData(passAction.regex); |
|
|
|
|
#else |
|
|
|
|
actionIdCounter++; |
|
|
|
|
auto *act = new KRunner::Action(passAction.regex, QIcon::hasThemeIcon(passAction.icon) ? |
|
|
|
|
passAction.icon : QStringLiteral("object-unlocked"), passAction.name); |
|
|
|
|
#endif |
|
|
|
|
this->orderedActions << act; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -79,16 +100,20 @@ void Pass::reloadConfiguration() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (cfg.readEntry(Config::showFileContentAction, false)) { |
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
auto *act = new QAction(QIcon::fromTheme("document-new"), |
|
|
|
|
i18n("Show password file contents"), this); |
|
|
|
|
act->setData(Config::showFileContentAction); |
|
|
|
|
#else |
|
|
|
|
auto *act = new KRunner::Action(Config::showFileContentAction, "document-new", i18n("Show password file contents")); |
|
|
|
|
#endif |
|
|
|
|
this->orderedActions << act; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
addSyntax(Plasma::RunnerSyntax(QString(":q:"), |
|
|
|
|
addSyntax(KRunner::RunnerSyntax(QString(":q:"), |
|
|
|
|
i18n("Looks for a password matching :q:. Pressing ENTER copies the password to the clipboard."))); |
|
|
|
|
|
|
|
|
|
addSyntax(Plasma::RunnerSyntax(QString("pass :q:"), |
|
|
|
|
addSyntax(KRunner::RunnerSyntax(QString("pass :q:"), |
|
|
|
|
i18n("Looks for a password matching :q:. This way you avoid results from other runners"))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -153,7 +178,7 @@ void Pass::reinitPasswords(const QString &path) |
|
|
|
|
lock.unlock(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Pass::match(Plasma::RunnerContext &context) |
|
|
|
|
void Pass::match(KRunner::RunnerContext &context) |
|
|
|
|
{ |
|
|
|
|
if (!context.isValid()) { |
|
|
|
|
return; |
|
|
|
|
@ -167,14 +192,14 @@ void Pass::match(Plasma::RunnerContext &context) |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
QList<Plasma::QueryMatch> matches; |
|
|
|
|
QList<KRunner::QueryMatch> matches; |
|
|
|
|
|
|
|
|
|
lock.lockForRead(); |
|
|
|
|
for (const auto &password: qAsConst(passwords)) { |
|
|
|
|
if (password.contains(input, Qt::CaseInsensitive)) { |
|
|
|
|
Plasma::QueryMatch match(this); |
|
|
|
|
match.setCategoryRelevance(input.length() == password.length() ? Plasma::QueryMatch::CategoryRelevance::Highest : |
|
|
|
|
Plasma::QueryMatch::CategoryRelevance::Moderate); |
|
|
|
|
KRunner::QueryMatch match(this); |
|
|
|
|
match.setCategoryRelevance(input.length() == password.length() ? KRunner::QueryMatch::CategoryRelevance::Highest : |
|
|
|
|
KRunner::QueryMatch::CategoryRelevance::Moderate); |
|
|
|
|
match.setIcon(QIcon::fromTheme("object-locked")); |
|
|
|
|
match.setText(password); |
|
|
|
|
matches.append(match); |
|
|
|
|
@ -198,7 +223,7 @@ void Pass::clip(const QString &msg) |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void Pass::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &match) |
|
|
|
|
void Pass::run(const KRunner::RunnerContext &context, const KRunner::QueryMatch &match) |
|
|
|
|
{ |
|
|
|
|
Q_UNUSED(context); |
|
|
|
|
const auto regexp = QRegularExpression("^" + QRegularExpression::escape(this->passOtpIdentifier) + ".*"); |
|
|
|
|
@ -218,8 +243,13 @@ void Pass::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &m |
|
|
|
|
|
|
|
|
|
if (exitCode == 0) { |
|
|
|
|
const auto output = pass->readAllStandardOutput(); |
|
|
|
|
if (match.selectedAction() != nullptr) { |
|
|
|
|
const auto data = match.selectedAction()->data().toString(); |
|
|
|
|
if (match.selectedAction()) { |
|
|
|
|
const auto data = |
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
match.selectedAction()->data().toString(); |
|
|
|
|
#else |
|
|
|
|
match.selectedAction().id(); |
|
|
|
|
#endif |
|
|
|
|
if (data == Config::showFileContentAction) { |
|
|
|
|
QMessageBox::information(nullptr, match.text(), output); |
|
|
|
|
} else { |
|
|
|
|
@ -228,7 +258,12 @@ void Pass::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &m |
|
|
|
|
|
|
|
|
|
if (matchre.hasMatch()) { |
|
|
|
|
clip(matchre.captured(1)); |
|
|
|
|
this->showNotification(match.text(), match.selectedAction()->text()); |
|
|
|
|
this->showNotification(match.text(), |
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
match.selectedAction()->text()); |
|
|
|
|
#else |
|
|
|
|
match.selectedAction().text()); |
|
|
|
|
#endif |
|
|
|
|
} else { |
|
|
|
|
// Show some information to understand what went wrong.
|
|
|
|
|
qInfo() << "Regexp: " << data; |
|
|
|
|
@ -251,7 +286,7 @@ void Pass::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &m |
|
|
|
|
pass->deleteLater(); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
QList<QAction *> Pass::actionsForMatch(const Plasma::QueryMatch &match) |
|
|
|
|
{ |
|
|
|
|
Q_UNUSED(match) |
|
|
|
|
@ -259,12 +294,18 @@ QList<QAction *> Pass::actionsForMatch(const Plasma::QueryMatch &match) |
|
|
|
|
return this->orderedActions; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
void Pass::showNotification(const QString &text, const QString &actionName) |
|
|
|
|
{ |
|
|
|
|
const QString msgPrefix = actionName.isEmpty() ? "" : actionName + i18n(" of "); |
|
|
|
|
const QString msg = i18n("Password %1 copied to clipboard for %2 seconds", text, timeout); |
|
|
|
|
KNotification::event("password-unlocked", "Pass", msgPrefix + msg, |
|
|
|
|
"object-unlocked", nullptr, KNotification::CloseOnTimeout, |
|
|
|
|
"object-unlocked", |
|
|
|
|
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
|
|
|
|
nullptr, |
|
|
|
|
#endif |
|
|
|
|
KNotification::CloseOnTimeout, |
|
|
|
|
"krunner_pass"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|