diff --git a/src/cli/Clip.cpp b/src/cli/Clip.cpp index 7c4a9bb1f..64e56638e 100644 --- a/src/cli/Clip.cpp +++ b/src/cli/Clip.cpp @@ -118,6 +118,9 @@ int Clip::executeWithDatabase(QSharedPointer database, QSharedPointer< found = true; value = entry->totp(); + } else if (Utils::EntryFieldNames.contains(selectedAttribute)) { + value = Utils::getTopLevelField(entry, selectedAttribute); + found = true; } else { QStringList attrs = Utils::findAttributes(*entry->attributes(), selectedAttribute); if (attrs.size() > 1) { diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index f4d8097f6..8ffd5614d 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -81,11 +81,22 @@ int Show::executeWithDatabase(QSharedPointer database, QSharedPointer< bool showDefaultAttributes = attributes.isEmpty() && !showTotp; if (showDefaultAttributes) { attributes = EntryAttributes::DefaultAttributes; + for (QString fieldName : Utils::EntryFieldNames) { + attributes.append(fieldName); + } } // Iterate over the attributes and output them line-by-line. bool encounteredError = false; for (const QString& attributeName : asConst(attributes)) { + if (Utils::EntryFieldNames.contains(attributeName)) { + if (showDefaultAttributes) { + out << attributeName << ": "; + } + out << Utils::getTopLevelField(entry, attributeName) << endl; + continue; + } + QStringList attrs = Utils::findAttributes(*entry->attributes(), attributeName); if (attrs.isEmpty()) { encounteredError = true; diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp index ec4fc81f7..6c0b73e93 100644 --- a/src/cli/Utils.cpp +++ b/src/cli/Utils.cpp @@ -18,6 +18,7 @@ #include "Utils.h" #include "core/Database.h" +#include "core/Entry.h" #include "core/EntryAttributes.h" #include "keys/FileKey.h" #ifdef WITH_XC_YUBIKEY @@ -368,6 +369,17 @@ namespace Utils return result; } + QString getTopLevelField(const Entry* entry, const QString& fieldName) + { + if (fieldName == UuidFieldName) { + return entry->uuid().toString(); + } + if (fieldName == TagsFieldName) { + return entry->tags(); + } + return QString(""); + } + QStringList findAttributes(const EntryAttributes& attributes, const QString& name) { QStringList result; diff --git a/src/cli/Utils.h b/src/cli/Utils.h index dedc818f6..84ddbbb4b 100644 --- a/src/cli/Utils.h +++ b/src/cli/Utils.h @@ -22,6 +22,7 @@ class CompositeKey; class Database; +class Entry; class EntryAttributes; class FileKey; class PasswordKey; @@ -33,6 +34,10 @@ namespace Utils extern QTextStream STDIN; extern QTextStream DEVNULL; + static const QString UuidFieldName = "Uuid"; + static const QString TagsFieldName = "Tags"; + static const QStringList EntryFieldNames(QStringList() << UuidFieldName << TagsFieldName); + void setDefaultTextStreams(); void setStdinEcho(bool enable); @@ -55,6 +60,10 @@ namespace Utils * (case-insensitive). */ 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 #endif // KEEPASSXC_UTILS_H diff --git a/src/core/EntryAttributes.cpp b/src/core/EntryAttributes.cpp index d6ca12102..6dfc8adba 100644 --- a/src/core/EntryAttributes.cpp +++ b/src/core/EntryAttributes.cpp @@ -30,7 +30,6 @@ const QString EntryAttributes::URLKey = "URL"; const QString EntryAttributes::NotesKey = "Notes"; const QStringList EntryAttributes::DefaultAttributes(QStringList() << TitleKey << UserNameKey << PasswordKey << URLKey << NotesKey); - const QString EntryAttributes::WantedFieldGroupName = "WantedField"; const QString EntryAttributes::SearchInGroupName = "SearchIn"; const QString EntryAttributes::SearchTextGroupName = "SearchText"; diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index b79b5357c..091c7a3ef 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -662,6 +662,11 @@ void TestCli::testClip() execCmd(clipCmd, {"clip", m_dbFile->fileName(), "/Sample Entry", "0", "-a", "username"}); QTRY_COMPARE(clipboard->text(), QString("User Name")); + // Uuid (top-level field) + setInput("a"); + execCmd(clipCmd, {"clip", m_dbFile->fileName(), "/Sample Entry", "0", "-a", "Uuid"}); + QTRY_COMPARE(clipboard->text(), QString("{9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}")); + // TOTP setInput("a"); execCmd(clipCmd, {"clip", m_dbFile->fileName(), "/Sample Entry", "0", "--totp"}); @@ -1937,7 +1942,9 @@ void TestCli::testShow() "UserName: User Name\n" "Password: PROTECTED\n" "URL: http://www.somesite.com/\n" - "Notes: Notes\n")); + "Notes: Notes\n" + "Uuid: {9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n" + "Tags: \n")); setInput("a"); execCmd(showCmd, {"show", "-s", m_dbFile->fileName(), "/Sample Entry"}); @@ -1946,7 +1953,9 @@ void TestCli::testShow() "UserName: User Name\n" "Password: Password\n" "URL: http://www.somesite.com/\n" - "Notes: Notes\n")); + "Notes: Notes\n" + "Uuid: {9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n" + "Tags: \n")); setInput("a"); execCmd(showCmd, {"show", m_dbFile->fileName(), "-q", "/Sample Entry"}); @@ -1956,7 +1965,9 @@ void TestCli::testShow() "UserName: User Name\n" "Password: PROTECTED\n" "URL: http://www.somesite.com/\n" - "Notes: Notes\n")); + "Notes: Notes\n" + "Uuid: {9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n" + "Tags: \n")); setInput("a"); execCmd(showCmd, {"show", m_dbFile->fileName(), "--show-attachments", "/Sample Entry"}); @@ -1968,6 +1979,8 @@ void TestCli::testShow() "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.0 B)\n")); @@ -1982,6 +1995,8 @@ void TestCli::testShow() "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")); @@ -1993,6 +2008,10 @@ void TestCli::testShow() execCmd(showCmd, {"show", "-a", "Password", m_dbFile->fileName(), "/Sample Entry"}); QCOMPARE(m_stdout->readAll(), QByteArray("Password\n")); + setInput("a"); + execCmd(showCmd, {"show", "-a", "Uuid", m_dbFile->fileName(), "/Sample Entry"}); + QCOMPARE(m_stdout->readAll(), QByteArray("{9f4544c2-ab00-c74a-8a1a-6eaf26cf57e9}\n")); + setInput("a"); execCmd(showCmd, {"show", "-a", "Title", "-a", "URL", m_dbFile->fileName(), "/Sample Entry"}); QCOMPARE(m_stdout->readAll(),