mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
CLI: Use stderr for password prompt
Fixes #3398. Convert to QTextStream for all CLI IO and greatly improve CLI tests * Completely overhaul CLI tests to be much more streamlined and easy to read. Removed unnecessary code blocks by using existing functions. Co-authored-by: Emma Brooks <me@pluvano.com>
This commit is contained in:
parent
612f8d2e5b
commit
485852c9db
@ -69,16 +69,15 @@ Add::Add()
|
||||
|
||||
int Add::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream inputTextStream(Utils::STDIN, QIODevice::ReadOnly);
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
auto& entryPath = args.at(1);
|
||||
|
||||
// Cannot use those 2 options at the same time!
|
||||
if (parser->isSet(Add::GenerateOption) && parser->isSet(Add::PasswordPromptOption)) {
|
||||
errorTextStream << QObject::tr("Cannot generate a password and prompt at the same time!") << endl;
|
||||
err << QObject::tr("Cannot generate a password and prompt at the same time!") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -94,7 +93,7 @@ int Add::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<Q
|
||||
|
||||
Entry* entry = database->rootGroup()->addEntryWithPath(entryPath);
|
||||
if (!entry) {
|
||||
errorTextStream << QObject::tr("Could not create entry with path %1.").arg(entryPath) << endl;
|
||||
err << QObject::tr("Could not create entry with path %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -108,9 +107,9 @@ int Add::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<Q
|
||||
|
||||
if (parser->isSet(Add::PasswordPromptOption)) {
|
||||
if (!parser->isSet(Command::QuietOption)) {
|
||||
outputTextStream << QObject::tr("Enter password for new entry: ") << flush;
|
||||
out << QObject::tr("Enter password for new entry: ") << flush;
|
||||
}
|
||||
QString password = Utils::getPassword(parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT);
|
||||
QString password = Utils::getPassword(parser->isSet(Command::QuietOption));
|
||||
entry->setPassword(password);
|
||||
} else if (parser->isSet(Add::GenerateOption)) {
|
||||
QString password = passwordGenerator->generatePassword();
|
||||
@ -119,12 +118,12 @@ int Add::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<Q
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(&errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!parser->isSet(Command::QuietOption)) {
|
||||
outputTextStream << QObject::tr("Successfully added entry %1.").arg(entry->title()) << endl;
|
||||
out << QObject::tr("Successfully added entry %1.").arg(entry->title()) << endl;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ AddGroup::~AddGroup()
|
||||
|
||||
int AddGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& groupPath = args.at(1);
|
||||
@ -51,13 +51,13 @@ int AddGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedPoin
|
||||
|
||||
Group* group = database->rootGroup()->findGroupByPath(groupPath);
|
||||
if (group) {
|
||||
errorTextStream << QObject::tr("Group %1 already exists!").arg(groupPath) << endl;
|
||||
err << QObject::tr("Group %1 already exists!").arg(groupPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Group* parentGroup = database->rootGroup()->findGroupByPath(parentGroupPath);
|
||||
if (!parentGroup) {
|
||||
errorTextStream << QObject::tr("Group %1 not found.").arg(parentGroupPath) << endl;
|
||||
err << QObject::tr("Group %1 not found.").arg(parentGroupPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -68,12 +68,12 @@ int AddGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedPoin
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(&errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!parser->isSet(Command::QuietOption)) {
|
||||
outputTextStream << QObject::tr("Successfully added group %1.").arg(groupName) << endl;
|
||||
out << QObject::tr("Successfully added group %1.").arg(groupName) << endl;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -43,29 +43,27 @@ Analyze::Analyze()
|
||||
|
||||
int Analyze::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream inputTextStream(Utils::STDIN, QIODevice::ReadOnly);
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
QString hibpDatabase = parser->value(Analyze::HIBPDatabaseOption);
|
||||
QFile hibpFile(hibpDatabase);
|
||||
if (!hibpFile.open(QFile::ReadOnly)) {
|
||||
errorTextStream << QObject::tr("Failed to open HIBP file %1: %2").arg(hibpDatabase).arg(hibpFile.errorString())
|
||||
<< endl;
|
||||
err << QObject::tr("Failed to open HIBP file %1: %2").arg(hibpDatabase).arg(hibpFile.errorString()) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << QObject::tr("Evaluating database entries against HIBP file, this will take a while...") << endl;
|
||||
out << QObject::tr("Evaluating database entries against HIBP file, this will take a while...") << endl;
|
||||
|
||||
QList<QPair<const Entry*, int>> findings;
|
||||
QString error;
|
||||
if (!HibpOffline::report(database, hibpFile, findings, &error)) {
|
||||
errorTextStream << error << endl;
|
||||
err << error << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (auto& finding : findings) {
|
||||
printHibpFinding(finding.first, finding.second, outputTextStream);
|
||||
printHibpFinding(finding.first, finding.second, out);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
@ -53,32 +53,32 @@ Clip::Clip()
|
||||
|
||||
int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& entryPath = args.at(1);
|
||||
QString timeout;
|
||||
if (args.size() == 3) {
|
||||
timeout = args.at(2);
|
||||
}
|
||||
TextStream errorTextStream(Utils::STDERR);
|
||||
|
||||
int timeoutSeconds = 0;
|
||||
if (!timeout.isEmpty() && timeout.toInt() <= 0) {
|
||||
errorTextStream << QObject::tr("Invalid timeout value %1.").arg(timeout) << endl;
|
||||
err << QObject::tr("Invalid timeout value %1.").arg(timeout) << endl;
|
||||
return EXIT_FAILURE;
|
||||
} else if (!timeout.isEmpty()) {
|
||||
timeoutSeconds = timeout.toInt();
|
||||
}
|
||||
|
||||
TextStream outputTextStream(parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT,
|
||||
QIODevice::WriteOnly);
|
||||
Entry* entry = database->rootGroup()->findEntryByPath(entryPath);
|
||||
if (!entry) {
|
||||
errorTextStream << QObject::tr("Entry %1 not found.").arg(entryPath) << endl;
|
||||
err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (parser->isSet(AttributeOption) && parser->isSet(TotpOption)) {
|
||||
errorTextStream << QObject::tr("ERROR: Please specify one of --attribute or --totp, not both.") << endl;
|
||||
err << QObject::tr("ERROR: Please specify one of --attribute or --totp, not both.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
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;
|
||||
err << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -96,9 +96,9 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
} else {
|
||||
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;
|
||||
err << 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;
|
||||
@ -108,7 +108,7 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
outputTextStream << QObject::tr("Attribute \"%1\" not found.").arg(selectedAttribute) << endl;
|
||||
out << QObject::tr("Attribute \"%1\" not found.").arg(selectedAttribute) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
outputTextStream << QObject::tr("Entry's \"%1\" attribute copied to the clipboard!").arg(selectedAttribute) << endl;
|
||||
out << QObject::tr("Entry's \"%1\" attribute copied to the clipboard!").arg(selectedAttribute) << endl;
|
||||
|
||||
if (!timeoutSeconds) {
|
||||
return exitCode;
|
||||
@ -125,15 +125,15 @@ int Clip::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
|
||||
QString lastLine = "";
|
||||
while (timeoutSeconds > 0) {
|
||||
outputTextStream << '\r' << QString(lastLine.size(), ' ') << '\r';
|
||||
out << '\r' << QString(lastLine.size(), ' ') << '\r';
|
||||
lastLine = QObject::tr("Clearing the clipboard in %1 second(s)...", "", timeoutSeconds).arg(timeoutSeconds);
|
||||
outputTextStream << lastLine << flush;
|
||||
out << lastLine << flush;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
--timeoutSeconds;
|
||||
}
|
||||
Utils::clipText("");
|
||||
outputTextStream << '\r' << QString(lastLine.size(), ' ') << '\r';
|
||||
outputTextStream << QObject::tr("Clipboard cleared!") << endl;
|
||||
out << '\r' << QString(lastLine.size(), ' ') << '\r';
|
||||
out << QObject::tr("Clipboard cleared!") << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -126,24 +126,24 @@ QString Command::getHelpText()
|
||||
|
||||
QSharedPointer<QCommandLineParser> Command::getCommandLineParser(const QStringList& arguments)
|
||||
{
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& err = Utils::STDERR;
|
||||
QSharedPointer<QCommandLineParser> parser = buildParser(this);
|
||||
|
||||
if (!parser->parse(arguments)) {
|
||||
errorTextStream << parser->errorText() << "\n\n";
|
||||
errorTextStream << getHelpText();
|
||||
err << parser->errorText() << "\n\n";
|
||||
err << getHelpText();
|
||||
return {};
|
||||
}
|
||||
if (parser->positionalArguments().size() < positionalArguments.size()) {
|
||||
errorTextStream << getHelpText();
|
||||
err << getHelpText();
|
||||
return {};
|
||||
}
|
||||
if (parser->positionalArguments().size() > (positionalArguments.size() + optionalArguments.size())) {
|
||||
errorTextStream << getHelpText();
|
||||
err << getHelpText();
|
||||
return {};
|
||||
}
|
||||
if (parser->isSet(HelpOption)) {
|
||||
errorTextStream << getHelpText();
|
||||
err << getHelpText();
|
||||
return {};
|
||||
}
|
||||
return parser;
|
||||
|
@ -77,10 +77,8 @@ int Create::execute(const QStringList& arguments)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
bool quiet = parser->isSet(Command::QuietOption);
|
||||
|
||||
QTextStream out(quiet ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
|
||||
@ -110,7 +108,7 @@ int Create::execute(const QStringList& arguments)
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
|
||||
if (parser->isSet(Create::SetPasswordOption)) {
|
||||
auto passwordKey = Utils::getPasswordFromStdin();
|
||||
auto passwordKey = Utils::getConfirmedPassword();
|
||||
if (passwordKey.isNull()) {
|
||||
err << QObject::tr("Failed to set database password.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
@ -151,7 +149,7 @@ int Create::execute(const QStringList& arguments)
|
||||
bool ok = db->changeKdf(kdf);
|
||||
|
||||
if (!ok) {
|
||||
err << QObject::tr("Error while setting database key derivation settings.") << endl;
|
||||
err << QObject::tr("error while setting database key derivation settings.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
@ -180,8 +178,7 @@ int Create::execute(const QStringList& arguments)
|
||||
*/
|
||||
bool Create::loadFileKey(const QString& path, QSharedPointer<FileKey>& fileKey)
|
||||
{
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
auto& err = Utils::STDERR;
|
||||
QString error;
|
||||
fileKey = QSharedPointer<FileKey>(new FileKey());
|
||||
|
||||
|
@ -57,8 +57,7 @@ int DatabaseCommand::execute(const QStringList& arguments)
|
||||
#else
|
||||
"",
|
||||
#endif
|
||||
parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT,
|
||||
Utils::STDERR);
|
||||
parser->isSet(Command::QuietOption));
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
@ -51,8 +51,8 @@ int Diceware::execute(const QStringList& arguments)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
PassphraseGenerator dicewareGenerator;
|
||||
|
||||
@ -60,7 +60,7 @@ int Diceware::execute(const QStringList& arguments)
|
||||
if (wordCount.isEmpty()) {
|
||||
dicewareGenerator.setWordCount(PassphraseGenerator::DefaultWordCount);
|
||||
} else if (wordCount.toInt() <= 0) {
|
||||
errorTextStream << QObject::tr("Invalid word count %1").arg(wordCount) << endl;
|
||||
err << QObject::tr("Invalid word count %1").arg(wordCount) << endl;
|
||||
return EXIT_FAILURE;
|
||||
} else {
|
||||
dicewareGenerator.setWordCount(wordCount.toInt());
|
||||
@ -74,12 +74,12 @@ int Diceware::execute(const QStringList& arguments)
|
||||
if (!dicewareGenerator.isValid()) {
|
||||
// We already validated the word count input so if the generator is invalid, it
|
||||
// must be because the word list is too small.
|
||||
errorTextStream << QObject::tr("The word list is too small (< 1000 items)") << endl;
|
||||
err << QObject::tr("The word list is too small (< 1000 items)") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QString password = dicewareGenerator.generatePassphrase();
|
||||
outputTextStream << password << endl;
|
||||
out << password << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -60,16 +60,15 @@ Edit::Edit()
|
||||
|
||||
int Edit::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream outputTextStream(parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT,
|
||||
QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& entryPath = args.at(1);
|
||||
|
||||
// Cannot use those 2 options at the same time!
|
||||
if (parser->isSet(Add::GenerateOption) && parser->isSet(Add::PasswordPromptOption)) {
|
||||
errorTextStream << QObject::tr("Cannot generate a password and prompt at the same time!") << endl;
|
||||
err << QObject::tr("Cannot generate a password and prompt at the same time!") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -86,7 +85,7 @@ int Edit::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
|
||||
Entry* entry = database->rootGroup()->findEntryByPath(entryPath);
|
||||
if (!entry) {
|
||||
errorTextStream << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl;
|
||||
err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -95,7 +94,7 @@ int Edit::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
QString title = parser->value(Edit::TitleOption);
|
||||
bool prompt = parser->isSet(Add::PasswordPromptOption);
|
||||
if (username.isEmpty() && url.isEmpty() && title.isEmpty() && !prompt && !generate) {
|
||||
errorTextStream << QObject::tr("Not changing any field for entry %1.").arg(entryPath) << endl;
|
||||
err << QObject::tr("Not changing any field for entry %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -114,8 +113,8 @@ int Edit::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
}
|
||||
|
||||
if (prompt) {
|
||||
outputTextStream << QObject::tr("Enter new password for entry: ") << flush;
|
||||
QString password = Utils::getPassword(parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT);
|
||||
out << QObject::tr("Enter new password for entry: ") << flush;
|
||||
QString password = Utils::getPassword(parser->isSet(Command::QuietOption));
|
||||
entry->setPassword(password);
|
||||
} else if (generate) {
|
||||
QString password = passwordGenerator->generatePassword();
|
||||
@ -126,10 +125,10 @@ int Edit::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(&errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << QObject::tr("Successfully edited entry %1.").arg(entry->title()) << endl;
|
||||
out << QObject::tr("Successfully edited entry %1.").arg(entry->title()) << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ Estimate::Estimate()
|
||||
|
||||
static void estimate(const char* pwd, bool advanced)
|
||||
{
|
||||
TextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
|
||||
int len = static_cast<int>(strlen(pwd));
|
||||
if (!advanced) {
|
||||
@ -163,14 +163,14 @@ int Estimate::execute(const QStringList& arguments)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
TextStream inputTextStream(Utils::STDIN, QIODevice::ReadOnly);
|
||||
auto& in = Utils::STDIN;
|
||||
const QStringList args = parser->positionalArguments();
|
||||
|
||||
QString password;
|
||||
if (args.size() == 1) {
|
||||
password = args.at(0);
|
||||
} else {
|
||||
password = inputTextStream.readLine();
|
||||
password = in.readLine();
|
||||
}
|
||||
|
||||
estimate(password.toLatin1(), parser->isSet(Estimate::AdvancedOption));
|
||||
|
@ -40,23 +40,23 @@ Export::Export()
|
||||
|
||||
int Export::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
TextStream out(Utils::STDOUT.device());
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
QString format = parser->value(Export::FormatOption);
|
||||
if (format.isEmpty() || format.startsWith(QStringLiteral("xml"), Qt::CaseInsensitive)) {
|
||||
QByteArray xmlData;
|
||||
QString errorMessage;
|
||||
if (!database->extract(xmlData, &errorMessage)) {
|
||||
errorTextStream << QObject::tr("Unable to export database to XML: %1").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Unable to export database to XML: %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
outputTextStream.write(xmlData.constData());
|
||||
out.write(xmlData.constData());
|
||||
} else if (format.startsWith(QStringLiteral("csv"), Qt::CaseInsensitive)) {
|
||||
CsvExporter csvExporter;
|
||||
outputTextStream << csvExporter.exportDatabase(database);
|
||||
out << csvExporter.exportDatabase(database);
|
||||
} else {
|
||||
errorTextStream << QObject::tr("Unsupported format %1").arg(format) << endl;
|
||||
err << QObject::tr("Unsupported format %1").arg(format) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -80,13 +80,13 @@ Generate::Generate()
|
||||
*/
|
||||
QSharedPointer<PasswordGenerator> Generate::createGenerator(QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& err = Utils::STDERR;
|
||||
QSharedPointer<PasswordGenerator> passwordGenerator = QSharedPointer<PasswordGenerator>(new PasswordGenerator());
|
||||
QString passwordLength = parser->value(Generate::PasswordLengthOption);
|
||||
if (passwordLength.isEmpty()) {
|
||||
passwordGenerator->setLength(PasswordGenerator::DefaultLength);
|
||||
} else if (passwordLength.toInt() <= 0) {
|
||||
errorTextStream << QObject::tr("Invalid password length %1").arg(passwordLength) << endl;
|
||||
err << QObject::tr("Invalid password length %1").arg(passwordLength) << endl;
|
||||
return QSharedPointer<PasswordGenerator>(nullptr);
|
||||
} else {
|
||||
passwordGenerator->setLength(passwordLength.toInt());
|
||||
@ -126,7 +126,7 @@ QSharedPointer<PasswordGenerator> Generate::createGenerator(QSharedPointer<QComm
|
||||
passwordGenerator->setExcludedChars(parser->value(Generate::ExcludeCharsOption));
|
||||
|
||||
if (!passwordGenerator->isValid()) {
|
||||
errorTextStream << QObject::tr("Invalid password generator after applying all options") << endl;
|
||||
err << QObject::tr("Invalid password generator after applying all options") << endl;
|
||||
return QSharedPointer<PasswordGenerator>(nullptr);
|
||||
}
|
||||
|
||||
@ -145,9 +145,9 @@ int Generate::execute(const QStringList& arguments)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
QString password = passwordGenerator->generatePassword();
|
||||
outputTextStream << password << endl;
|
||||
out << password << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ Help::Help()
|
||||
|
||||
int Help::execute(const QStringList& arguments)
|
||||
{
|
||||
TextStream out(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
QSharedPointer<Command> command;
|
||||
if (arguments.size() > 1 && (command = Commands::getCommand(arguments.at(1)))) {
|
||||
out << command->getHelpText();
|
||||
@ -39,5 +39,6 @@ int Help::execute(const QStringList& arguments)
|
||||
out << cmd->getDescriptionLine();
|
||||
}
|
||||
}
|
||||
out.flush();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -55,30 +55,29 @@ int Import::execute(const QStringList& arguments)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
TextStream outputTextStream(parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT,
|
||||
QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& xmlExportPath = args.at(0);
|
||||
const QString& dbPath = args.at(1);
|
||||
|
||||
if (QFileInfo::exists(dbPath)) {
|
||||
errorTextStream << QObject::tr("File %1 already exists.").arg(dbPath) << endl;
|
||||
err << QObject::tr("File %1 already exists.").arg(dbPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
|
||||
auto passwordKey = Utils::getPasswordFromStdin();
|
||||
auto passwordKey = Utils::getConfirmedPassword();
|
||||
if (passwordKey.isNull()) {
|
||||
errorTextStream << QObject::tr("Failed to set database password.") << endl;
|
||||
err << QObject::tr("Failed to set database password.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
key->addKey(passwordKey);
|
||||
|
||||
if (key->isEmpty()) {
|
||||
errorTextStream << QObject::tr("No key is set. Aborting database creation.") << endl;
|
||||
err << QObject::tr("No key is set. Aborting database creation.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -88,15 +87,15 @@ int Import::execute(const QStringList& arguments)
|
||||
db.setKey(key);
|
||||
|
||||
if (!db.import(xmlExportPath, &errorMessage)) {
|
||||
errorTextStream << QObject::tr("Unable to import XML database: %1").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Unable to import XML database: %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!db.saveAs(dbPath, &errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Failed to save the database: %1.").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Failed to save the database: %1.").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << QObject::tr("Successfully imported database.") << endl;
|
||||
out << QObject::tr("Successfully imported database.") << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ Info::Info()
|
||||
|
||||
int Info::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser>)
|
||||
{
|
||||
TextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
|
||||
out << QObject::tr("UUID: ") << database->uuid().toString() << endl;
|
||||
out << QObject::tr("Name: ") << database->metadata()->name() << endl;
|
||||
|
@ -47,8 +47,8 @@ List::List()
|
||||
|
||||
int List::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
bool recursive = parser->isSet(List::RecursiveOption);
|
||||
@ -56,17 +56,17 @@ int List::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
|
||||
// No group provided, defaulting to root group.
|
||||
if (args.size() == 1) {
|
||||
outputTextStream << database->rootGroup()->print(recursive, flatten) << flush;
|
||||
out << database->rootGroup()->print(recursive, flatten) << flush;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
const QString& groupPath = args.at(1);
|
||||
Group* group = database->rootGroup()->findGroupByPath(groupPath);
|
||||
if (!group) {
|
||||
errorTextStream << QObject::tr("Cannot find group %1.").arg(groupPath) << endl;
|
||||
err << QObject::tr("Cannot find group %1.").arg(groupPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << group->print(recursive, flatten) << flush;
|
||||
out << group->print(recursive, flatten) << flush;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -38,20 +38,20 @@ Locate::Locate()
|
||||
|
||||
int Locate::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& searchTerm = args.at(1);
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QStringList results = database->rootGroup()->locate(searchTerm);
|
||||
if (results.isEmpty()) {
|
||||
errorTextStream << "No results for that search term." << endl;
|
||||
err << "No results for that search term." << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (const QString& result : asConst(results)) {
|
||||
outputTextStream << result << endl;
|
||||
out << result << endl;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -61,9 +61,8 @@ Merge::Merge()
|
||||
|
||||
int Merge::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream outputTextStream(parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT,
|
||||
QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
|
||||
@ -76,8 +75,7 @@ int Merge::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer
|
||||
!parser->isSet(Merge::NoPasswordFromOption),
|
||||
parser->value(Merge::KeyFileFromOption),
|
||||
parser->value(Merge::YubiKeyFromOption),
|
||||
parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT,
|
||||
Utils::STDERR);
|
||||
parser->isSet(Command::QuietOption));
|
||||
if (!db2) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@ -85,7 +83,7 @@ int Merge::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer
|
||||
db2 = QSharedPointer<Database>::create();
|
||||
QString errorMessage;
|
||||
if (!db2->open(fromDatabasePath, database->key(), &errorMessage, false)) {
|
||||
errorTextStream << QObject::tr("Error reading merge file:\n%1").arg(errorMessage);
|
||||
err << QObject::tr("Error reading merge file:\n%1").arg(errorMessage);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
@ -94,19 +92,18 @@ int Merge::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer
|
||||
QStringList changeList = merger.merge();
|
||||
|
||||
for (auto& mergeChange : changeList) {
|
||||
outputTextStream << "\t" << mergeChange << endl;
|
||||
out << "\t" << mergeChange << endl;
|
||||
}
|
||||
|
||||
if (!changeList.isEmpty() && !parser->isSet(Merge::DryRunOption)) {
|
||||
QString errorMessage;
|
||||
if (!database->save(&errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
outputTextStream << QObject::tr("Successfully merged %1 into %2.").arg(fromDatabasePath, toDatabasePath)
|
||||
<< endl;
|
||||
out << QObject::tr("Successfully merged %1 into %2.").arg(fromDatabasePath, toDatabasePath) << endl;
|
||||
} else {
|
||||
outputTextStream << QObject::tr("Database was not modified by merge operation.") << endl;
|
||||
out << QObject::tr("Database was not modified by merge operation.") << endl;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
@ -40,8 +40,8 @@ Move::~Move()
|
||||
|
||||
int Move::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& entryPath = args.at(1);
|
||||
@ -49,18 +49,18 @@ int Move::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
|
||||
Entry* entry = database->rootGroup()->findEntryByPath(entryPath);
|
||||
if (!entry) {
|
||||
errorTextStream << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl;
|
||||
err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Group* destinationGroup = database->rootGroup()->findGroupByPath(destinationPath);
|
||||
if (!destinationGroup) {
|
||||
errorTextStream << QObject::tr("Could not find group with path %1.").arg(destinationPath) << endl;
|
||||
err << QObject::tr("Could not find group with path %1.").arg(destinationPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (destinationGroup == entry->parent()) {
|
||||
errorTextStream << QObject::tr("Entry is already in group %1.").arg(destinationPath) << endl;
|
||||
err << QObject::tr("Entry is already in group %1.").arg(destinationPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -70,11 +70,10 @@ int Move::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(&errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << QObject::tr("Successfully moved entry %1 to group %2.").arg(entry->title(), destinationPath)
|
||||
<< endl;
|
||||
out << QObject::tr("Successfully moved entry %1 to group %2.").arg(entry->title(), destinationPath) << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -37,15 +37,13 @@ Remove::Remove()
|
||||
|
||||
int Remove::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
bool quiet = parser->isSet(Command::QuietOption);
|
||||
auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
auto& entryPath = parser->positionalArguments().at(1);
|
||||
|
||||
TextStream outputTextStream(quiet ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QPointer<Entry> entry = database->rootGroup()->findEntryByPath(entryPath);
|
||||
if (!entry) {
|
||||
errorTextStream << QObject::tr("Entry %1 not found.").arg(entryPath) << endl;
|
||||
err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -61,14 +59,14 @@ int Remove::executeWithDatabase(QSharedPointer<Database> database, QSharedPointe
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(&errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (recycled) {
|
||||
outputTextStream << QObject::tr("Successfully recycled entry %1.").arg(entryTitle) << endl;
|
||||
out << QObject::tr("Successfully recycled entry %1.").arg(entryTitle) << endl;
|
||||
} else {
|
||||
outputTextStream << QObject::tr("Successfully deleted entry %1.").arg(entryTitle) << endl;
|
||||
out << QObject::tr("Successfully deleted entry %1.").arg(entryTitle) << endl;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
@ -41,21 +41,20 @@ RemoveGroup::~RemoveGroup()
|
||||
|
||||
int RemoveGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
bool quiet = parser->isSet(Command::QuietOption);
|
||||
QString groupPath = parser->positionalArguments().at(1);
|
||||
auto& out = parser->isSet(Command::QuietOption) ? Utils::DEVNULL : Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
TextStream outputTextStream(quiet ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
QString groupPath = parser->positionalArguments().at(1);
|
||||
|
||||
// Recursive option means were looking for a group to remove.
|
||||
QPointer<Group> group = database->rootGroup()->findGroupByPath(groupPath);
|
||||
if (!group) {
|
||||
errorTextStream << QObject::tr("Group %1 not found.").arg(groupPath) << endl;
|
||||
err << QObject::tr("Group %1 not found.").arg(groupPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (group == database->rootGroup()) {
|
||||
errorTextStream << QObject::tr("Cannot remove root group from database.") << endl;
|
||||
err << QObject::tr("Cannot remove root group from database.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -70,14 +69,14 @@ int RemoveGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedP
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(&errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
||||
err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (recycled) {
|
||||
outputTextStream << QObject::tr("Successfully recycled group %1.").arg(groupPath) << endl;
|
||||
out << QObject::tr("Successfully recycled group %1.").arg(groupPath) << endl;
|
||||
} else {
|
||||
outputTextStream << QObject::tr("Successfully deleted group %1.").arg(groupPath) << endl;
|
||||
out << QObject::tr("Successfully deleted group %1.").arg(groupPath) << endl;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
@ -59,8 +59,8 @@ Show::Show()
|
||||
|
||||
int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& entryPath = args.at(1);
|
||||
@ -70,12 +70,12 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
|
||||
Entry* entry = database->rootGroup()->findEntryByPath(entryPath);
|
||||
if (!entry) {
|
||||
errorTextStream << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl;
|
||||
err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (showTotp && !entry->hasTotp()) {
|
||||
errorTextStream << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl;
|
||||
err << QObject::tr("Entry with path %1 has no TOTP set up.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -91,28 +91,28 @@ int Show::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<
|
||||
QStringList attrs = Utils::findAttributes(*entry->attributes(), attributeName);
|
||||
if (attrs.isEmpty()) {
|
||||
encounteredError = true;
|
||||
errorTextStream << QObject::tr("ERROR: unknown attribute %1.").arg(attributeName) << endl;
|
||||
err << 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;
|
||||
err << QObject::tr("ERROR: attribute %1 is ambiguous, it matches %2.")
|
||||
.arg(attributeName, QLocale().createSeparatedList(attrs))
|
||||
<< endl;
|
||||
continue;
|
||||
}
|
||||
QString canonicalName = attrs[0];
|
||||
if (showDefaultAttributes) {
|
||||
outputTextStream << canonicalName << ": ";
|
||||
out << canonicalName << ": ";
|
||||
}
|
||||
if (entry->attributes()->isProtected(canonicalName) && showDefaultAttributes && !showProtectedAttributes) {
|
||||
outputTextStream << "PROTECTED" << endl;
|
||||
out << "PROTECTED" << endl;
|
||||
} else {
|
||||
outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(canonicalName)) << endl;
|
||||
out << entry->resolveMultiplePlaceholders(entry->attributes()->value(canonicalName)) << endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (showTotp) {
|
||||
outputTextStream << entry->totp() << endl;
|
||||
out << entry->totp() << endl;
|
||||
}
|
||||
|
||||
return encounteredError ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
@ -30,29 +30,33 @@
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
/**
|
||||
* STDOUT file handle for the CLI.
|
||||
*/
|
||||
FILE* STDOUT = stdout;
|
||||
QTextStream STDOUT;
|
||||
QTextStream STDERR;
|
||||
QTextStream STDIN;
|
||||
QTextStream DEVNULL;
|
||||
|
||||
/**
|
||||
* STDERR file handle for the CLI.
|
||||
*/
|
||||
FILE* STDERR = stderr;
|
||||
void setDefaultTextStreams()
|
||||
{
|
||||
auto fd = new QFile();
|
||||
fd->open(stdout, QIODevice::WriteOnly);
|
||||
STDOUT.setDevice(fd);
|
||||
|
||||
/**
|
||||
* STDIN file handle for the CLI.
|
||||
*/
|
||||
FILE* STDIN = stdin;
|
||||
fd = new QFile();
|
||||
fd->open(stderr, QIODevice::WriteOnly);
|
||||
STDERR.setDevice(fd);
|
||||
|
||||
/**
|
||||
* DEVNULL file handle for the CLI.
|
||||
*/
|
||||
fd = new QFile();
|
||||
fd->open(stdin, QIODevice::ReadOnly);
|
||||
STDIN.setDevice(fd);
|
||||
|
||||
fd = new QFile();
|
||||
#ifdef Q_OS_WIN
|
||||
FILE* DEVNULL = fopen("nul", "w");
|
||||
fd->open(fopen("nul", "w"), QIODevice::WriteOnly);
|
||||
#else
|
||||
FILE* DEVNULL = fopen("/dev/null", "w");
|
||||
fd->open(fopen("/dev/null", "w"), QIODevice::WriteOnly);
|
||||
#endif
|
||||
DEVNULL.setDevice(fd);
|
||||
}
|
||||
|
||||
void setStdinEcho(bool enable = true)
|
||||
{
|
||||
@ -82,36 +86,14 @@ namespace Utils
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace Test
|
||||
{
|
||||
QStringList nextPasswords = {};
|
||||
|
||||
/**
|
||||
* Set the next password returned by \link getPassword() instead of reading it from STDIN.
|
||||
* Multiple calls to this method will fill a queue of passwords.
|
||||
* This function is intended for testing purposes.
|
||||
*
|
||||
* @param password password to return next
|
||||
*/
|
||||
void setNextPassword(const QString& password, bool repeat)
|
||||
{
|
||||
nextPasswords.append(password);
|
||||
if (repeat) {
|
||||
nextPasswords.append(password);
|
||||
}
|
||||
}
|
||||
} // namespace Test
|
||||
|
||||
QSharedPointer<Database> unlockDatabase(const QString& databaseFilename,
|
||||
const bool isPasswordProtected,
|
||||
const QString& keyFilename,
|
||||
const QString& yubiKeySlot,
|
||||
FILE* outputDescriptor,
|
||||
FILE* errorDescriptor)
|
||||
bool quiet)
|
||||
{
|
||||
auto& err = quiet ? DEVNULL : STDERR;
|
||||
auto compositeKey = QSharedPointer<CompositeKey>::create();
|
||||
TextStream out(outputDescriptor);
|
||||
TextStream err(errorDescriptor);
|
||||
|
||||
QFileInfo dbFileInfo(databaseFilename);
|
||||
if (dbFileInfo.canonicalFilePath().isEmpty()) {
|
||||
@ -130,8 +112,8 @@ namespace Utils
|
||||
}
|
||||
|
||||
if (isPasswordProtected) {
|
||||
out << QObject::tr("Enter password to unlock %1: ").arg(databaseFilename) << flush;
|
||||
QString line = Utils::getPassword(outputDescriptor);
|
||||
err << QObject::tr("Enter password to unlock %1: ").arg(databaseFilename) << flush;
|
||||
QString line = Utils::getPassword(quiet);
|
||||
auto passwordKey = QSharedPointer<PasswordKey>::create();
|
||||
passwordKey->setPassword(line);
|
||||
compositeKey->addKey(passwordKey);
|
||||
@ -177,7 +159,7 @@ namespace Utils
|
||||
slot,
|
||||
blocking,
|
||||
QObject::tr("Please touch the button on your YubiKey to unlock %1").arg(databaseFilename),
|
||||
outputDescriptor));
|
||||
err.device()));
|
||||
compositeKey->addChallengeResponseKey(key);
|
||||
}
|
||||
#else
|
||||
@ -200,19 +182,10 @@ namespace Utils
|
||||
*
|
||||
* @return the password
|
||||
*/
|
||||
QString getPassword(FILE* outputDescriptor)
|
||||
QString getPassword(bool quiet)
|
||||
{
|
||||
TextStream out(outputDescriptor, QIODevice::WriteOnly);
|
||||
|
||||
// return preset password if one is set
|
||||
if (!Test::nextPasswords.isEmpty()) {
|
||||
auto password = Test::nextPasswords.takeFirst();
|
||||
// simulate user entering newline
|
||||
out << endl;
|
||||
return password;
|
||||
}
|
||||
|
||||
static TextStream in(STDIN, QIODevice::ReadOnly);
|
||||
auto& in = STDIN;
|
||||
auto& out = quiet ? DEVNULL : STDERR;
|
||||
|
||||
setStdinEcho(false);
|
||||
QString line = in.readLine();
|
||||
@ -228,37 +201,34 @@ namespace Utils
|
||||
* @return Pointer to the PasswordKey or null if passwordkey is skipped
|
||||
* by user
|
||||
*/
|
||||
QSharedPointer<PasswordKey> getPasswordFromStdin()
|
||||
QSharedPointer<PasswordKey> getConfirmedPassword()
|
||||
{
|
||||
QSharedPointer<PasswordKey> passwordKey;
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
auto& err = STDERR;
|
||||
auto& in = STDIN;
|
||||
|
||||
out << QObject::tr("Enter password to encrypt database (optional): ");
|
||||
out.flush();
|
||||
QSharedPointer<PasswordKey> passwordKey;
|
||||
|
||||
err << QObject::tr("Enter password to encrypt database (optional): ");
|
||||
err.flush();
|
||||
auto password = Utils::getPassword();
|
||||
|
||||
if (password.isEmpty()) {
|
||||
out << QObject::tr("Do you want to create a database with an empty password? [y/N]: ");
|
||||
out.flush();
|
||||
TextStream ts(STDIN, QIODevice::ReadOnly);
|
||||
if (!ts.device()->isSequential()) {
|
||||
// This is required for testing on macOS
|
||||
ts.seek(0);
|
||||
}
|
||||
auto ans = ts.readLine();
|
||||
err << QObject::tr("Do you want to create a database with an empty password? [y/N]: ");
|
||||
err.flush();
|
||||
auto ans = in.readLine();
|
||||
if (ans.toLower().startsWith("y")) {
|
||||
passwordKey = QSharedPointer<PasswordKey>::create("");
|
||||
}
|
||||
out << endl;
|
||||
err << endl;
|
||||
} else {
|
||||
out << QObject::tr("Repeat password: ");
|
||||
out.flush();
|
||||
err << QObject::tr("Repeat password: ");
|
||||
err.flush();
|
||||
auto repeat = Utils::getPassword();
|
||||
|
||||
if (password == repeat) {
|
||||
passwordKey = QSharedPointer<PasswordKey>::create(password);
|
||||
} else {
|
||||
out << QObject::tr("Error: Passwords do not match.") << endl;
|
||||
err << QObject::tr("Error: Passwords do not match.") << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,7 +241,7 @@ namespace Utils
|
||||
*/
|
||||
int clipText(const QString& text)
|
||||
{
|
||||
TextStream err(Utils::STDERR);
|
||||
auto& err = STDERR;
|
||||
|
||||
// List of programs and their arguments
|
||||
QList<QPair<QString, QString>> clipPrograms;
|
||||
|
@ -34,21 +34,22 @@
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
extern FILE* STDOUT;
|
||||
extern FILE* STDERR;
|
||||
extern FILE* STDIN;
|
||||
extern FILE* DEVNULL;
|
||||
extern QTextStream STDOUT;
|
||||
extern QTextStream STDERR;
|
||||
extern QTextStream STDIN;
|
||||
extern QTextStream DEVNULL;
|
||||
|
||||
void setDefaultTextStreams();
|
||||
|
||||
void setStdinEcho(bool enable);
|
||||
QString getPassword(FILE* outputDescriptor = STDOUT);
|
||||
QSharedPointer<PasswordKey> getPasswordFromStdin();
|
||||
QString getPassword(bool quiet = false);
|
||||
QSharedPointer<PasswordKey> getConfirmedPassword();
|
||||
int clipText(const QString& text);
|
||||
QSharedPointer<Database> unlockDatabase(const QString& databaseFilename,
|
||||
const bool isPasswordProtected = true,
|
||||
const QString& keyFilename = {},
|
||||
const QString& yubiKeySlot = {},
|
||||
FILE* outputDescriptor = STDOUT,
|
||||
FILE* errorDescriptor = STDERR);
|
||||
bool quiet = false);
|
||||
|
||||
QStringList splitCommandString(const QString& command);
|
||||
|
||||
@ -59,11 +60,6 @@ namespace Utils
|
||||
* (case-insensitive).
|
||||
*/
|
||||
QStringList findAttributes(const EntryAttributes& attributes, const QString& name);
|
||||
|
||||
namespace Test
|
||||
{
|
||||
void setNextPassword(const QString& password, bool repeat = false);
|
||||
}
|
||||
}; // namespace Utils
|
||||
|
||||
#endif // KEEPASSXC_UTILS_H
|
||||
|
@ -118,13 +118,14 @@ private:
|
||||
|
||||
void enterInteractiveMode(const QStringList& arguments)
|
||||
{
|
||||
auto& err = Utils::STDERR;
|
||||
// Replace command list with interactive version
|
||||
Commands::setupCommands(true);
|
||||
|
||||
Open o;
|
||||
Open openCmd;
|
||||
QStringList openArgs(arguments);
|
||||
openArgs.removeFirst();
|
||||
o.execute(openArgs);
|
||||
openCmd.execute(openArgs);
|
||||
|
||||
QScopedPointer<LineReader> reader;
|
||||
#if defined(USE_READLINE)
|
||||
@ -133,12 +134,10 @@ void enterInteractiveMode(const QStringList& arguments)
|
||||
reader.reset(new SimpleLineReader());
|
||||
#endif
|
||||
|
||||
QSharedPointer<Database> currentDatabase(o.currentDatabase);
|
||||
QSharedPointer<Database> currentDatabase(openCmd.currentDatabase);
|
||||
|
||||
QString command;
|
||||
while (true) {
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QString prompt;
|
||||
if (currentDatabase) {
|
||||
prompt += currentDatabase->metadata()->name();
|
||||
@ -159,7 +158,7 @@ void enterInteractiveMode(const QStringList& arguments)
|
||||
|
||||
auto cmd = Commands::getCommand(args[0]);
|
||||
if (!cmd) {
|
||||
errorTextStream << QObject::tr("Unknown command %1").arg(args[0]) << "\n";
|
||||
err << QObject::tr("Unknown command %1").arg(args[0]) << "\n";
|
||||
continue;
|
||||
} else if (cmd->name == "quit" || cmd->name == "exit") {
|
||||
break;
|
||||
@ -186,10 +185,12 @@ int main(int argc, char** argv)
|
||||
QCoreApplication::setApplicationVersion(KEEPASSXC_VERSION);
|
||||
|
||||
Bootstrap::bootstrap();
|
||||
Utils::setDefaultTextStreams();
|
||||
Commands::setupCommands(false);
|
||||
|
||||
TextStream out(stdout);
|
||||
TextStream err(stderr);
|
||||
auto& out = Utils::STDOUT;
|
||||
auto& err = Utils::STDERR;
|
||||
|
||||
QStringList arguments;
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
arguments << QString(argv[i]);
|
||||
|
@ -77,8 +77,7 @@ protected:
|
||||
{
|
||||
Q_ASSERT(static_cast<unsigned long>(data.size()) < (1ull << (sizeof(SizedQInt) * 8)));
|
||||
|
||||
QByteArray fieldIdArr;
|
||||
fieldIdArr[0] = static_cast<char>(fieldId);
|
||||
QByteArray fieldIdArr(1, static_cast<char>(fieldId));
|
||||
CHECK_RETURN_FALSE(writeData(device, fieldIdArr));
|
||||
CHECK_RETURN_FALSE(writeData(
|
||||
device, Endian::sizedIntToBytes<SizedQInt>(static_cast<SizedQInt>(data.size()), KeePass2::BYTEORDER)));
|
||||
|
@ -30,13 +30,13 @@ QUuid YkChallengeResponseKeyCLI::UUID("e2be77c0-c810-417a-8437-32f41d00bd1d");
|
||||
YkChallengeResponseKeyCLI::YkChallengeResponseKeyCLI(int slot,
|
||||
bool blocking,
|
||||
QString messageInteraction,
|
||||
FILE* outputDescriptor)
|
||||
QIODevice* out)
|
||||
: ChallengeResponseKey(UUID)
|
||||
, m_slot(slot)
|
||||
, m_blocking(blocking)
|
||||
, m_messageInteraction(messageInteraction)
|
||||
, m_out(outputDescriptor)
|
||||
{
|
||||
m_out.setDevice(out);
|
||||
}
|
||||
|
||||
QByteArray YkChallengeResponseKeyCLI::rawKey() const
|
||||
@ -54,12 +54,11 @@ bool YkChallengeResponseKeyCLI::challenge(const QByteArray& c)
|
||||
|
||||
bool YkChallengeResponseKeyCLI::challenge(const QByteArray& challenge, unsigned int retries)
|
||||
{
|
||||
QTextStream out(m_out, QIODevice::WriteOnly);
|
||||
do {
|
||||
--retries;
|
||||
|
||||
if (m_blocking) {
|
||||
out << m_messageInteraction << endl;
|
||||
m_out << m_messageInteraction << endl;
|
||||
}
|
||||
YubiKey::ChallengeResult result = YubiKey::instance()->challenge(m_slot, m_blocking, challenge, m_key);
|
||||
if (result == YubiKey::SUCCESS) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "keys/drivers/YubiKey.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QTextStream>
|
||||
|
||||
class YkChallengeResponseKeyCLI : public QObject, public ChallengeResponseKey
|
||||
@ -32,7 +33,7 @@ class YkChallengeResponseKeyCLI : public QObject, public ChallengeResponseKey
|
||||
public:
|
||||
static QUuid UUID;
|
||||
|
||||
explicit YkChallengeResponseKeyCLI(int slot, bool blocking, QString messageInteraction, FILE* outputDescriptor);
|
||||
explicit YkChallengeResponseKeyCLI(int slot, bool blocking, QString messageInteraction, QIODevice* out);
|
||||
|
||||
QByteArray rawKey() const override;
|
||||
bool challenge(const QByteArray& challenge) override;
|
||||
@ -43,7 +44,7 @@ private:
|
||||
int m_slot;
|
||||
bool m_blocking;
|
||||
QString m_messageInteraction;
|
||||
FILE* m_out;
|
||||
QTextStream m_out;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_YK_CHALLENGERESPONSEKEYCLI_H
|
||||
|
1846
tests/TestCli.cpp
1846
tests/TestCli.cpp
File diff suppressed because it is too large
Load Diff
@ -18,30 +18,33 @@
|
||||
#ifndef KEEPASSXC_TESTCLI_H
|
||||
#define KEEPASSXC_TESTCLI_H
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "util/TemporaryFile.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QFile>
|
||||
#include <QBuffer>
|
||||
#include <QScopedPointer>
|
||||
#include <QSharedPointer>
|
||||
#include <QTemporaryFile>
|
||||
#include <QStringList>
|
||||
#include <QTest>
|
||||
|
||||
#include <stdio.h>
|
||||
#include "util/TemporaryFile.h"
|
||||
|
||||
class Command;
|
||||
class Database;
|
||||
|
||||
class TestCli : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QSharedPointer<Database> readTestDatabase() const;
|
||||
QSharedPointer<Database>
|
||||
readDatabase(const QString& filename = {}, const QString& pw = {}, const QString& keyfile = {});
|
||||
int execCmd(Command& cmd, const QStringList& args) const;
|
||||
bool isTotp(const QString& value);
|
||||
void setInput(const QString& input);
|
||||
void setInput(const QStringList& input);
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
void cleanupTestCase();
|
||||
|
||||
void testBatchCommands();
|
||||
void testAdd();
|
||||
@ -78,21 +81,16 @@ private slots:
|
||||
void testYubiKeyOption();
|
||||
|
||||
private:
|
||||
QByteArray m_dbData;
|
||||
QByteArray m_dbData2;
|
||||
QByteArray m_xmlData;
|
||||
QByteArray m_yubiKeyProtectedDbData;
|
||||
QByteArray m_keyFileProtectedDbData;
|
||||
QByteArray m_keyFileProtectedNoPasswordDbData;
|
||||
QScopedPointer<TemporaryFile> m_dbFile;
|
||||
QScopedPointer<TemporaryFile> m_dbFile2;
|
||||
QScopedPointer<TemporaryFile> m_xmlFile;
|
||||
QScopedPointer<TemporaryFile> m_keyFileProtectedDbFile;
|
||||
QScopedPointer<TemporaryFile> m_keyFileProtectedNoPasswordDbFile;
|
||||
QScopedPointer<TemporaryFile> m_yubiKeyProtectedDbFile;
|
||||
QScopedPointer<TemporaryFile> m_stdoutFile;
|
||||
QScopedPointer<TemporaryFile> m_stderrFile;
|
||||
QScopedPointer<TemporaryFile> m_stdinFile;
|
||||
|
||||
QScopedPointer<QBuffer> m_stdout;
|
||||
QScopedPointer<QBuffer> m_stderr;
|
||||
QScopedPointer<QBuffer> m_stdin;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_TESTCLI_H
|
||||
|
Loading…
Reference in New Issue
Block a user