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:
Jonathan White 2020-05-11 07:31:29 -04:00
parent 612f8d2e5b
commit 485852c9db
30 changed files with 938 additions and 1407 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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,7 +96,7 @@ 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.")
err << QObject::tr("ERROR: attribute %1 is ambiguous, it matches %2.")
.arg(selectedAttribute, QLocale().createSeparatedList(attrs))
<< endl;
return EXIT_FAILURE;
@ -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;
}

View File

@ -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;

View File

@ -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());

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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));

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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.")
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;

View File

@ -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;

View File

@ -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

View File

@ -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]);

View File

@ -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)));

View File

@ -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) {

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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