From 7d37f65ad00c49f2f3542ae9bcfda00b73a13c49 Mon Sep 17 00:00:00 2001 From: Andre Blanke Date: Wed, 3 Nov 2021 23:29:44 -0400 Subject: [PATCH] CLI: Add commands to handle attachments * Add commands to manipulate entry attachments from the CLI * Closes #4462 * Add the following commands: attachment-export: Exports the content of an attachment to a specified file. attachment-import: Imports the attachment into an entry. An existing attachment with the same name may be overwritten if the -f option is specified. attachment-rm: Removes the named attachment from an entry. * Add --show-attachments to the show command --- docs/man/keepassxc-cli.1.adoc | 14 ++ share/translations/keepassxc_en.ts | 88 ++++++++++++ src/cli/AttachmentExport.cpp | 88 ++++++++++++ src/cli/AttachmentExport.h | 33 +++++ src/cli/AttachmentImport.cpp | 87 ++++++++++++ src/cli/AttachmentImport.h | 33 +++++ src/cli/AttachmentRemove.cpp | 68 +++++++++ src/cli/AttachmentRemove.h | 31 ++++ src/cli/CMakeLists.txt | 3 + src/cli/Command.cpp | 8 +- src/cli/Show.cpp | 24 ++++ src/cli/Show.h | 1 + tests/TestCli.cpp | 218 ++++++++++++++++++++++++++++- tests/TestCli.h | 3 + tests/data/Attachment.txt | 1 + tests/data/NewDatabase.kdbx | Bin 20590 -> 24654 bytes 16 files changed, 697 insertions(+), 3 deletions(-) create mode 100644 src/cli/AttachmentExport.cpp create mode 100644 src/cli/AttachmentExport.h create mode 100644 src/cli/AttachmentImport.cpp create mode 100644 src/cli/AttachmentImport.h create mode 100644 src/cli/AttachmentRemove.cpp create mode 100644 src/cli/AttachmentRemove.h create mode 100644 tests/data/Attachment.txt diff --git a/docs/man/keepassxc-cli.1.adoc b/docs/man/keepassxc-cli.1.adoc index 1cee5ad68..b5eabd550 100644 --- a/docs/man/keepassxc-cli.1.adoc +++ b/docs/man/keepassxc-cli.1.adoc @@ -40,6 +40,17 @@ It provides the ability to query and modify the entries of a KeePass database, d *analyze* [_options_] <__database__>:: Analyzes passwords in a database for weaknesses using offline HIBP SHA-1 hash lookup. +*attachment-export* [_options_] <__database__> <__entry__> <__attachment_name__> <__export_file__>:: + Exports the content of an attachment to a specified file. + Use *--stdout* option to instead output the contents of the attachment to stdout. + +*attachment-import* [_options_] <__database__> <__entry__> <__attachment_name__> <__import_file__>:: + Imports the attachment into an entry. + An existing attachment with the same name may be overwritten if the *-f* option is specified. + +*attachment-rm* <__database__> <__entry__> <__attachment_name__>:: + Removes the named attachment from an entry. + *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. If no attribute name is specified using the *-a* option, the password is copied. @@ -244,6 +255,9 @@ The same password generation options as documented for the generate command can *-s*, *--show-protected*:: Shows the protected attributes in clear text. +*--show-attachments*:: + Shows the attachment names along with the size of the attachments. + *-t*, *--totp*:: Also shows the current TOTP, reporting an error if no TOTP is configured for the entry. diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index c3ffb2d9c..dd87e1920 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -7386,6 +7386,94 @@ Please consider generating a new key file. %1 (invalid executable path) + + Export an attachment of an entry. + + + + Path of the entry with the target attachment. + + + + Name of the attachment to be exported. + + + + Path to which the attachment should be exported. + + + + Could not find attachment with name %1. + + + + No export target given. Please use '--stdout' or specify an 'export-file'. + + + + Could not open output file %1. + + + + Successfully exported attachment %1 of entry %2 to %3. + + + + Overwrite existing attachments. + + + + Imports an attachment to an entry. + + + + Path of the entry. + + + + Name of the attachment to be added. + + + + Path of the attachment to be imported. + + + + Attachment %1 already exists for entry %2. + + + + Could not open attachment file %1. + + + + Successfully imported attachment %1 as %2 to entry %3. + + + + Remove an attachment of an entry. + + + + Name of the attachment to be removed. + + + + Successfully removed attachment %1 from entry %2. + + + + Show the attachments of the entry. + + + + No attachments present. + + + + Attachments: + + QtIOCompressor diff --git a/src/cli/AttachmentExport.cpp b/src/cli/AttachmentExport.cpp new file mode 100644 index 000000000..46dc5f4b6 --- /dev/null +++ b/src/cli/AttachmentExport.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "AttachmentExport.h" + +#include "Utils.h" +#include "core/Group.h" + +#include +#include + +const QCommandLineOption AttachmentExport::StdoutOption = + QCommandLineOption(QStringList() << "stdout", QObject::tr("")); + +AttachmentExport::AttachmentExport() +{ + name = QString("attachment-export"); + description = QObject::tr("Export an attachment of an entry."); + options.append(AttachmentExport::StdoutOption); + positionalArguments.append( + {QString("entry"), QObject::tr("Path of the entry with the target attachment."), QString("")}); + positionalArguments.append( + {QString("attachment-name"), QObject::tr("Name of the attachment to be exported."), QString("")}); + optionalArguments.append( + {QString("export-file"), QObject::tr("Path to which the attachment should be exported."), QString("")}); +} + +int AttachmentExport::executeWithDatabase(QSharedPointer database, QSharedPointer parser) +{ + auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT; + auto& err = Utils::STDERR; + + auto args = parser->positionalArguments(); + auto entryPath = args.at(1); + + auto entry = database->rootGroup()->findEntryByPath(entryPath); + if (!entry) { + err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; + return EXIT_FAILURE; + } + + auto attachmentName = args.at(2); + + auto attachments = entry->attachments(); + if (!attachments->hasKey(attachmentName)) { + err << QObject::tr("Could not find attachment with name %1.").arg(attachmentName) << endl; + return EXIT_FAILURE; + } + + if (parser->isSet(AttachmentExport::StdoutOption)) { + // Output to STDOUT even in quiet mode + Utils::STDOUT << attachments->value(attachmentName) << flush; + return EXIT_SUCCESS; + } + + if (args.size() < 4) { + err << QObject::tr("No export target given. Please use '--stdout' or specify an 'export-file'.") << endl; + return EXIT_FAILURE; + } + + auto exportFileName = args.at(3); + QFile exportFile(exportFileName); + if (!exportFile.open(QIODevice::WriteOnly)) { + err << QObject::tr("Could not open output file %1.").arg(exportFileName) << endl; + return EXIT_FAILURE; + } + exportFile.write(attachments->value(attachmentName)); + + out << QObject::tr("Successfully exported attachment %1 of entry %2 to %3.") + .arg(attachmentName, entryPath, exportFileName) + << endl; + + return EXIT_SUCCESS; +} diff --git a/src/cli/AttachmentExport.h b/src/cli/AttachmentExport.h new file mode 100644 index 000000000..9fd8c15ec --- /dev/null +++ b/src/cli/AttachmentExport.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_ATTACHMENTEXPORT_H +#define KEEPASSXC_ATTACHMENTEXPORT_H + +#include "DatabaseCommand.h" + +class AttachmentExport : public DatabaseCommand +{ +public: + AttachmentExport(); + + int executeWithDatabase(QSharedPointer db, QSharedPointer parser) override; + + static const QCommandLineOption StdoutOption; +}; + +#endif // KEEPASSXC_ATTACHMENTEXPORT_H diff --git a/src/cli/AttachmentImport.cpp b/src/cli/AttachmentImport.cpp new file mode 100644 index 000000000..080f81af0 --- /dev/null +++ b/src/cli/AttachmentImport.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2019 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "AttachmentImport.h" + +#include "Utils.h" +#include "core/Group.h" + +#include +#include + +const QCommandLineOption AttachmentImport::ForceOption = + QCommandLineOption(QStringList() << "f" + << "force", + QObject::tr("Overwrite existing attachments.")); + +AttachmentImport::AttachmentImport() +{ + name = QString("attachment-import"); + description = QObject::tr("Imports an attachment to an entry."); + options.append(AttachmentImport::ForceOption); + positionalArguments.append({QString("entry"), QObject::tr("Path of the entry."), QString("")}); + positionalArguments.append( + {QString("attachment-name"), QObject::tr("Name of the attachment to be added."), QString("")}); + positionalArguments.append( + {QString("import-file"), QObject::tr("Path of the attachment to be imported."), QString("")}); +} + +int AttachmentImport::executeWithDatabase(QSharedPointer database, QSharedPointer parser) +{ + auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT; + auto& err = Utils::STDERR; + + auto args = parser->positionalArguments(); + auto entryPath = args.at(1); + + auto entry = database->rootGroup()->findEntryByPath(entryPath); + if (!entry) { + err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; + return EXIT_FAILURE; + } + + auto attachmentName = args.at(2); + + auto attachments = entry->attachments(); + if (attachments->hasKey(attachmentName) && !parser->isSet(AttachmentImport::ForceOption)) { + err << QObject::tr("Attachment %1 already exists for entry %2.").arg(attachmentName, entryPath) << endl; + return EXIT_FAILURE; + } + + auto importFileName = args.at(3); + + QFile importFile(importFileName); + if (!importFile.open(QIODevice::ReadOnly)) { + err << QObject::tr("Could not open attachment file %1.").arg(importFileName) << endl; + return EXIT_FAILURE; + } + + entry->beginUpdate(); + attachments->set(attachmentName, importFile.readAll()); + entry->endUpdate(); + + QString errorMessage; + if (!database->save(Database::Atomic, false, &errorMessage)) { + err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl; + return EXIT_FAILURE; + } + + out << QObject::tr("Successfully imported attachment %1 as %2 to entry %3.") + .arg(importFileName, attachmentName, entryPath) + << endl; + return EXIT_SUCCESS; +} diff --git a/src/cli/AttachmentImport.h b/src/cli/AttachmentImport.h new file mode 100644 index 000000000..ff6686af9 --- /dev/null +++ b/src/cli/AttachmentImport.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_ATTACHMENTIMPORT_H +#define KEEPASSXC_ATTACHMENTIMPORT_H + +#include "DatabaseCommand.h" + +class AttachmentImport : public DatabaseCommand +{ +public: + AttachmentImport(); + + int executeWithDatabase(QSharedPointer db, QSharedPointer parser) override; + + static const QCommandLineOption ForceOption; +}; + +#endif // KEEPASSXC_ATTACHMENTIMPORT_H diff --git a/src/cli/AttachmentRemove.cpp b/src/cli/AttachmentRemove.cpp new file mode 100644 index 000000000..6561948b0 --- /dev/null +++ b/src/cli/AttachmentRemove.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "AttachmentRemove.h" + +#include "Utils.h" +#include "core/Group.h" + +#include + +AttachmentRemove::AttachmentRemove() +{ + name = QString("attachment-rm"); + description = QObject::tr("Remove an attachment of an entry."); + positionalArguments.append( + {QString("entry"), QObject::tr("Path of the entry with the target attachment."), QString("")}); + positionalArguments.append({QString("name"), QObject::tr("Name of the attachment to be removed."), QString("")}); +} + +int AttachmentRemove::executeWithDatabase(QSharedPointer database, QSharedPointer parser) +{ + auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT; + auto& err = Utils::STDERR; + + auto args = parser->positionalArguments(); + auto entryPath = args.at(1); + + auto entry = database->rootGroup()->findEntryByPath(entryPath); + if (!entry) { + err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl; + return EXIT_FAILURE; + } + + auto attachmentName = args.at(2); + + auto attachments = entry->attachments(); + if (!attachments->hasKey(attachmentName)) { + err << QObject::tr("Could not find attachment with name %1.").arg(attachmentName) << endl; + return EXIT_FAILURE; + } + + entry->beginUpdate(); + attachments->remove(attachmentName); + entry->endUpdate(); + + QString errorMessage; + if (!database->save(Database::Atomic, false, &errorMessage)) { + err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl; + return EXIT_FAILURE; + } + + out << QObject::tr("Successfully removed attachment %1 from entry %2.").arg(attachmentName, entryPath) << endl; + return EXIT_SUCCESS; +} diff --git a/src/cli/AttachmentRemove.h b/src/cli/AttachmentRemove.h new file mode 100644 index 000000000..c6a494a3b --- /dev/null +++ b/src/cli/AttachmentRemove.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 KeePassXC Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_ATTACHMENTREMOVE_H +#define KEEPASSXC_ATTACHMENTREMOVE_H + +#include "DatabaseCommand.h" + +class AttachmentRemove : public DatabaseCommand +{ +public: + AttachmentRemove(); + + int executeWithDatabase(QSharedPointer db, QSharedPointer parser) override; +}; + +#endif // KEEPASSXC_ATTACHMENTMOVE_H diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 0b348d3c9..470af6283 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -17,6 +17,9 @@ set(cli_SOURCES Add.cpp AddGroup.cpp Analyze.cpp + AttachmentExport.cpp + AttachmentImport.cpp + AttachmentRemove.cpp Clip.cpp Close.cpp Create.cpp diff --git a/src/cli/Command.cpp b/src/cli/Command.cpp index 0699921ac..2eab320a3 100644 --- a/src/cli/Command.cpp +++ b/src/cli/Command.cpp @@ -18,6 +18,9 @@ #include "Add.h" #include "AddGroup.h" #include "Analyze.h" +#include "AttachmentExport.h" +#include "AttachmentImport.h" +#include "AttachmentRemove.h" #include "Clip.h" #include "Close.h" #include "Create.h" @@ -107,7 +110,7 @@ QString Command::getDescriptionLine() { QString response = name; QString space(" "); - QString spaces = space.repeated(15 - name.length()); + QString spaces = space.repeated(20 - name.length()); response = response.append(spaces); response = response.append(description); response = response.append("\n"); @@ -164,6 +167,9 @@ namespace Commands s_commands.insert(QStringLiteral("add"), QSharedPointer(new Add())); s_commands.insert(QStringLiteral("analyze"), QSharedPointer(new Analyze())); + s_commands.insert(QStringLiteral("attachment-export"), QSharedPointer(new AttachmentExport())); + s_commands.insert(QStringLiteral("attachment-import"), QSharedPointer(new AttachmentImport())); + s_commands.insert(QStringLiteral("attachment-rm"), QSharedPointer(new AttachmentRemove())); s_commands.insert(QStringLiteral("clip"), QSharedPointer(new Clip())); s_commands.insert(QStringLiteral("close"), QSharedPointer(new Close())); s_commands.insert(QStringLiteral("db-create"), QSharedPointer(new Create())); diff --git a/src/cli/Show.cpp b/src/cli/Show.cpp index e378c8e4a..f4d8097f6 100644 --- a/src/cli/Show.cpp +++ b/src/cli/Show.cpp @@ -19,6 +19,7 @@ #include "Utils.h" #include "core/Group.h" +#include "core/Tools.h" #include @@ -31,6 +32,9 @@ const QCommandLineOption Show::ProtectedAttributesOption = << "show-protected", QObject::tr("Show the protected attributes in clear text.")); +const QCommandLineOption Show::AttachmentsOption = + QCommandLineOption(QStringList() << "show-attachments", QObject::tr("Show the attachments of the entry.")); + const QCommandLineOption Show::AttributesOption = QCommandLineOption( QStringList() << "a" << "attributes", @@ -47,6 +51,7 @@ Show::Show() options.append(Show::TotpOption); options.append(Show::AttributesOption); options.append(Show::ProtectedAttributesOption); + options.append(Show::AttachmentsOption); positionalArguments.append({QString("entry"), QObject::tr("Name of the entry to show."), QString("")}); } @@ -104,6 +109,25 @@ int Show::executeWithDatabase(QSharedPointer database, QSharedPointer< } } + if (parser->isSet(Show::AttachmentsOption)) { + // Separate attachment output from attributes output via a newline. + out << endl; + + EntryAttachments* attachments = entry->attachments(); + if (attachments->isEmpty()) { + out << QObject::tr("No attachments present.") << endl; + } else { + out << QObject::tr("Attachments:") << endl; + + // Iterate over the attachments and output their names and size line-by-line, indented. + for (const QString& attachmentName : attachments->keys()) { + // TODO: use QLocale::formattedDataSize when >= Qt 5.10 + QString attachmentSize = Tools::humanReadableFileSize(attachments->value(attachmentName).size(), 1); + out << " " << attachmentName << " (" << attachmentSize << ")" << endl; + } + } + } + if (showTotp) { out << entry->totp() << endl; } diff --git a/src/cli/Show.h b/src/cli/Show.h index bf76c6973..93713dbd8 100644 --- a/src/cli/Show.h +++ b/src/cli/Show.h @@ -30,6 +30,7 @@ public: static const QCommandLineOption TotpOption; static const QCommandLineOption AttributesOption; static const QCommandLineOption ProtectedAttributesOption; + static const QCommandLineOption AttachmentsOption; }; #endif // KEEPASSXC_SHOW_H diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index 03e6f2439..a6f1a0d54 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -30,6 +30,9 @@ #include "cli/Add.h" #include "cli/AddGroup.h" #include "cli/Analyze.h" +#include "cli/AttachmentExport.h" +#include "cli/AttachmentImport.h" +#include "cli/AttachmentRemove.h" #include "cli/Clip.h" #include "cli/Create.h" #include "cli/Diceware.h" @@ -215,6 +218,9 @@ void TestCli::testBatchCommands() Commands::setupCommands(false); QVERIFY(Commands::getCommand("add")); QVERIFY(Commands::getCommand("analyze")); + QVERIFY(Commands::getCommand("attachment-export")); + QVERIFY(Commands::getCommand("attachment-import")); + QVERIFY(Commands::getCommand("attachment-rm")); QVERIFY(Commands::getCommand("clip")); QVERIFY(Commands::getCommand("close")); QVERIFY(Commands::getCommand("db-create")); @@ -236,7 +242,7 @@ void TestCli::testBatchCommands() QVERIFY(Commands::getCommand("show")); QVERIFY(Commands::getCommand("search")); QVERIFY(!Commands::getCommand("doesnotexist")); - QCOMPARE(Commands::getCommands().size(), 22); + QCOMPARE(Commands::getCommands().size(), 25); } void TestCli::testInteractiveCommands() @@ -244,6 +250,9 @@ void TestCli::testInteractiveCommands() Commands::setupCommands(true); QVERIFY(Commands::getCommand("add")); QVERIFY(Commands::getCommand("analyze")); + QVERIFY(Commands::getCommand("attachment-export")); + QVERIFY(Commands::getCommand("attachment-import")); + QVERIFY(Commands::getCommand("attachment-rm")); QVERIFY(Commands::getCommand("clip")); QVERIFY(Commands::getCommand("close")); QVERIFY(Commands::getCommand("db-create")); @@ -265,7 +274,7 @@ void TestCli::testInteractiveCommands() QVERIFY(Commands::getCommand("show")); QVERIFY(Commands::getCommand("search")); QVERIFY(!Commands::getCommand("doesnotexist")); - QCOMPARE(Commands::getCommands().size(), 22); + QCOMPARE(Commands::getCommands().size(), 25); } void TestCli::testAdd() @@ -435,6 +444,184 @@ void TestCli::testAnalyze() QCOMPARE(m_stderr->readAll(), QByteArray()); } +void TestCli::testAttachmentExport() +{ + AttachmentExport attachmentExportCmd; + QVERIFY(!attachmentExportCmd.name.isEmpty()); + QVERIFY(attachmentExportCmd.getDescriptionLine().contains(attachmentExportCmd.name)); + + TemporaryFile exportOutput; + exportOutput.open(QIODevice::WriteOnly); + exportOutput.close(); + + // Try exporting an attachment of a non-existent entry + setInput("a"); + execCmd(attachmentExportCmd, + {"attachment-export", + m_dbFile->fileName(), + "invalid_entry_path", + "invalid_attachment_name", + exportOutput.fileName()}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray("Could not find entry with path invalid_entry_path.\n")); + QCOMPARE(m_stdout->readAll(), QByteArray()); + + // Try exporting a non-existent attachment + setInput("a"); + execCmd(attachmentExportCmd, + {"attachment-export", + m_dbFile->fileName(), + "/Sample Entry", + "invalid_attachment_name", + exportOutput.fileName()}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray("Could not find attachment with name invalid_attachment_name.\n")); + QCOMPARE(m_stdout->readAll(), QByteArray()); + + // Export an existing attachment to a file + setInput("a"); + execCmd( + attachmentExportCmd, + {"attachment-export", m_dbFile->fileName(), "/Sample Entry", "Sample attachment.txt", exportOutput.fileName()}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray()); + QCOMPARE(m_stdout->readAll(), + QByteArray(qPrintable(QString("Successfully exported attachment %1 of entry %2 to %3.\n") + .arg("Sample attachment.txt", "/Sample Entry", exportOutput.fileName())))); + + exportOutput.open(QIODevice::ReadOnly); + QCOMPARE(exportOutput.readAll(), QByteArray("Sample content\n")); + + // Export an existing attachment to stdout + setInput("a"); + execCmd(attachmentExportCmd, + {"attachment-export", "--stdout", m_dbFile->fileName(), "/Sample Entry", "Sample attachment.txt"}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray()); + QCOMPARE(m_stdout->readAll(), QByteArray("Sample content\n")); + + // Ensure --stdout works even in quiet mode + setInput("a"); + execCmd( + attachmentExportCmd, + {"attachment-export", "--quiet", "--stdout", m_dbFile->fileName(), "/Sample Entry", "Sample attachment.txt"}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray()); + QCOMPARE(m_stdout->readAll(), QByteArray("Sample content\n")); +} + +void TestCli::testAttachmentImport() +{ + AttachmentImport attachmentImportCmd; + QVERIFY(!attachmentImportCmd.name.isEmpty()); + QVERIFY(attachmentImportCmd.getDescriptionLine().contains(attachmentImportCmd.name)); + + const QString attachmentPath = QString(KEEPASSX_TEST_DATA_DIR).append("/Attachment.txt"); + QVERIFY(QFile::exists(attachmentPath)); + + // Try importing an attachment to a non-existent entry + setInput("a"); + execCmd(attachmentImportCmd, + {"attachment-import", + m_dbFile->fileName(), + "invalid_entry_path", + "invalid_attachment_name", + "invalid_attachment_path"}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray("Could not find entry with path invalid_entry_path.\n")); + QCOMPARE(m_stdout->readAll(), QByteArray()); + + // Try importing an attachment with an occupied name without -f option + setInput("a"); + execCmd(attachmentImportCmd, + {"attachment-import", + m_dbFile->fileName(), + "/Sample Entry", + "Sample attachment.txt", + "invalid_attachment_path"}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), + QByteArray("Attachment Sample attachment.txt already exists for entry /Sample Entry.\n")); + QCOMPARE(m_stdout->readAll(), QByteArray()); + + // Try importing a non-existent attachment + setInput("a"); + execCmd(attachmentImportCmd, + {"attachment-import", + m_dbFile->fileName(), + "/Sample Entry", + "Sample attachment 2.txt", + "invalid_attachment_path"}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray("Could not open attachment file invalid_attachment_path.\n")); + QCOMPARE(m_stdout->readAll(), QByteArray()); + + // Try importing an attachment with an occupied name with -f option + setInput("a"); + execCmd( + attachmentImportCmd, + {"attachment-import", "-f", m_dbFile->fileName(), "/Sample Entry", "Sample attachment.txt", attachmentPath}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray()); + QCOMPARE(m_stdout->readAll(), + QByteArray(qPrintable( + QString("Successfully imported attachment %1 as Sample attachment.txt to entry /Sample Entry.\n") + .arg(attachmentPath)))); + + // Try importing an attachment with an unoccupied name + setInput("a"); + execCmd(attachmentImportCmd, + {"attachment-import", m_dbFile->fileName(), "/Sample Entry", "Attachment.txt", attachmentPath}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray()); + QCOMPARE( + m_stdout->readAll(), + QByteArray(qPrintable(QString("Successfully imported attachment %1 as Attachment.txt to entry /Sample Entry.\n") + .arg(attachmentPath)))); +} + +void TestCli::testAttachmentRemove() +{ + AttachmentRemove attachmentRemoveCmd; + QVERIFY(!attachmentRemoveCmd.name.isEmpty()); + QVERIFY(attachmentRemoveCmd.getDescriptionLine().contains(attachmentRemoveCmd.name)); + + // Try deleting an attachment belonging to an non-existent entry + setInput("a"); + execCmd(attachmentRemoveCmd, + {"attachment-rm", m_dbFile->fileName(), "invalid_entry_path", "invalid_attachment_name"}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray("Could not find entry with path invalid_entry_path.\n")); + QCOMPARE(m_stdout->readAll(), QByteArray()); + + // Try deleting a non-existent attachment from an entry + setInput("a"); + execCmd(attachmentRemoveCmd, {"attachment-rm", m_dbFile->fileName(), "/Sample Entry", "invalid_attachment_name"}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray("Could not find attachment with name invalid_attachment_name.\n")); + QCOMPARE(m_stdout->readAll(), QByteArray()); + + // Finally delete an existing attachment from an existing entry + auto db = readDatabase(); + QVERIFY(db); + + const Entry* entry = db->rootGroup()->findEntryByPath("/Sample Entry"); + QVERIFY(entry); + + QVERIFY(entry->attachments()->hasKey("Sample attachment.txt")); + + setInput("a"); + execCmd(attachmentRemoveCmd, {"attachment-rm", m_dbFile->fileName(), "/Sample Entry", "Sample attachment.txt"}); + m_stderr->readLine(); // skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray()); + QCOMPARE(m_stdout->readAll(), + QByteArray("Successfully removed attachment Sample attachment.txt from entry /Sample Entry.\n")); + + db = readDatabase(); + QVERIFY(db); + QVERIFY(!db->rootGroup()->findEntryByPath("/Sample Entry")->attachments()->hasKey("Sample attachment.txt")); +} + void TestCli::testClip() { if (QProcessEnvironment::systemEnvironment().contains("WAYLAND_DISPLAY")) { @@ -1751,6 +1938,33 @@ void TestCli::testShow() "URL: http://www.somesite.com/\n" "Notes: Notes\n")); + setInput("a"); + execCmd(showCmd, {"show", m_dbFile->fileName(), "--show-attachments", "/Sample Entry"}); + m_stderr->readLine(); // Skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray()); + QCOMPARE(m_stdout->readAll(), + QByteArray("Title: Sample Entry\n" + "UserName: User Name\n" + "Password: PROTECTED\n" + "URL: http://www.somesite.com/\n" + "Notes: Notes\n" + "\n" + "Attachments:\n" + " Sample attachment.txt (15.0 B)\n")); + + setInput("a"); + execCmd(showCmd, {"show", m_dbFile->fileName(), "--show-attachments", "/Homebanking/Subgroup/Subgroup Entry"}); + m_stderr->readLine(); // Skip password prompt + QCOMPARE(m_stderr->readAll(), QByteArray()); + QCOMPARE(m_stdout->readAll(), + QByteArray("Title: Subgroup Entry\n" + "UserName: Bank User Name\n" + "Password: PROTECTED\n" + "URL: https://www.bank.com\n" + "Notes: Important note\n" + "\n" + "No attachments present.\n")); + setInput("a"); execCmd(showCmd, {"show", "-a", "Title", m_dbFile->fileName(), "/Sample Entry"}); QCOMPARE(m_stdout->readAll(), QByteArray("Sample Entry\n")); diff --git a/tests/TestCli.h b/tests/TestCli.h index 299264dd7..bbe80a4b3 100644 --- a/tests/TestCli.h +++ b/tests/TestCli.h @@ -47,6 +47,9 @@ private slots: void testAdd(); void testAddGroup(); void testAnalyze(); + void testAttachmentExport(); + void testAttachmentImport(); + void testAttachmentRemove(); void testClip(); void testCommandParsing_data(); void testCommandParsing(); diff --git a/tests/data/Attachment.txt b/tests/data/Attachment.txt new file mode 100644 index 000000000..39c9f3681 --- /dev/null +++ b/tests/data/Attachment.txt @@ -0,0 +1 @@ +Content diff --git a/tests/data/NewDatabase.kdbx b/tests/data/NewDatabase.kdbx index dfffcc1e4e037bc90059f4b7fb1f81e5d2ed1d61..cf7b2ffaa5c3e264d6eeb0852bbdb571aec9c4ab 100644 GIT binary patch literal 24654 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 Kf5)_$rLq8B_$CPe literal 20590 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