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