mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-08-06 05:24:13 -04:00
Add --username option to Clip command. (#3947)
* make Clip accept an attribute name This allows users to copy arbitrary attributes (e.g. username, notes, URL) to the clipboard in addition to the password and TOTP values. * update Clip manpage * Add findAttributes to CLI utils * Use case-insensitive search in Show command. * Use case-insensitive search in Clip command. Co-authored-by: louib <L0U13@protonmail.com>
This commit is contained in:
parent
06e0f38523
commit
71a39c37ec
8 changed files with 135 additions and 26 deletions
|
@ -17,7 +17,6 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <stdio.h>
|
||||
#include <thread>
|
||||
|
||||
#include "Clip.h"
|
||||
|
@ -28,14 +27,23 @@
|
|||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
|
||||
const QCommandLineOption Clip::TotpOption = QCommandLineOption(QStringList() << "t"
|
||||
<< "totp",
|
||||
QObject::tr("Copy the current TOTP to the clipboard."));
|
||||
const QCommandLineOption Clip::AttributeOption = QCommandLineOption(
|
||||
QStringList() << "a"
|
||||
<< "attribute",
|
||||
QObject::tr("Copy the given attribute to the clipboard. Defaults to \"password\" if not specified."),
|
||||
"attr",
|
||||
"password");
|
||||
|
||||
const QCommandLineOption Clip::TotpOption =
|
||||
QCommandLineOption(QStringList() << "t"
|
||||
<< "totp",
|
||||
QObject::tr("Copy the current TOTP to the clipboard (equivalent to \"-a totp\")."));
|
||||
|
||||
Clip::Clip()
|
||||
{
|
||||
name = QString("clip");
|
||||
description = QObject::tr("Copy an entry's password to the clipboard.");
|
||||
description = QObject::tr("Copy an entry's attribute to the clipboard.");
|
||||
options.append(Clip::AttributeOption);
|
||||
options.append(Clip::TotpOption);
|
||||
positionalArguments.append(
|
||||
{QString("entry"), QObject::tr("Path of the entry to clip.", "clip = copy to clipboard"), QString("")});
|
||||
|
@ -51,7 +59,6 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
|||
if (args.size() == 3) {
|
||||
timeout = args.at(2);
|
||||
}
|
||||
bool clipTotp = parser->isSet(Clip::TotpOption);
|
||||
TextStream errorTextStream(Utils::STDERR);
|
||||
|
||||
int timeoutSeconds = 0;
|
||||
|
@ -70,16 +77,39 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (parser->isSet(AttributeOption) && parser->isSet(TotpOption)) {
|
||||
errorTextStream << QObject::tr("ERROR: Please specify one of --attribute or --totp, not both.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QString selectedAttribute = parser->value(AttributeOption);
|
||||
QString value;
|
||||
if (clipTotp) {
|
||||
bool found = false;
|
||||
if (parser->isSet(TotpOption) || selectedAttribute == "totp") {
|
||||
if (!entry->hasTotp()) {
|
||||
errorTextStream << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
found = true;
|
||||
value = entry->totp();
|
||||
} else {
|
||||
value = entry->password();
|
||||
QStringList attrs = Utils::findAttributes(*entry->attributes(), selectedAttribute);
|
||||
if (attrs.size() > 1) {
|
||||
errorTextStream << QObject::tr("ERROR: attribute %1 is ambiguous, it matches %2.")
|
||||
.arg(selectedAttribute, QLocale().createSeparatedList(attrs))
|
||||
<< endl;
|
||||
return EXIT_FAILURE;
|
||||
} else if (attrs.size() == 1) {
|
||||
found = true;
|
||||
selectedAttribute = attrs[0];
|
||||
value = entry->attributes()->value(selectedAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
outputTextStream << QObject::tr("Attribute \"%1\" not found.").arg(selectedAttribute) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int exitCode = Utils::clipText(value);
|
||||
|
@ -87,11 +117,7 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
|||
return exitCode;
|
||||
}
|
||||
|
||||
if (clipTotp) {
|
||||
outputTextStream << QObject::tr("Entry's current TOTP copied to the clipboard!") << endl;
|
||||
} else {
|
||||
outputTextStream << QObject::tr("Entry's password copied to the clipboard!") << endl;
|
||||
}
|
||||
outputTextStream << QObject::tr("Entry's \"%1\" attribute copied to the clipboard!").arg(selectedAttribute) << endl;
|
||||
|
||||
if (!timeoutSeconds) {
|
||||
return exitCode;
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
|
||||
|
||||
static const QCommandLineOption AttributeOption;
|
||||
static const QCommandLineOption TotpOption;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include "core/Global.h"
|
||||
#include "core/Group.h"
|
||||
|
||||
#include <QLocale>
|
||||
|
||||
const QCommandLineOption Show::TotpOption = QCommandLineOption(QStringList() << "t"
|
||||
<< "totp",
|
||||
QObject::tr("Show the entry's current TOTP."));
|
||||
|
@ -79,25 +81,33 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
|||
|
||||
// If no attributes specified, output the default attribute set.
|
||||
bool showDefaultAttributes = attributes.isEmpty() && !showTotp;
|
||||
if (attributes.isEmpty() && !showTotp) {
|
||||
if (showDefaultAttributes) {
|
||||
attributes = EntryAttributes::DefaultAttributes;
|
||||
}
|
||||
|
||||
// Iterate over the attributes and output them line-by-line.
|
||||
bool sawUnknownAttribute = false;
|
||||
bool encounteredError = false;
|
||||
for (const QString& attributeName : asConst(attributes)) {
|
||||
if (!entry->attributes()->contains(attributeName)) {
|
||||
sawUnknownAttribute = true;
|
||||
QStringList attrs = Utils::findAttributes(*entry->attributes(), attributeName);
|
||||
if (attrs.isEmpty()) {
|
||||
encounteredError = true;
|
||||
errorTextStream << QObject::tr("ERROR: unknown attribute %1.").arg(attributeName) << endl;
|
||||
continue;
|
||||
} else if (attrs.size() > 1) {
|
||||
encounteredError = true;
|
||||
errorTextStream << QObject::tr("ERROR: attribute %1 is ambiguous, it matches %2.")
|
||||
.arg(attributeName, QLocale().createSeparatedList(attrs))
|
||||
<< endl;
|
||||
continue;
|
||||
}
|
||||
QString canonicalName = attrs[0];
|
||||
if (showDefaultAttributes) {
|
||||
outputTextStream << attributeName << ": ";
|
||||
outputTextStream << canonicalName << ": ";
|
||||
}
|
||||
if (entry->attributes()->isProtected(attributeName) && showDefaultAttributes && !showProtectedAttributes) {
|
||||
if (entry->attributes()->isProtected(canonicalName) && showDefaultAttributes && !showProtectedAttributes) {
|
||||
outputTextStream << "PROTECTED" << endl;
|
||||
} else {
|
||||
outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(attributeName)) << endl;
|
||||
outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(canonicalName)) << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,5 +115,5 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
|||
outputTextStream << entry->totp() << endl;
|
||||
}
|
||||
|
||||
return sawUnknownAttribute ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
return encounteredError ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -331,4 +331,21 @@ namespace Utils
|
|||
return result;
|
||||
}
|
||||
|
||||
QStringList findAttributes(const EntryAttributes& attributes, const QString& name)
|
||||
{
|
||||
QStringList result;
|
||||
if (attributes.hasKey(name)) {
|
||||
result.append(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
for (const QString& key : attributes.keys()) {
|
||||
if (key.compare(name, Qt::CaseSensitivity::CaseInsensitive) == 0) {
|
||||
result.append(key);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "cli/TextStream.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/EntryAttributes.h"
|
||||
#include "keys/CompositeKey.h"
|
||||
#include "keys/FileKey.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
@ -51,6 +52,14 @@ namespace Utils
|
|||
|
||||
QStringList splitCommandString(const QString& command);
|
||||
|
||||
/**
|
||||
* If `attributes` contains an attribute named `name` (case-sensitive),
|
||||
* returns a list containing only `name`. Otherwise, returns the list of
|
||||
* all attribute names in `attributes` matching the given name
|
||||
* (case-insensitive).
|
||||
*/
|
||||
QStringList findAttributes(const EntryAttributes& attributes, const QString& name);
|
||||
|
||||
namespace Test
|
||||
{
|
||||
void setNextPassword(const QString& password);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue