Improve CLI Show and Clip Commands

* Fixes #11767
* When requesting TOTP, only output that value and then exit
* Add explicit requests for UUID and Tags for both show and clip commands.
* When showing all values, also include UUID and Tags.
* Only hide the attribute names when specifically requesting them by name
This commit is contained in:
Jonathan White
2025-05-10 09:03:47 -04:00
parent f53c7e5af5
commit 87ccc85247
9 changed files with 151 additions and 131 deletions

View File

@@ -52,11 +52,12 @@ It provides the ability to query and modify the entries of a KeePass database, d
Removes the named attachment from an entry. Removes the named attachment from an entry.
*clip* [_options_] <__database__> <__entry__> [_timeout_]:: *clip* [_options_] <__database__> <__entry__> [_timeout_]::
Copies an attribute or the current TOTP (if the *-t* option is specified) of a database entry to the clipboard. Copies an attribute, current TOTP value, UUID, or tags list of a database entry to the clipboard.
If no attribute name is specified using the *-a* option, the password is copied. If no attribute name is specified using the *-a* option, the password is copied.
If multiple entries with the same name exist in different groups, only the attribute for the first one is copied. If multiple entries with the same name exist in different groups, only the attribute for the first one is copied.
For copying the attribute of an entry in a specific group, the group path to the entry should be specified as well, instead of just the name. For copying the attribute of an entry in a specific group, the group path to the entry should be specified as well, instead of just the name.
Optionally, a timeout in seconds can be specified to automatically clear the clipboard, the default timeout is 10 seconds, set to 0 to disable. Optionally, a timeout in seconds can be specified to automatically clear the clipboard, the default timeout is 10 seconds, set to 0 to disable.
Note: an error will be thrown if you specify multiple options at once (eg, *--uuid* and *-a*).
*close*:: *close*::
In interactive mode, closes the currently opened database (see *open*). In interactive mode, closes the currently opened database (see *open*).
@@ -143,8 +144,8 @@ It provides the ability to query and modify the entries of a KeePass database, d
Searches all entries that match a specific search term in a database. Searches all entries that match a specific search term in a database.
*show* [_options_] <__database__> <__entry__>:: *show* [_options_] <__database__> <__entry__>::
Shows the title, username, password, URL and notes of a database entry. Shows the title, username, password, URL and notes of a database entry by default.
Can also show the current TOTP. Can also show the current TOTP, entry UUID, and tags list.
Regarding the occurrence of multiple entries with the same name in different groups, everything stated in the *clip* command section also applies here. Regarding the occurrence of multiple entries with the same name in different groups, everything stated in the *clip* command section also applies here.
== OPTIONS == OPTIONS
@@ -235,6 +236,12 @@ The same password generation options as documented for the generate command can
Copies the current TOTP instead of the specified attribute to the clipboard. Copies the current TOTP instead of the specified attribute to the clipboard.
Will report an error if no TOTP is configured for the entry. Will report an error if no TOTP is configured for the entry.
*--uuid*::
Copies the UUID of the entry to the clipboard.
*--tags*::
Copies the tags of the entry to the clipboard.
*-b*, *--best*:: *-b*, *--best*::
Try to find and copy to clipboard a unique entry matching the input Try to find and copy to clipboard a unique entry matching the input
If a unique matching entry is found it will be copied to the clipboard. If a unique matching entry is found it will be copied to the clipboard.
@@ -262,7 +269,6 @@ The same password generation options as documented for the generate command can
*-a*, *--attributes* <__attribute__>...:: *-a*, *--attributes* <__attribute__>...::
Shows the named attributes. Shows the named attributes.
This option can be specified more than once, with each attribute shown one-per-line in the given order. This option can be specified more than once, with each attribute shown one-per-line in the given order.
If no attributes are specified and *-t* is not specified, a summary of the default attributes is given.
Protected attributes will be displayed in clear text if specified explicitly by this option. Protected attributes will be displayed in clear text if specified explicitly by this option.
*--all*:: *--all*::
@@ -275,7 +281,13 @@ The same password generation options as documented for the generate command can
Shows the attachment names along with the size of the attachments. Shows the attachment names along with the size of the attachments.
*-t*, *--totp*:: *-t*, *--totp*::
Also shows the current TOTP, reporting an error if no TOTP is configured for the entry. Shows the current TOTP and then exits. An error is thrown if no TOTP is configured for the entry.
*--uuid*::
Shows the UUID of the entry.
*--tags*::
Shows the tag list of the entry.
=== Diceware options === Diceware options
*-W*, *--words* <__count__>:: *-W*, *--words* <__count__>::

View File

@@ -7635,10 +7635,6 @@ Do you want to overwrite it?</source>
<source>Entry %1 not found.</source> <source>Entry %1 not found.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>ERROR: Please specify one of --attribute or --totp, not both.</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>Entry with path %1 has no TOTP set up.</source> <source>Entry with path %1 has no TOTP set up.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -8352,18 +8348,10 @@ Available commands:
<source>Search term.</source> <source>Search term.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Show the entry&apos;s current TOTP.</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>Show the protected attributes in clear text.</source> <source>Show the protected attributes in clear text.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Show all the attributes of the entry.</source>
<translation type="unfinished"></translation>
</message>
<message> <message>
<source>Show the attachments of the entry.</source> <source>Show the attachments of the entry.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
@@ -9248,6 +9236,34 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Tags</source> <source>Tags</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Copy the entry&apos;s UUID to the clipboard.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Copy the entry&apos;s tag list to the clipboard.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>ERROR: Cannot specify multiple options at once (--attribute, --totp, --uuid, --tags).</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Only show the entry&apos;s current TOTP.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show the entry&apos;s UUID.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show the entry&apos;s tags.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show all the attributes of the entry, including UUID and Tags.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>QtIOCompressor</name> <name>QtIOCompressor</name>

View File

@@ -37,6 +37,12 @@ const QCommandLineOption Clip::TotpOption =
QCommandLineOption(QStringList() << "t" << "totp", QCommandLineOption(QStringList() << "t" << "totp",
QObject::tr("Copy the current TOTP to the clipboard (equivalent to \"-a totp\").")); QObject::tr("Copy the current TOTP to the clipboard (equivalent to \"-a totp\")."));
const QCommandLineOption Clip::UuidOption =
QCommandLineOption(QStringList() << "uuid", QObject::tr("Copy the entry's UUID to the clipboard."));
const QCommandLineOption Clip::TagsOption =
QCommandLineOption(QStringList() << "tags", QObject::tr("Copy the entry's tag list to the clipboard."));
const QCommandLineOption Clip::BestMatchOption = const QCommandLineOption Clip::BestMatchOption =
QCommandLineOption(QStringList() << "b" << "best-match", QCommandLineOption(QStringList() << "b" << "best-match",
QObject::tr("Must match only one entry, otherwise a list of possible matches is shown.")); QObject::tr("Must match only one entry, otherwise a list of possible matches is shown."));
@@ -47,6 +53,8 @@ Clip::Clip()
description = QObject::tr("Copy an entry's attribute to the clipboard."); description = QObject::tr("Copy an entry's attribute to the clipboard.");
options.append(Clip::AttributeOption); options.append(Clip::AttributeOption);
options.append(Clip::TotpOption); options.append(Clip::TotpOption);
options.append(Clip::UuidOption);
options.append(Clip::TagsOption);
options.append(Clip::BestMatchOption); options.append(Clip::BestMatchOption);
positionalArguments.append( positionalArguments.append(
{QString("entry"), QObject::tr("Path of the entry to clip.", "clip = copy to clipboard"), QString("")}); {QString("entry"), QObject::tr("Path of the entry to clip.", "clip = copy to clipboard"), QString("")});
@@ -99,8 +107,13 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (parser->isSet(AttributeOption) && parser->isSet(TotpOption)) { auto optionCount = parser->isSet(AttributeOption) ? 1 : 0;
err << QObject::tr("ERROR: Please specify one of --attribute or --totp, not both.") << Qt::endl; optionCount += parser->isSet(TotpOption) ? 1 : 0;
optionCount += parser->isSet(UuidOption) ? 1 : 0;
optionCount += parser->isSet(TagsOption) ? 1 : 0;
if (optionCount > 1) {
err << QObject::tr("ERROR: Cannot specify multiple options at once (--attribute, --totp, --uuid, --tags).")
<< Qt::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -113,11 +126,16 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
return EXIT_FAILURE; return EXIT_FAILURE;
} }
selectedAttribute = "totp";
found = true;
value = entry->totp(); value = entry->totp();
} else if (Utils::EntryFieldNames.contains(selectedAttribute)) { selectedAttribute = "TOTP";
value = Utils::getTopLevelField(entry, selectedAttribute); found = true;
} else if (parser->isSet(UuidOption)) {
value = entry->uuid().toString();
selectedAttribute = "UUID";
found = true;
} else if (parser->isSet(TagsOption)) {
value = entry->tags();
selectedAttribute = "Tags";
found = true; found = true;
} else { } else {
QStringList attrs = Utils::findAttributes(*entry->attributes(), selectedAttribute); QStringList attrs = Utils::findAttributes(*entry->attributes(), selectedAttribute);

View File

@@ -29,6 +29,8 @@ public:
static const QCommandLineOption AttributeOption; static const QCommandLineOption AttributeOption;
static const QCommandLineOption TotpOption; static const QCommandLineOption TotpOption;
static const QCommandLineOption UuidOption;
static const QCommandLineOption TagsOption;
static const QCommandLineOption BestMatchOption; static const QCommandLineOption BestMatchOption;
}; };

View File

@@ -24,14 +24,21 @@
#include <QCommandLineParser> #include <QCommandLineParser>
const QCommandLineOption Show::TotpOption = const QCommandLineOption Show::TotpOption =
QCommandLineOption(QStringList() << "t" << "totp", QObject::tr("Show the entry's current TOTP.")); QCommandLineOption(QStringList() << "t" << "totp", QObject::tr("Only show the entry's current TOTP."));
const QCommandLineOption Show::UuidOption =
QCommandLineOption(QStringList() << "uuid", QObject::tr("Show the entry's UUID."));
const QCommandLineOption Show::TagsOption =
QCommandLineOption(QStringList() << "tags", QObject::tr("Show the entry's tags."));
const QCommandLineOption Show::ProtectedAttributesOption = const QCommandLineOption Show::ProtectedAttributesOption =
QCommandLineOption(QStringList() << "s" << "show-protected", QCommandLineOption(QStringList() << "s" << "show-protected",
QObject::tr("Show the protected attributes in clear text.")); QObject::tr("Show the protected attributes in clear text."));
const QCommandLineOption Show::AllAttributesOption = const QCommandLineOption Show::AllAttributesOption =
QCommandLineOption(QStringList() << "all", QObject::tr("Show all the attributes of the entry.")); QCommandLineOption(QStringList() << "all",
QObject::tr("Show all the attributes of the entry, including UUID and Tags."));
const QCommandLineOption Show::AttachmentsOption = const QCommandLineOption Show::AttachmentsOption =
QCommandLineOption(QStringList() << "show-attachments", QObject::tr("Show the attachments of the entry.")); QCommandLineOption(QStringList() << "show-attachments", QObject::tr("Show the attachments of the entry."));
@@ -49,6 +56,8 @@ Show::Show()
name = QString("show"); name = QString("show");
description = QObject::tr("Show an entry's information."); description = QObject::tr("Show an entry's information.");
options.append(Show::TotpOption); options.append(Show::TotpOption);
options.append(Show::UuidOption);
options.append(Show::TagsOption);
options.append(Show::AttributesOption); options.append(Show::AttributesOption);
options.append(Show::ProtectedAttributesOption); options.append(Show::ProtectedAttributesOption);
options.append(Show::AllAttributesOption); options.append(Show::AllAttributesOption);
@@ -63,9 +72,10 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
const QStringList args = parser->positionalArguments(); const QStringList args = parser->positionalArguments();
const QString& entryPath = args.at(1); const QString& entryPath = args.at(1);
bool showTotp = parser->isSet(Show::TotpOption);
bool showProtectedAttributes = parser->isSet(Show::ProtectedAttributesOption); bool showProtectedAttributes = parser->isSet(Show::ProtectedAttributesOption);
bool showAllAttributes = parser->isSet(Show::AllAttributesOption); bool showAllAttributes = parser->isSet(Show::AllAttributesOption);
bool showUuid = parser->isSet(Show::UuidOption);
bool showTags = parser->isSet(Show::TagsOption);
QStringList attributes = parser->values(Show::AttributesOption); QStringList attributes = parser->values(Show::AttributesOption);
Entry* entry = database->rootGroup()->findEntryByPath(entryPath); Entry* entry = database->rootGroup()->findEntryByPath(entryPath);
@@ -74,18 +84,23 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (showTotp && !entry->hasTotp()) { // Early exit if the user only wants to show the TOTP
err << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << Qt::endl; if (parser->isSet(Show::TotpOption)) {
return EXIT_FAILURE; if (!entry->hasTotp()) {
err << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << Qt::endl;
return EXIT_FAILURE;
}
out << entry->totp() << Qt::endl;
return EXIT_SUCCESS;
} }
bool attributesWereSpecified = true; bool attributesWereSpecified = !showUuid && !showTags;
if (showAllAttributes) { if (showAllAttributes) {
attributesWereSpecified = false; attributesWereSpecified = false;
showUuid = true;
showTags = true;
attributes = EntryAttributes::DefaultAttributes; attributes = EntryAttributes::DefaultAttributes;
for (QString fieldName : Utils::EntryFieldNames) {
attributes.append(fieldName);
}
// Adding the custom attributes after the default attributes so that // Adding the custom attributes after the default attributes so that
// the default attributes are always shown first. // the default attributes are always shown first.
for (QString attributeName : entry->attributes()->keys()) { for (QString attributeName : entry->attributes()->keys()) {
@@ -94,26 +109,16 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
} }
attributes.append(attributeName); attributes.append(attributeName);
} }
} else if (attributes.isEmpty() && !showTotp) { } else if (attributes.isEmpty() && !showUuid && !showTags) {
// If no attributes are specified, output the default attribute set. // If no attributes are specified, output the default attribute set.
attributesWereSpecified = false; attributesWereSpecified = false;
attributes = EntryAttributes::DefaultAttributes; attributes = EntryAttributes::DefaultAttributes;
for (QString fieldName : Utils::EntryFieldNames) { showTags = true;
attributes.append(fieldName);
}
} }
// Iterate over the attributes and output them line-by-line. // Iterate over the attributes and output them line-by-line.
bool encounteredError = false; bool encounteredError = false;
for (const QString& attributeName : asConst(attributes)) { for (const QString& attributeName : asConst(attributes)) {
if (Utils::EntryFieldNames.contains(attributeName)) {
if (!attributesWereSpecified) {
out << attributeName << ": ";
}
out << Utils::getTopLevelField(entry, attributeName) << Qt::endl;
continue;
}
QStringList attrs = Utils::findAttributes(*entry->attributes(), attributeName); QStringList attrs = Utils::findAttributes(*entry->attributes(), attributeName);
if (attrs.isEmpty()) { if (attrs.isEmpty()) {
encounteredError = true; encounteredError = true;
@@ -137,6 +142,14 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
} }
} }
// Output UUID and Tags if a certain field wasn't specified
if (showTags) {
out << "Tags: " << entry->tags() << Qt::endl;
}
if (showUuid) {
out << "UUID: " << entry->uuid().toString() << Qt::endl;
}
if (parser->isSet(Show::AttachmentsOption)) { if (parser->isSet(Show::AttachmentsOption)) {
// Separate attachment output from attributes output via a newline. // Separate attachment output from attributes output via a newline.
out << Qt::endl; out << Qt::endl;
@@ -156,9 +169,5 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
} }
} }
if (showTotp) {
out << entry->totp() << Qt::endl;
}
return encounteredError ? EXIT_FAILURE : EXIT_SUCCESS; return encounteredError ? EXIT_FAILURE : EXIT_SUCCESS;
} }

View File

@@ -28,6 +28,8 @@ public:
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override; int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
static const QCommandLineOption TotpOption; static const QCommandLineOption TotpOption;
static const QCommandLineOption UuidOption;
static const QCommandLineOption TagsOption;
static const QCommandLineOption AllAttributesOption; static const QCommandLineOption AllAttributesOption;
static const QCommandLineOption AttributesOption; static const QCommandLineOption AttributesOption;
static const QCommandLineOption ProtectedAttributesOption; static const QCommandLineOption ProtectedAttributesOption;

View File

@@ -395,17 +395,6 @@ namespace Utils
return result; return result;
} }
QString getTopLevelField(const Entry* entry, const QString& fieldName)
{
if (fieldName == UuidFieldName) {
return entry->uuid().toString();
}
if (fieldName == TagsFieldName) {
return entry->tags();
}
return "";
}
QStringList findAttributes(const EntryAttributes& attributes, const QString& name) QStringList findAttributes(const EntryAttributes& attributes, const QString& name)
{ {
QStringList result; QStringList result;

View File

@@ -34,10 +34,6 @@ namespace Utils
extern QTextStream STDIN; extern QTextStream STDIN;
extern QTextStream DEVNULL; extern QTextStream DEVNULL;
static const QString UuidFieldName = "Uuid";
static const QString TagsFieldName = "Tags";
static const QStringList EntryFieldNames(QStringList() << UuidFieldName << TagsFieldName);
void setDefaultTextStreams(); void setDefaultTextStreams();
void resetTextStreams(); void resetTextStreams();
@@ -61,10 +57,6 @@ namespace Utils
* (case-insensitive). * (case-insensitive).
*/ */
QStringList findAttributes(const EntryAttributes& attributes, const QString& name); QStringList findAttributes(const EntryAttributes& attributes, const QString& name);
/**
* Get the value of a top-level Entry field using its name.
*/
QString getTopLevelField(const Entry* entry, const QString& fieldName);
}; // namespace Utils }; // namespace Utils
#endif // KEEPASSXC_UTILS_H #endif // KEEPASSXC_UTILS_H

View File

@@ -673,14 +673,14 @@ void TestCli::testClip()
// Uuid (top-level field) // Uuid (top-level field)
setInput("a"); setInput("a");
execCmd(clipCmd, {"clip", m_dbFile->fileName(), "/Sample Entry", "0", "-a", "Uuid"}); execCmd(clipCmd, {"clip", m_dbFile->fileName(), "/Sample Entry", "0", "--uuid"});
QTRY_COMPARE(clipboard->text(), QString("{9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}")); QTRY_COMPARE(clipboard->text(), QString("{9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}"));
// TOTP // TOTP
setInput("a"); setInput("a");
execCmd(clipCmd, {"clip", m_dbFile->fileName(), "/Sample Entry", "0", "--totp"}); execCmd(clipCmd, {"clip", m_dbFile->fileName(), "/Sample Entry", "0", "--totp"});
QTRY_VERIFY(isTotp(clipboard->text())); QTRY_VERIFY(isTotp(clipboard->text()));
QCOMPARE(m_stdout->readLine(), QByteArray("Entry's \"totp\" attribute copied to the clipboard!\n")); QCOMPARE(m_stdout->readLine(), QByteArray("Entry's \"TOTP\" attribute copied to the clipboard!\n"));
// Test Unicode // Test Unicode
setInput("a"); setInput("a");
@@ -725,7 +725,7 @@ void TestCli::testClip()
setInput("a"); setInput("a");
execCmd(clipCmd, {"clip", m_dbFile2->fileName(), "--attribute", "Username", "--totp", "/Sample Entry", "0"}); execCmd(clipCmd, {"clip", m_dbFile2->fileName(), "--attribute", "Username", "--totp", "/Sample Entry", "0"});
QVERIFY(m_stderr->readAll().contains("ERROR: Please specify one of --attribute or --totp, not both.\n")); QVERIFY(m_stderr->readAll().contains("ERROR: Cannot specify multiple options at once"));
// Best option // Best option
setInput("a"); setInput("a");
@@ -2077,72 +2077,55 @@ void TestCli::testShow()
QVERIFY(!showCmd.name.isEmpty()); QVERIFY(!showCmd.name.isEmpty());
QVERIFY(showCmd.getDescriptionLine().contains(showCmd.name)); QVERIFY(showCmd.getDescriptionLine().contains(showCmd.name));
const QByteArray expectTitle("Title: Sample Entry");
const QByteArray expectUserName("UserName: User Name");
const QByteArray expectUrl("URL: http://www.somesite.com/");
const QByteArray expectUuid("UUID: {9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}");
const QByteArray expectNotes("Notes: Notes");
const QByteArray expectTags("Tags: ");
setInput("a"); setInput("a");
execCmd(showCmd, {"show", m_dbFile->fileName(), "/Sample Entry"}); execCmd(showCmd, {"show", m_dbFile->fileName(), "/Sample Entry"});
m_stderr->readLine(); // Skip password prompt m_stderr->readLine(); // Skip password prompt
QCOMPARE(m_stderr->readAll(), QByteArray()); QCOMPARE(m_stderr->readAll(), QByteArray());
QCOMPARE(m_stdout->readAll(), auto out = m_stdout->readAll();
QByteArray("Title: Sample Entry\n" QVERIFY(out.contains(expectTitle));
"UserName: User Name\n" QVERIFY(out.contains(expectUserName));
"Password: PROTECTED\n" QVERIFY(out.contains(expectUrl));
"URL: http://www.somesite.com/\n" QVERIFY(out.contains(expectNotes));
"Notes: Notes\n" QVERIFY(out.contains(expectTags));
"Uuid: {9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n" QVERIFY(!out.contains(expectUuid));
"Tags: \n")); QVERIFY(out.contains("Password: PROTECTED"));
setInput("a"); setInput("a");
execCmd(showCmd, {"show", "-s", m_dbFile->fileName(), "/Sample Entry"}); execCmd(showCmd, {"show", "-s", m_dbFile->fileName(), "/Sample Entry"});
QCOMPARE(m_stdout->readAll(), out = m_stdout->readAll();
QByteArray("Title: Sample Entry\n" QVERIFY(out.contains("Password: Password"));
"UserName: User Name\n"
"Password: Password\n"
"URL: http://www.somesite.com/\n"
"Notes: Notes\n"
"Uuid: {9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n"
"Tags: \n"));
setInput("a"); setInput("a");
execCmd(showCmd, {"show", m_dbFile->fileName(), "-q", "/Sample Entry"}); execCmd(showCmd, {"show", m_dbFile->fileName(), "-q", "/Sample Entry"});
QCOMPARE(m_stderr->readAll(), QByteArray()); QCOMPARE(m_stderr->readAll(), QByteArray());
QCOMPARE(m_stdout->readAll(), out = m_stdout->readAll();
QByteArray("Title: Sample Entry\n" QVERIFY(out.contains(expectTitle));
"UserName: User Name\n" QVERIFY(out.contains(expectUserName));
"Password: PROTECTED\n" QVERIFY(out.contains(expectUrl));
"URL: http://www.somesite.com/\n" QVERIFY(out.contains(expectNotes));
"Notes: Notes\n" QVERIFY(out.contains(expectTags));
"Uuid: {9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n" QVERIFY(!out.contains(expectUuid));
"Tags: \n"));
setInput("a"); setInput("a");
execCmd(showCmd, {"show", m_dbFile->fileName(), "--show-attachments", "/Sample Entry"}); execCmd(showCmd, {"show", m_dbFile->fileName(), "--show-attachments", "/Sample Entry"});
m_stderr->readLine(); // Skip password prompt m_stderr->readLine(); // Skip password prompt
QCOMPARE(m_stderr->readAll(), QByteArray()); QCOMPARE(m_stderr->readAll(), QByteArray());
QCOMPARE(m_stdout->readAll(), out = m_stdout->readAll();
QByteArray("Title: Sample Entry\n" QVERIFY(out.contains("Attachments:\n Sample attachment.txt (15 B)"));
"UserName: User Name\n"
"Password: PROTECTED\n"
"URL: http://www.somesite.com/\n"
"Notes: Notes\n"
"Uuid: {9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n"
"Tags: \n"
"\n"
"Attachments:\n"
" Sample attachment.txt (15 B)\n"));
setInput("a"); setInput("a");
execCmd(showCmd, {"show", m_dbFile->fileName(), "--show-attachments", "/Homebanking/Subgroup/Subgroup Entry"}); execCmd(showCmd, {"show", m_dbFile->fileName(), "--show-attachments", "/Homebanking/Subgroup/Subgroup Entry"});
m_stderr->readLine(); // Skip password prompt m_stderr->readLine(); // Skip password prompt
QCOMPARE(m_stderr->readAll(), QByteArray()); QCOMPARE(m_stderr->readAll(), QByteArray());
QCOMPARE(m_stdout->readAll(), out = m_stdout->readAll();
QByteArray("Title: Subgroup Entry\n" QVERIFY(out.contains("No attachments present."));
"UserName: Bank User Name\n"
"Password: PROTECTED\n"
"URL: https://www.bank.com\n"
"Notes: Important note\n"
"Uuid: {20b183fd-6878-4506-a50b-06d30792aa10}\n"
"Tags: \n"
"\n"
"No attachments present.\n"));
setInput("a"); setInput("a");
execCmd(showCmd, {"show", "-a", "Title", m_dbFile->fileName(), "/Sample Entry"}); execCmd(showCmd, {"show", "-a", "Title", m_dbFile->fileName(), "/Sample Entry"});
@@ -2153,8 +2136,8 @@ void TestCli::testShow()
QCOMPARE(m_stdout->readAll(), QByteArray("Password\n")); QCOMPARE(m_stdout->readAll(), QByteArray("Password\n"));
setInput("a"); setInput("a");
execCmd(showCmd, {"show", "-a", "Uuid", m_dbFile->fileName(), "/Sample Entry"}); execCmd(showCmd, {"show", "--uuid", m_dbFile->fileName(), "/Sample Entry"});
QCOMPARE(m_stdout->readAll(), QByteArray("{9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n")); QVERIFY(m_stdout->readAll().contains(expectUuid));
setInput("a"); setInput("a");
execCmd(showCmd, {"show", "-a", "Title", "-a", "URL", m_dbFile->fileName(), "/Sample Entry"}); execCmd(showCmd, {"show", "-a", "Title", "-a", "URL", m_dbFile->fileName(), "/Sample Entry"});
@@ -2178,9 +2161,9 @@ void TestCli::testShow()
execCmd(showCmd, {"show", "-t", m_dbFile->fileName(), "/Sample Entry"}); execCmd(showCmd, {"show", "-t", m_dbFile->fileName(), "/Sample Entry"});
QVERIFY(isTotp(m_stdout->readAll())); QVERIFY(isTotp(m_stdout->readAll()));
// TOTP paramter short circuits any other parameter
setInput("a"); setInput("a");
execCmd(showCmd, {"show", "-a", "Title", m_dbFile->fileName(), "--totp", "/Sample Entry"}); execCmd(showCmd, {"show", "-a", "Title", m_dbFile->fileName(), "--totp", "/Sample Entry"});
QCOMPARE(m_stdout->readLine(), QByteArray("Sample Entry\n"));
QVERIFY(isTotp(m_stdout->readAll())); QVERIFY(isTotp(m_stdout->readAll()));
setInput("a"); setInput("a");
@@ -2196,18 +2179,15 @@ void TestCli::testShow()
setInput("a"); setInput("a");
execCmd(showCmd, {"show", "--all", m_dbFile->fileName(), "/Sample Entry"}); execCmd(showCmd, {"show", "--all", m_dbFile->fileName(), "/Sample Entry"});
QCOMPARE(m_stdout->readAll(), out = m_stdout->readAll();
QByteArray("Title: Sample Entry\n" QVERIFY(out.contains(expectTitle));
"UserName: User Name\n" QVERIFY(out.contains(expectUserName));
"Password: PROTECTED\n" QVERIFY(out.contains(expectUuid));
"URL: http://www.somesite.com/\n" QVERIFY(out.contains(expectTags));
"Notes: Notes\n" QVERIFY(out.contains("TOTP Seed: PROTECTED"));
"Uuid: {9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n" QVERIFY(out.contains("TOTP Settings: 30;6"));
"Tags: \n" QVERIFY(out.contains("TestAttribute1: b"));
"TOTP Seed: PROTECTED\n" QVERIFY(out.contains("testattribute1: a"));
"TOTP Settings: 30;6\n"
"TestAttribute1: b\n"
"testattribute1: a\n"));
} }
void TestCli::testInvalidDbFiles() void TestCli::testInvalidDbFiles()