mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-08-13 00:36:00 -04:00
Add CLI tests and improve coding style and i18n
The CLI module was lacking unit test coverage and showed some severe coding style violations, which this patch addresses. In addition, all uses of qCritical() with untranslatble raw char* sequences were removed in favor of proper locale strings. These are written to STDERR through QTextStreams and support output redirection for testing purposes. With this change, error messages don't depend on the global Qt logging settings and targets anymore and go directly to the terminal or into a file if needed. This patch also fixes a bug discovered during unit test development, where the extract command would just dump the raw XML contents without decrypting embedded Salsa20-protected values first, making the XML export mostly useless, since passwords are scrambled. Lastly, all CLI commands received a dedicated -h/--help option.
This commit is contained in:
parent
18b22834c1
commit
113c8eb702
67 changed files with 2259 additions and 1250 deletions
|
@ -41,22 +41,20 @@ Add::~Add()
|
|||
|
||||
int Add::execute(const QStringList& arguments)
|
||||
{
|
||||
|
||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QTextStream inputTextStream(Utils::STDIN, QIODevice::ReadOnly);
|
||||
QTextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
parser.setApplicationDescription(description);
|
||||
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
|
||||
|
||||
QCommandLineOption keyFile(QStringList() << "k"
|
||||
<< "key-file",
|
||||
QCommandLineOption keyFile(QStringList() << "k" << "key-file",
|
||||
QObject::tr("Key file of the database."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFile);
|
||||
|
||||
QCommandLineOption username(QStringList() << "u"
|
||||
<< "username",
|
||||
QCommandLineOption username(QStringList() << "u" << "username",
|
||||
QObject::tr("Username for the entry."),
|
||||
QObject::tr("username"));
|
||||
parser.addOption(username);
|
||||
|
@ -64,23 +62,22 @@ int Add::execute(const QStringList& arguments)
|
|||
QCommandLineOption url(QStringList() << "url", QObject::tr("URL for the entry."), QObject::tr("URL"));
|
||||
parser.addOption(url);
|
||||
|
||||
QCommandLineOption prompt(QStringList() << "p"
|
||||
<< "password-prompt",
|
||||
QCommandLineOption prompt(QStringList() << "p" << "password-prompt",
|
||||
QObject::tr("Prompt for the entry's password."));
|
||||
parser.addOption(prompt);
|
||||
|
||||
QCommandLineOption generate(QStringList() << "g"
|
||||
<< "generate",
|
||||
QCommandLineOption generate(QStringList() << "g" << "generate",
|
||||
QObject::tr("Generate a password for the entry."));
|
||||
parser.addOption(generate);
|
||||
|
||||
QCommandLineOption length(QStringList() << "l"
|
||||
<< "password-length",
|
||||
QCommandLineOption length(QStringList() << "l" << "password-length",
|
||||
QObject::tr("Length for the generated password."),
|
||||
QObject::tr("length"));
|
||||
parser.addOption(length);
|
||||
|
||||
parser.addPositionalArgument("entry", QObject::tr("Path of the entry to add."));
|
||||
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
@ -89,11 +86,11 @@ int Add::execute(const QStringList& arguments)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QString databasePath = args.at(0);
|
||||
QString entryPath = args.at(1);
|
||||
const QString& databasePath = args.at(0);
|
||||
const QString& entryPath = args.at(1);
|
||||
|
||||
Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile));
|
||||
if (db == nullptr) {
|
||||
Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -101,13 +98,13 @@ int Add::execute(const QStringList& arguments)
|
|||
// the entry.
|
||||
QString passwordLength = parser.value(length);
|
||||
if (!passwordLength.isEmpty() && !passwordLength.toInt()) {
|
||||
qCritical("Invalid value for password length %s.", qPrintable(passwordLength));
|
||||
errorTextStream << QObject::tr("Invalid value for password length %1.").arg(passwordLength) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Entry* entry = db->rootGroup()->addEntryWithPath(entryPath);
|
||||
if (!entry) {
|
||||
qCritical("Could not create entry with path %s.", qPrintable(entryPath));
|
||||
errorTextStream << QObject::tr("Could not create entry with path %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -120,8 +117,7 @@ int Add::execute(const QStringList& arguments)
|
|||
}
|
||||
|
||||
if (parser.isSet(prompt)) {
|
||||
outputTextStream << "Enter password for new entry: ";
|
||||
outputTextStream.flush();
|
||||
outputTextStream << QObject::tr("Enter password for new entry: ") << flush;
|
||||
QString password = Utils::getPassword();
|
||||
entry->setPassword(password);
|
||||
} else if (parser.isSet(generate)) {
|
||||
|
@ -130,7 +126,7 @@ int Add::execute(const QStringList& arguments)
|
|||
if (passwordLength.isEmpty()) {
|
||||
passwordGenerator.setLength(PasswordGenerator::DefaultLength);
|
||||
} else {
|
||||
passwordGenerator.setLength(passwordLength.toInt());
|
||||
passwordGenerator.setLength(static_cast<size_t>(passwordLength.toInt()));
|
||||
}
|
||||
|
||||
passwordGenerator.setCharClasses(PasswordGenerator::DefaultCharset);
|
||||
|
@ -141,10 +137,10 @@ int Add::execute(const QStringList& arguments)
|
|||
|
||||
QString errorMessage = db->saveToFile(databasePath);
|
||||
if (!errorMessage.isEmpty()) {
|
||||
qCritical("Writing the database failed %s.", qPrintable(errorMessage));
|
||||
errorTextStream << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << "Successfully added entry " << entry->title() << "." << endl;
|
||||
outputTextStream << QObject::tr("Successfully added entry %1.").arg(entry->title()) << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -42,20 +42,19 @@ Clip::~Clip()
|
|||
|
||||
int Clip::execute(const QStringList& arguments)
|
||||
{
|
||||
|
||||
QTextStream out(stdout);
|
||||
QTextStream out(Utils::STDOUT);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
parser.setApplicationDescription(description);
|
||||
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
|
||||
QCommandLineOption keyFile(QStringList() << "k"
|
||||
<< "key-file",
|
||||
QCommandLineOption keyFile(QStringList() << "k" << "key-file",
|
||||
QObject::tr("Key file of the database."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFile);
|
||||
parser.addPositionalArgument("entry", QObject::tr("Path of the entry to clip.", "clip = copy to clipboard"));
|
||||
parser.addPositionalArgument(
|
||||
"timeout", QObject::tr("Timeout in seconds before clearing the clipboard."), QString("[timeout]"));
|
||||
parser.addPositionalArgument("timeout",
|
||||
QObject::tr("Timeout in seconds before clearing the clipboard."), "[timeout]");
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
@ -64,29 +63,30 @@ int Clip::execute(const QStringList& arguments)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
|
||||
Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return this->clipEntry(db, args.at(1), args.value(2));
|
||||
return clipEntry(db, args.at(1), args.value(2));
|
||||
}
|
||||
|
||||
int Clip::clipEntry(Database* database, QString entryPath, QString timeout)
|
||||
{
|
||||
QTextStream err(Utils::STDERR);
|
||||
|
||||
int timeoutSeconds = 0;
|
||||
if (!timeout.isEmpty() && !timeout.toInt()) {
|
||||
qCritical("Invalid timeout value %s.", qPrintable(timeout));
|
||||
err << QObject::tr("Invalid timeout value %1.").arg(timeout) << endl;
|
||||
return EXIT_FAILURE;
|
||||
} else if (!timeout.isEmpty()) {
|
||||
timeoutSeconds = timeout.toInt();
|
||||
}
|
||||
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QTextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
Entry* entry = database->rootGroup()->findEntry(entryPath);
|
||||
if (!entry) {
|
||||
qCritical("Entry %s not found.", qPrintable(entryPath));
|
||||
err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -95,20 +95,23 @@ int Clip::clipEntry(Database* database, QString entryPath, QString timeout)
|
|||
return exitCode;
|
||||
}
|
||||
|
||||
outputTextStream << "Entry's password copied to the clipboard!" << endl;
|
||||
outputTextStream << QObject::tr("Entry's password copied to the clipboard!") << endl;
|
||||
|
||||
if (!timeoutSeconds) {
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
QString lastLine = "";
|
||||
while (timeoutSeconds > 0) {
|
||||
outputTextStream << "\rClearing the clipboard in " << timeoutSeconds << " seconds...";
|
||||
outputTextStream.flush();
|
||||
outputTextStream << '\r' << QString(lastLine.size(), ' ') << '\r';
|
||||
lastLine = QObject::tr("Clearing the clipboard in %1 second(s)...", "", timeoutSeconds).arg(timeoutSeconds);
|
||||
outputTextStream << lastLine << flush;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
timeoutSeconds--;
|
||||
--timeoutSeconds;
|
||||
}
|
||||
Utils::clipText("");
|
||||
outputTextStream << "\nClipboard cleared!" << endl;
|
||||
outputTextStream << '\r' << QString(lastLine.size(), ' ') << '\r';
|
||||
outputTextStream << QObject::tr("Clipboard cleared!") << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -41,19 +41,14 @@ Command::~Command()
|
|||
{
|
||||
}
|
||||
|
||||
int Command::execute(const QStringList&)
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QString Command::getDescriptionLine()
|
||||
{
|
||||
|
||||
QString response = this->name;
|
||||
QString response = name;
|
||||
QString space(" ");
|
||||
QString spaces = space.repeated(15 - this->name.length());
|
||||
QString spaces = space.repeated(15 - name.length());
|
||||
response = response.append(spaces);
|
||||
response = response.append(this->description);
|
||||
response = response.append(description);
|
||||
response = response.append("\n");
|
||||
return response;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class Command
|
|||
{
|
||||
public:
|
||||
virtual ~Command();
|
||||
virtual int execute(const QStringList& arguments);
|
||||
virtual int execute(const QStringList& arguments) = 0;
|
||||
QString name;
|
||||
QString description;
|
||||
QString getDescriptionLine();
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <QTextStream>
|
||||
|
||||
#include "core/PassphraseGenerator.h"
|
||||
#include "Utils.h"
|
||||
|
||||
Diceware::Diceware()
|
||||
{
|
||||
|
@ -37,26 +38,25 @@ Diceware::~Diceware()
|
|||
|
||||
int Diceware::execute(const QStringList& arguments)
|
||||
{
|
||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QTextStream in(Utils::STDIN, QIODevice::ReadOnly);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
QCommandLineOption words(QStringList() << "W"
|
||||
<< "words",
|
||||
parser.setApplicationDescription(description);
|
||||
QCommandLineOption words(QStringList() << "W" << "words",
|
||||
QObject::tr("Word count for the diceware passphrase."),
|
||||
QObject::tr("count"));
|
||||
QObject::tr("count", "CLI parameter"));
|
||||
parser.addOption(words);
|
||||
QCommandLineOption wordlistFile(QStringList() << "w"
|
||||
<< "word-list",
|
||||
QCommandLineOption wordlistFile(QStringList() << "w" << "word-list",
|
||||
QObject::tr("Wordlist for the diceware generator.\n[Default: EFF English]"),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(wordlistFile);
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
if (!args.isEmpty()) {
|
||||
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware");
|
||||
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -76,12 +76,12 @@ int Diceware::execute(const QStringList& arguments)
|
|||
}
|
||||
|
||||
if (!dicewareGenerator.isValid()) {
|
||||
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware");
|
||||
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli diceware");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QString password = dicewareGenerator.generatePassphrase();
|
||||
outputTextStream << password << endl;
|
||||
out << password << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -41,22 +41,20 @@ Edit::~Edit()
|
|||
|
||||
int Edit::execute(const QStringList& arguments)
|
||||
{
|
||||
|
||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QTextStream in(Utils::STDIN, QIODevice::ReadOnly);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
parser.setApplicationDescription(description);
|
||||
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
|
||||
|
||||
QCommandLineOption keyFile(QStringList() << "k"
|
||||
<< "key-file",
|
||||
QCommandLineOption keyFile(QStringList() << "k" << "key-file",
|
||||
QObject::tr("Key file of the database."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFile);
|
||||
|
||||
QCommandLineOption username(QStringList() << "u"
|
||||
<< "username",
|
||||
QCommandLineOption username(QStringList() << "u" << "username",
|
||||
QObject::tr("Username for the entry."),
|
||||
QObject::tr("username"));
|
||||
parser.addOption(username);
|
||||
|
@ -64,61 +62,58 @@ int Edit::execute(const QStringList& arguments)
|
|||
QCommandLineOption url(QStringList() << "url", QObject::tr("URL for the entry."), QObject::tr("URL"));
|
||||
parser.addOption(url);
|
||||
|
||||
QCommandLineOption title(QStringList() << "t"
|
||||
<< "title",
|
||||
QCommandLineOption title(QStringList() << "t" << "title",
|
||||
QObject::tr("Title for the entry."),
|
||||
QObject::tr("title"));
|
||||
parser.addOption(title);
|
||||
|
||||
QCommandLineOption prompt(QStringList() << "p"
|
||||
<< "password-prompt",
|
||||
QCommandLineOption prompt(QStringList() << "p" << "password-prompt",
|
||||
QObject::tr("Prompt for the entry's password."));
|
||||
parser.addOption(prompt);
|
||||
|
||||
QCommandLineOption generate(QStringList() << "g"
|
||||
<< "generate",
|
||||
QCommandLineOption generate(QStringList() << "g" << "generate",
|
||||
QObject::tr("Generate a password for the entry."));
|
||||
parser.addOption(generate);
|
||||
|
||||
QCommandLineOption length(QStringList() << "l"
|
||||
<< "password-length",
|
||||
QCommandLineOption length(QStringList() << "l" << "password-length",
|
||||
QObject::tr("Length for the generated password."),
|
||||
QObject::tr("length"));
|
||||
parser.addOption(length);
|
||||
|
||||
parser.addPositionalArgument("entry", QObject::tr("Path of the entry to edit."));
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
if (args.size() != 2) {
|
||||
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli edit");
|
||||
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli edit");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QString databasePath = args.at(0);
|
||||
QString entryPath = args.at(1);
|
||||
const QString& databasePath = args.at(0);
|
||||
const QString& entryPath = args.at(1);
|
||||
|
||||
Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile));
|
||||
if (db == nullptr) {
|
||||
Database* db = Database::unlockFromStdin(databasePath, parser.value(keyFile), Utils::STDOUT, Utils::STDERR);
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QString passwordLength = parser.value(length);
|
||||
if (!passwordLength.isEmpty() && !passwordLength.toInt()) {
|
||||
qCritical("Invalid value for password length %s.", qPrintable(passwordLength));
|
||||
err << QObject::tr("Invalid value for password length: %1").arg(passwordLength) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Entry* entry = db->rootGroup()->findEntryByPath(entryPath);
|
||||
if (!entry) {
|
||||
qCritical("Could not find entry with path %s.", qPrintable(entryPath));
|
||||
err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (parser.value("username").isEmpty() && parser.value("url").isEmpty() && parser.value("title").isEmpty()
|
||||
&& !parser.isSet(prompt)
|
||||
&& !parser.isSet(generate)) {
|
||||
qCritical("Not changing any field for entry %s.", qPrintable(entryPath));
|
||||
err << QObject::tr("Not changing any field for entry %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -137,8 +132,7 @@ int Edit::execute(const QStringList& arguments)
|
|||
}
|
||||
|
||||
if (parser.isSet(prompt)) {
|
||||
outputTextStream << "Enter new password for entry: ";
|
||||
outputTextStream.flush();
|
||||
out << QObject::tr("Enter new password for entry: ") << flush;
|
||||
QString password = Utils::getPassword();
|
||||
entry->setPassword(password);
|
||||
} else if (parser.isSet(generate)) {
|
||||
|
@ -147,7 +141,7 @@ int Edit::execute(const QStringList& arguments)
|
|||
if (passwordLength.isEmpty()) {
|
||||
passwordGenerator.setLength(PasswordGenerator::DefaultLength);
|
||||
} else {
|
||||
passwordGenerator.setLength(passwordLength.toInt());
|
||||
passwordGenerator.setLength(static_cast<size_t>(passwordLength.toInt()));
|
||||
}
|
||||
|
||||
passwordGenerator.setCharClasses(PasswordGenerator::DefaultCharset);
|
||||
|
@ -160,10 +154,10 @@ int Edit::execute(const QStringList& arguments)
|
|||
|
||||
QString errorMessage = db->saveToFile(databasePath);
|
||||
if (!errorMessage.isEmpty()) {
|
||||
qCritical("Writing the database failed %s.", qPrintable(errorMessage));
|
||||
err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << "Successfully edited entry " << entry->title() << "." << endl;
|
||||
out << QObject::tr("Successfully edited entry %1.").arg(entry->title()) << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
#include "Estimate.h"
|
||||
#include "cli/Utils.h"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QTextStream>
|
||||
|
@ -44,117 +45,126 @@ Estimate::~Estimate()
|
|||
|
||||
static void estimate(const char* pwd, bool advanced)
|
||||
{
|
||||
double e;
|
||||
int len = strlen(pwd);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
|
||||
double e = 0.0;
|
||||
int len = static_cast<int>(strlen(pwd));
|
||||
if (!advanced) {
|
||||
e = ZxcvbnMatch(pwd, 0, 0);
|
||||
printf("Length %d\tEntropy %.3f\tLog10 %.3f\n", len, e, e * 0.301029996);
|
||||
e = ZxcvbnMatch(pwd, nullptr, nullptr);
|
||||
out << QObject::tr("Length %1").arg(len, 0) << '\t'
|
||||
<< QObject::tr("Entropy %1").arg(e, 0, 'f', 3) << '\t'
|
||||
<< QObject::tr("Log10 %1").arg(e * 0.301029996, 0, 'f', 3) << endl;
|
||||
} else {
|
||||
int ChkLen;
|
||||
int ChkLen = 0;
|
||||
ZxcMatch_t *info, *p;
|
||||
double m = 0.0;
|
||||
e = ZxcvbnMatch(pwd, 0, &info);
|
||||
e = ZxcvbnMatch(pwd, nullptr, &info);
|
||||
for (p = info; p; p = p->Next) {
|
||||
m += p->Entrpy;
|
||||
}
|
||||
m = e - m;
|
||||
printf("Length %d\tEntropy %.3f\tLog10 %.3f\n Multi-word extra bits %.1f\n", len, e, e * 0.301029996, m);
|
||||
out << QObject::tr("Length %1").arg(len) << '\t'
|
||||
<< QObject::tr("Entropy %1").arg(e, 0, 'f', 3) << '\t'
|
||||
<< QObject::tr("Log10 %1").arg(e * 0.301029996, 0, 'f', 3) << "\n "
|
||||
<< QObject::tr("Multi-word extra bits %1").arg(m, 0, 'f', 1) << endl;
|
||||
p = info;
|
||||
ChkLen = 0;
|
||||
while (p) {
|
||||
int n;
|
||||
switch (static_cast<int>(p->Type)) {
|
||||
case BRUTE_MATCH:
|
||||
printf(" Type: Bruteforce ");
|
||||
out << " " << QObject::tr("Type: Bruteforce") << " ";
|
||||
break;
|
||||
case DICTIONARY_MATCH:
|
||||
printf(" Type: Dictionary ");
|
||||
out << " " << QObject::tr("Type: Dictionary") << " ";
|
||||
break;
|
||||
case DICT_LEET_MATCH:
|
||||
printf(" Type: Dict+Leet ");
|
||||
out << " " << QObject::tr("Type: Dict+Leet") << " ";
|
||||
break;
|
||||
case USER_MATCH:
|
||||
printf(" Type: User Words ");
|
||||
out << " " << QObject::tr("Type: User Words") << " ";
|
||||
break;
|
||||
case USER_LEET_MATCH:
|
||||
printf(" Type: User+Leet ");
|
||||
out << " " << QObject::tr("Type: User+Leet") << " ";
|
||||
break;
|
||||
case REPEATS_MATCH:
|
||||
printf(" Type: Repeated ");
|
||||
out << " " << QObject::tr("Type: Repeated") << " ";
|
||||
break;
|
||||
case SEQUENCE_MATCH:
|
||||
printf(" Type: Sequence ");
|
||||
out << " " << QObject::tr("Type: Sequence") << " ";
|
||||
break;
|
||||
case SPATIAL_MATCH:
|
||||
printf(" Type: Spatial ");
|
||||
out << " " << QObject::tr("Type: Spatial") << " ";
|
||||
break;
|
||||
case DATE_MATCH:
|
||||
printf(" Type: Date ");
|
||||
out << " " << QObject::tr("Type: Date") << " ";
|
||||
break;
|
||||
case BRUTE_MATCH + MULTIPLE_MATCH:
|
||||
printf(" Type: Bruteforce(Rep)");
|
||||
out << " " << QObject::tr("Type: Bruteforce(Rep)") << " ";
|
||||
break;
|
||||
case DICTIONARY_MATCH + MULTIPLE_MATCH:
|
||||
printf(" Type: Dictionary(Rep)");
|
||||
out << " " << QObject::tr("Type: Dictionary(Rep)") << " ";
|
||||
break;
|
||||
case DICT_LEET_MATCH + MULTIPLE_MATCH:
|
||||
printf(" Type: Dict+Leet(Rep) ");
|
||||
out << " " << QObject::tr("Type: Dict+Leet(Rep)") << " ";
|
||||
break;
|
||||
case USER_MATCH + MULTIPLE_MATCH:
|
||||
printf(" Type: User Words(Rep)");
|
||||
out << " " << QObject::tr("Type: User Words(Rep)") << " ";
|
||||
break;
|
||||
case USER_LEET_MATCH + MULTIPLE_MATCH:
|
||||
printf(" Type: User+Leet(Rep) ");
|
||||
out << " " << QObject::tr("Type: User+Leet(Rep)") << " ";
|
||||
break;
|
||||
case REPEATS_MATCH + MULTIPLE_MATCH:
|
||||
printf(" Type: Repeated(Rep) ");
|
||||
out << " " << QObject::tr("Type: Repeated(Rep)") << " ";
|
||||
break;
|
||||
case SEQUENCE_MATCH + MULTIPLE_MATCH:
|
||||
printf(" Type: Sequence(Rep) ");
|
||||
out << " " << QObject::tr("Type: Sequence(Rep)") << " ";
|
||||
break;
|
||||
case SPATIAL_MATCH + MULTIPLE_MATCH:
|
||||
printf(" Type: Spatial(Rep) ");
|
||||
out << " " << QObject::tr("Type: Spatial(Rep)") << " ";
|
||||
break;
|
||||
case DATE_MATCH + MULTIPLE_MATCH:
|
||||
printf(" Type: Date(Rep) ");
|
||||
out << " " << QObject::tr("Type: Date(Rep)") << " ";
|
||||
break;
|
||||
|
||||
default:
|
||||
printf(" Type: Unknown%d ", p->Type);
|
||||
out << " " << QObject::tr("Type: Unknown%1").arg(p->Type) << " ";
|
||||
break;
|
||||
}
|
||||
ChkLen += p->Length;
|
||||
printf(" Length %d Entropy %6.3f (%.2f) ", p->Length, p->Entrpy, p->Entrpy * 0.301029996);
|
||||
|
||||
out << QObject::tr("Length %1").arg(p->Length) << '\t'
|
||||
<< QObject::tr("Entropy %1 (%2)").arg(p->Entrpy, 6, 'f', 3).arg(p->Entrpy * 0.301029996, 0, 'f', 2) << '\t';
|
||||
for (n = 0; n < p->Length; ++n, ++pwd) {
|
||||
printf("%c", *pwd);
|
||||
out << *pwd;
|
||||
}
|
||||
printf("\n");
|
||||
out << endl;
|
||||
p = p->Next;
|
||||
}
|
||||
ZxcvbnFreeInfo(info);
|
||||
if (ChkLen != len) {
|
||||
printf("*** Password length (%d) != sum of length of parts (%d) ***\n", len, ChkLen);
|
||||
out << QObject::tr("*** Password length (%1) != sum of length of parts (%2) ***").arg(len).arg(ChkLen) << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Estimate::execute(const QStringList& arguments)
|
||||
{
|
||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QTextStream in(Utils::STDIN, QIODevice::ReadOnly);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
parser.setApplicationDescription(description);
|
||||
parser.addPositionalArgument("password", QObject::tr("Password for which to estimate the entropy."), "[password]");
|
||||
QCommandLineOption advancedOption(QStringList() << "a"
|
||||
<< "advanced",
|
||||
QCommandLineOption advancedOption(QStringList() << "a" << "advanced",
|
||||
QObject::tr("Perform advanced analysis on the password."));
|
||||
parser.addOption(advancedOption);
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
if (args.size() > 1) {
|
||||
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli estimate");
|
||||
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli estimate");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -162,7 +172,7 @@ int Estimate::execute(const QStringList& arguments)
|
|||
if (args.size() == 1) {
|
||||
password = args.at(0);
|
||||
} else {
|
||||
password = inputTextStream.readLine();
|
||||
password = in.readLine();
|
||||
}
|
||||
|
||||
estimate(password.toLatin1(), parser.isSet(advancedOption));
|
||||
|
|
|
@ -43,17 +43,17 @@ Extract::~Extract()
|
|||
|
||||
int Extract::execute(const QStringList& arguments)
|
||||
{
|
||||
QTextStream out(stdout);
|
||||
QTextStream errorTextStream(stderr);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
parser.setApplicationDescription(description);
|
||||
parser.addPositionalArgument("database", QObject::tr("Path of the database to extract."));
|
||||
QCommandLineOption keyFile(QStringList() << "k"
|
||||
<< "key-file",
|
||||
QCommandLineOption keyFile(QStringList() << "k" << "key-file",
|
||||
QObject::tr("Key file of the database."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFile);
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
@ -62,8 +62,7 @@ int Extract::execute(const QStringList& arguments)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
out << QObject::tr("Insert password to unlock %1: ").arg(args.at(0));
|
||||
out.flush();
|
||||
out << QObject::tr("Insert password to unlock %1: ").arg(args.at(0)) << flush;
|
||||
|
||||
auto compositeKey = QSharedPointer<CompositeKey>::create();
|
||||
|
||||
|
@ -74,52 +73,51 @@ int Extract::execute(const QStringList& arguments)
|
|||
|
||||
QString keyFilePath = parser.value(keyFile);
|
||||
if (!keyFilePath.isEmpty()) {
|
||||
// LCOV_EXCL_START
|
||||
auto fileKey = QSharedPointer<FileKey>::create();
|
||||
QString errorMsg;
|
||||
if (!fileKey->load(keyFilePath, &errorMsg)) {
|
||||
errorTextStream << QObject::tr("Failed to load key file %1 : %2").arg(keyFilePath).arg(errorMsg);
|
||||
errorTextStream << endl;
|
||||
err << QObject::tr("Failed to load key file %1: %2").arg(keyFilePath).arg(errorMsg) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (fileKey->type() != FileKey::Hashed) {
|
||||
errorTextStream << QObject::tr("WARNING: You are using a legacy key file format which may become\n"
|
||||
"unsupported in the future.\n\n"
|
||||
"Please consider generating a new key file.");
|
||||
errorTextStream << endl;
|
||||
err << QObject::tr("WARNING: You are using a legacy key file format which may become\n"
|
||||
"unsupported in the future.\n\n"
|
||||
"Please consider generating a new key file.") << endl;
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
compositeKey->addKey(fileKey);
|
||||
}
|
||||
|
||||
QString databaseFilename = args.at(0);
|
||||
const QString& databaseFilename = args.at(0);
|
||||
QFile dbFile(databaseFilename);
|
||||
if (!dbFile.exists()) {
|
||||
qCritical("File %s does not exist.", qPrintable(databaseFilename));
|
||||
err << QObject::tr("File %1 does not exist.").arg(databaseFilename) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if (!dbFile.open(QIODevice::ReadOnly)) {
|
||||
qCritical("Unable to open file %s.", qPrintable(databaseFilename));
|
||||
err << QObject::tr("Unable to open file %1.").arg(databaseFilename) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
KeePass2Reader reader;
|
||||
reader.setSaveXml(true);
|
||||
Database* db = reader.readDatabase(&dbFile, compositeKey);
|
||||
delete db;
|
||||
QScopedPointer<Database> db(reader.readDatabase(&dbFile, compositeKey));
|
||||
|
||||
QByteArray xmlData = reader.reader()->xmlData();
|
||||
|
||||
if (reader.hasError()) {
|
||||
if (xmlData.isEmpty()) {
|
||||
qCritical("Error while reading the database:\n%s", qPrintable(reader.errorString()));
|
||||
err << QObject::tr("Error while reading the database:\n%1").arg(reader.errorString()) << endl;
|
||||
} else {
|
||||
qWarning("Error while parsing the database:\n%s\n", qPrintable(reader.errorString()));
|
||||
err << QObject::tr("Error while parsing the database:\n%1").arg(reader.errorString()) << endl;
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
out << xmlData.constData() << "\n";
|
||||
out << xmlData.constData() << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "Generate.h"
|
||||
#include "cli/Utils.h"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QTextStream>
|
||||
|
@ -37,38 +38,32 @@ Generate::~Generate()
|
|||
|
||||
int Generate::execute(const QStringList& arguments)
|
||||
{
|
||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QTextStream in(Utils::STDIN, QIODevice::ReadOnly);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
out.setCodec("UTF-8"); // force UTF-8 to prevent ??? characters in extended-ASCII passwords
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
QCommandLineOption len(QStringList() << "L"
|
||||
<< "length",
|
||||
parser.setApplicationDescription(description);
|
||||
QCommandLineOption len(QStringList() << "L" << "length",
|
||||
QObject::tr("Length of the generated password"),
|
||||
QObject::tr("length"));
|
||||
parser.addOption(len);
|
||||
QCommandLineOption lower(QStringList() << "l"
|
||||
<< "lower",
|
||||
QCommandLineOption lower(QStringList() << "l" << "lower",
|
||||
QObject::tr("Use lowercase characters"));
|
||||
parser.addOption(lower);
|
||||
QCommandLineOption upper(QStringList() << "u"
|
||||
<< "upper",
|
||||
QCommandLineOption upper(QStringList() << "u" << "upper",
|
||||
QObject::tr("Use uppercase characters"));
|
||||
parser.addOption(upper);
|
||||
QCommandLineOption numeric(QStringList() << "n"
|
||||
<< "numeric",
|
||||
QCommandLineOption numeric(QStringList() << "n" << "numeric",
|
||||
QObject::tr("Use numbers."));
|
||||
parser.addOption(numeric);
|
||||
QCommandLineOption special(QStringList() << "s"
|
||||
<< "special",
|
||||
QCommandLineOption special(QStringList() << "s" << "special",
|
||||
QObject::tr("Use special characters"));
|
||||
parser.addOption(special);
|
||||
QCommandLineOption extended(QStringList() << "e"
|
||||
<< "extended",
|
||||
QCommandLineOption extended(QStringList() << "e" << "extended",
|
||||
QObject::tr("Use extended ASCII"));
|
||||
parser.addOption(extended);
|
||||
QCommandLineOption exclude(QStringList() << "x"
|
||||
<< "exclude",
|
||||
QCommandLineOption exclude(QStringList() << "x" << "exclude",
|
||||
QObject::tr("Exclude character set"),
|
||||
QObject::tr("chars"));
|
||||
parser.addOption(exclude);
|
||||
|
@ -78,12 +73,12 @@ int Generate::execute(const QStringList& arguments)
|
|||
QCommandLineOption every_group(QStringList() << "every-group",
|
||||
QObject::tr("Include characters from every selected group"));
|
||||
parser.addOption(every_group);
|
||||
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
if (!args.isEmpty()) {
|
||||
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate");
|
||||
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -93,7 +88,7 @@ int Generate::execute(const QStringList& arguments)
|
|||
passwordGenerator.setLength(PasswordGenerator::DefaultLength);
|
||||
} else {
|
||||
int length = parser.value(len).toInt();
|
||||
passwordGenerator.setLength(length);
|
||||
passwordGenerator.setLength(static_cast<size_t>(length));
|
||||
}
|
||||
|
||||
PasswordGenerator::CharClasses classes = 0x0;
|
||||
|
@ -128,12 +123,12 @@ int Generate::execute(const QStringList& arguments)
|
|||
passwordGenerator.setExcludedChars(parser.value(exclude));
|
||||
|
||||
if (!passwordGenerator.isValid()) {
|
||||
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate");
|
||||
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli generate");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QString password = passwordGenerator.generatePassword();
|
||||
outputTextStream << password << endl;
|
||||
out << password << endl;
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include "List.h"
|
||||
#include "cli/Utils.h"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QTextStream>
|
||||
|
@ -39,22 +40,20 @@ List::~List()
|
|||
|
||||
int List::execute(const QStringList& arguments)
|
||||
{
|
||||
QTextStream out(stdout);
|
||||
QTextStream out(Utils::STDOUT);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
parser.setApplicationDescription(description);
|
||||
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
|
||||
parser.addPositionalArgument("group", QObject::tr("Path of the group to list. Default is /"), QString("[group]"));
|
||||
QCommandLineOption keyFile(QStringList() << "k"
|
||||
<< "key-file",
|
||||
parser.addPositionalArgument("group", QObject::tr("Path of the group to list. Default is /"), "[group]");
|
||||
QCommandLineOption keyFile(QStringList() << "k" << "key-file",
|
||||
QObject::tr("Key file of the database."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFile);
|
||||
|
||||
QCommandLineOption recursiveOption(QStringList() << "R"
|
||||
<< "recursive",
|
||||
QCommandLineOption recursiveOption(QStringList() << "R" << "recursive",
|
||||
QObject::tr("Recursive mode, list elements recursively"));
|
||||
parser.addOption(recursiveOption);
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
@ -65,33 +64,33 @@ int List::execute(const QStringList& arguments)
|
|||
|
||||
bool recursive = parser.isSet(recursiveOption);
|
||||
|
||||
Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
|
||||
if (db == nullptr) {
|
||||
QScopedPointer<Database> db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (args.size() == 2) {
|
||||
return this->listGroup(db, recursive, args.at(1));
|
||||
return listGroup(db.data(), recursive, args.at(1));
|
||||
}
|
||||
return this->listGroup(db, recursive);
|
||||
return listGroup(db.data(), recursive);
|
||||
}
|
||||
|
||||
int List::listGroup(Database* database, bool recursive, QString groupPath)
|
||||
int List::listGroup(Database* database, bool recursive, const QString& groupPath)
|
||||
{
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
if (groupPath.isEmpty()) {
|
||||
outputTextStream << database->rootGroup()->print(recursive);
|
||||
outputTextStream.flush();
|
||||
out << database->rootGroup()->print(recursive) << flush;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
Group* group = database->rootGroup()->findGroupByPath(groupPath);
|
||||
if (group == nullptr) {
|
||||
qCritical("Cannot find group %s.", qPrintable(groupPath));
|
||||
if (!group) {
|
||||
err << QObject::tr("Cannot find group %1.").arg(groupPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << group->print(recursive);
|
||||
outputTextStream.flush();
|
||||
out << group->print(recursive) << flush;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
List();
|
||||
~List();
|
||||
int execute(const QStringList& arguments);
|
||||
int listGroup(Database* database, bool recursive, QString groupPath = QString(""));
|
||||
int listGroup(Database* database, bool recursive, const QString& groupPath = {});
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_LIST_H
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <utility>
|
||||
|
||||
/*
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
|
@ -25,6 +27,7 @@
|
|||
#include <QTextStream>
|
||||
|
||||
#include "cli/Utils.h"
|
||||
#include "core/Global.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
|
@ -41,18 +44,17 @@ Locate::~Locate()
|
|||
|
||||
int Locate::execute(const QStringList& arguments)
|
||||
{
|
||||
|
||||
QTextStream out(stdout);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
parser.setApplicationDescription(description);
|
||||
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
|
||||
parser.addPositionalArgument("term", QObject::tr("Search term."));
|
||||
QCommandLineOption keyFile(QStringList() << "k"
|
||||
<< "key-file",
|
||||
QCommandLineOption keyFile(QStringList() << "k" << "key-file",
|
||||
QObject::tr("Key file of the database."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFile);
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
@ -61,26 +63,27 @@ int Locate::execute(const QStringList& arguments)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
|
||||
QScopedPointer<Database> db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return this->locateEntry(db, args.at(1));
|
||||
return locateEntry(db.data(), args.at(1));
|
||||
}
|
||||
|
||||
int Locate::locateEntry(Database* database, QString searchTerm)
|
||||
int Locate::locateEntry(Database* database, const QString& searchTerm)
|
||||
{
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QStringList results = database->rootGroup()->locate(searchTerm);
|
||||
if (results.isEmpty()) {
|
||||
outputTextStream << "No results for that search term" << endl;
|
||||
return EXIT_SUCCESS;
|
||||
err << "No results for that search term." << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (QString result : results) {
|
||||
outputTextStream << result << endl;
|
||||
for (const QString& result : asConst(results)) {
|
||||
out << result << endl;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
Locate();
|
||||
~Locate();
|
||||
int execute(const QStringList& arguments);
|
||||
int locateEntry(Database* database, QString searchTerm);
|
||||
int locateEntry(Database* database, const QString& searchTerm);
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_LOCATE_H
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "Merge.h"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
|
@ -24,6 +22,9 @@
|
|||
|
||||
#include "core/Database.h"
|
||||
#include "core/Merger.h"
|
||||
#include "cli/Utils.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
Merge::Merge()
|
||||
{
|
||||
|
@ -37,29 +38,28 @@ Merge::~Merge()
|
|||
|
||||
int Merge::execute(const QStringList& arguments)
|
||||
{
|
||||
QTextStream out(stdout);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
parser.setApplicationDescription(description);
|
||||
parser.addPositionalArgument("database1", QObject::tr("Path of the database to merge into."));
|
||||
parser.addPositionalArgument("database2", QObject::tr("Path of the database to merge from."));
|
||||
|
||||
QCommandLineOption samePasswordOption(QStringList() << "s"
|
||||
<< "same-credentials",
|
||||
QCommandLineOption samePasswordOption(QStringList() << "s" << "same-credentials",
|
||||
QObject::tr("Use the same credentials for both database files."));
|
||||
|
||||
QCommandLineOption keyFile(QStringList() << "k"
|
||||
<< "key-file",
|
||||
QCommandLineOption keyFile(QStringList() << "k" << "key-file",
|
||||
QObject::tr("Key file of the database."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFile);
|
||||
QCommandLineOption keyFileFrom(QStringList() << "f"
|
||||
<< "key-file-from",
|
||||
QCommandLineOption keyFileFrom(QStringList() << "f" << "key-file-from",
|
||||
QObject::tr("Key file of the database to merge from."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFileFrom);
|
||||
|
||||
parser.addOption(samePasswordOption);
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
@ -68,30 +68,30 @@ int Merge::execute(const QStringList& arguments)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Database* db1 = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
|
||||
if (db1 == nullptr) {
|
||||
QScopedPointer<Database> db1(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
|
||||
if (!db1) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Database* db2;
|
||||
QScopedPointer<Database> db2;
|
||||
if (!parser.isSet("same-credentials")) {
|
||||
db2 = Database::unlockFromStdin(args.at(1), parser.value(keyFileFrom));
|
||||
db2.reset(Database::unlockFromStdin(args.at(1), parser.value(keyFileFrom), Utils::STDOUT, Utils::STDERR));
|
||||
} else {
|
||||
db2 = Database::openDatabaseFile(args.at(1), db1->key());
|
||||
db2.reset(Database::openDatabaseFile(args.at(1), db1->key()));
|
||||
}
|
||||
if (db2 == nullptr) {
|
||||
if (!db2) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Merger merger(db2, db1);
|
||||
Merger merger(db2.data(), db1.data());
|
||||
merger.merge();
|
||||
|
||||
QString errorMessage = db1->saveToFile(args.at(0));
|
||||
if (!errorMessage.isEmpty()) {
|
||||
qCritical("Unable to save database to file : %s", qPrintable(errorMessage));
|
||||
err << QObject::tr("Unable to save database to file : %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
out << "Successfully merged the database files.\n";
|
||||
out << "Successfully merged the database files." << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -44,40 +44,41 @@ Remove::~Remove()
|
|||
|
||||
int Remove::execute(const QStringList& arguments)
|
||||
{
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QTextStream out(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QCoreApplication::translate("main", "Remove an entry from the database."));
|
||||
parser.addPositionalArgument("database", QCoreApplication::translate("main", "Path of the database."));
|
||||
QCommandLineOption keyFile(QStringList() << "k"
|
||||
<< "key-file",
|
||||
parser.setApplicationDescription(QCoreApplication::tr("main", "Remove an entry from the database."));
|
||||
parser.addPositionalArgument("database", QCoreApplication::tr("main", "Path of the database."));
|
||||
QCommandLineOption keyFile(QStringList() << "k" << "key-file",
|
||||
QObject::tr("Key file of the database."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFile);
|
||||
parser.addPositionalArgument("entry", QCoreApplication::translate("main", "Path of the entry to remove."));
|
||||
parser.addPositionalArgument("entry", QCoreApplication::tr("main", "Path of the entry to remove."));
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
if (args.size() != 2) {
|
||||
outputTextStream << parser.helpText().replace("keepassxc-cli", "keepassxc-cli rm");
|
||||
out << parser.helpText().replace("keepassxc-cli", "keepassxc-cli rm");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
|
||||
if (db == nullptr) {
|
||||
QScopedPointer<Database> db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return this->removeEntry(db, args.at(0), args.at(1));
|
||||
return removeEntry(db.data(), args.at(0), args.at(1));
|
||||
}
|
||||
|
||||
int Remove::removeEntry(Database* database, QString databasePath, QString entryPath)
|
||||
int Remove::removeEntry(Database* database, const QString& databasePath, const QString& entryPath)
|
||||
{
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
Entry* entry = database->rootGroup()->findEntryByPath(entryPath);
|
||||
if (!entry) {
|
||||
qCritical("Entry %s not found.", qPrintable(entryPath));
|
||||
err << QObject::tr("Entry %1 not found.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -92,14 +93,14 @@ int Remove::removeEntry(Database* database, QString databasePath, QString entryP
|
|||
|
||||
QString errorMessage = database->saveToFile(databasePath);
|
||||
if (!errorMessage.isEmpty()) {
|
||||
qCritical("Unable to save database to file : %s", qPrintable(errorMessage));
|
||||
err << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (recycled) {
|
||||
outputTextStream << "Successfully recycled entry " << entryTitle << "." << endl;
|
||||
out << QObject::tr("Successfully recycled entry %1.").arg(entryTitle) << endl;
|
||||
} else {
|
||||
outputTextStream << "Successfully deleted entry " << entryTitle << "." << endl;
|
||||
out << QObject::tr("Successfully deleted entry %1.").arg(entryTitle) << endl;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
Remove();
|
||||
~Remove();
|
||||
int execute(const QStringList& arguments);
|
||||
int removeEntry(Database* database, QString databasePath, QString entryPath);
|
||||
int removeEntry(Database* database, const QString& databasePath, const QString& entryPath);
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_REMOVE_H
|
||||
|
|
|
@ -15,17 +15,19 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Show.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Show.h"
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Global.h"
|
||||
#include "Utils.h"
|
||||
|
||||
Show::Show()
|
||||
{
|
||||
|
@ -39,19 +41,17 @@ Show::~Show()
|
|||
|
||||
int Show::execute(const QStringList& arguments)
|
||||
{
|
||||
QTextStream out(stdout);
|
||||
QTextStream out(Utils::STDOUT);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(this->description);
|
||||
parser.setApplicationDescription(description);
|
||||
parser.addPositionalArgument("database", QObject::tr("Path of the database."));
|
||||
QCommandLineOption keyFile(QStringList() << "k"
|
||||
<< "key-file",
|
||||
QCommandLineOption keyFile(QStringList() << "k" << "key-file",
|
||||
QObject::tr("Key file of the database."),
|
||||
QObject::tr("path"));
|
||||
parser.addOption(keyFile);
|
||||
QCommandLineOption attributes(
|
||||
QStringList() << "a"
|
||||
<< "attributes",
|
||||
QStringList() << "a" << "attributes",
|
||||
QObject::tr(
|
||||
"Names of the attributes to show. "
|
||||
"This option can be specified more than once, with each attribute shown one-per-line in the given order. "
|
||||
|
@ -59,6 +59,7 @@ int Show::execute(const QStringList& arguments)
|
|||
QObject::tr("attribute"));
|
||||
parser.addOption(attributes);
|
||||
parser.addPositionalArgument("entry", QObject::tr("Name of the entry to show."));
|
||||
parser.addHelpOption();
|
||||
parser.process(arguments);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
@ -67,23 +68,23 @@ int Show::execute(const QStringList& arguments)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Database* db = Database::unlockFromStdin(args.at(0), parser.value(keyFile));
|
||||
if (db == nullptr) {
|
||||
QScopedPointer<Database> db(Database::unlockFromStdin(args.at(0), parser.value(keyFile), Utils::STDOUT, Utils::STDERR));
|
||||
if (!db) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return this->showEntry(db, parser.values(attributes), args.at(1));
|
||||
return showEntry(db.data(), parser.values(attributes), args.at(1));
|
||||
}
|
||||
|
||||
int Show::showEntry(Database* database, QStringList attributes, QString entryPath)
|
||||
int Show::showEntry(Database* database, QStringList attributes, const QString& entryPath)
|
||||
{
|
||||
|
||||
QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||
QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
QTextStream in(Utils::STDIN, QIODevice::ReadOnly);
|
||||
QTextStream out(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
QTextStream err(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
Entry* entry = database->rootGroup()->findEntry(entryPath);
|
||||
if (!entry) {
|
||||
qCritical("Could not find entry with path %s.", qPrintable(entryPath));
|
||||
err << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -95,16 +96,16 @@ int Show::showEntry(Database* database, QStringList attributes, QString entryPat
|
|||
|
||||
// Iterate over the attributes and output them line-by-line.
|
||||
bool sawUnknownAttribute = false;
|
||||
for (QString attribute : attributes) {
|
||||
for (const QString& attribute : asConst(attributes)) {
|
||||
if (!entry->attributes()->contains(attribute)) {
|
||||
sawUnknownAttribute = true;
|
||||
qCritical("ERROR: unknown attribute '%s'.", qPrintable(attribute));
|
||||
err << QObject::tr("ERROR: unknown attribute %1.").arg(attribute) << endl;
|
||||
continue;
|
||||
}
|
||||
if (showAttributeNames) {
|
||||
outputTextStream << attribute << ": ";
|
||||
out << attribute << ": ";
|
||||
}
|
||||
outputTextStream << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl;
|
||||
out << entry->resolveMultiplePlaceholders(entry->attributes()->value(attribute)) << endl;
|
||||
}
|
||||
return sawUnknownAttribute ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
Show();
|
||||
~Show();
|
||||
int execute(const QStringList& arguments);
|
||||
int showEntry(Database* database, QStringList attributes, QString entryPath);
|
||||
int showEntry(Database* database, QStringList attributes, const QString& entryPath);
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_SHOW_H
|
||||
|
|
|
@ -25,9 +25,25 @@
|
|||
#endif
|
||||
|
||||
#include <QProcess>
|
||||
#include <QTextStream>
|
||||
|
||||
void Utils::setStdinEcho(bool enable = true)
|
||||
namespace Utils
|
||||
{
|
||||
/**
|
||||
* STDOUT file handle for the CLI.
|
||||
*/
|
||||
FILE* STDOUT = stdout;
|
||||
|
||||
/**
|
||||
* STDERR file handle for the CLI.
|
||||
*/
|
||||
FILE* STDERR = stderr;
|
||||
|
||||
/**
|
||||
* STDIN file handle for the CLI.
|
||||
*/
|
||||
FILE* STDIN = stdin;
|
||||
|
||||
void setStdinEcho(bool enable = true)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
HANDLE hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
@ -56,28 +72,55 @@ void Utils::setStdinEcho(bool enable = true)
|
|||
#endif
|
||||
}
|
||||
|
||||
QString Utils::getPassword()
|
||||
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)
|
||||
{
|
||||
static QTextStream inputTextStream(stdin, QIODevice::ReadOnly);
|
||||
static QTextStream outputTextStream(stdout, QIODevice::WriteOnly);
|
||||
nextPasswords.append(password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a user password from STDIN or return a password previously
|
||||
* set by \link setNextPassword().
|
||||
*
|
||||
* @return the password
|
||||
*/
|
||||
QString getPassword()
|
||||
{
|
||||
QTextStream out(STDOUT, QIODevice::WriteOnly);
|
||||
|
||||
// return preset password if one is set
|
||||
if (!nextPasswords.isEmpty()) {
|
||||
auto password = nextPasswords.takeFirst();
|
||||
// simulate user entering newline
|
||||
out << endl;
|
||||
return password;
|
||||
}
|
||||
|
||||
QTextStream in(STDIN, QIODevice::ReadOnly);
|
||||
|
||||
setStdinEcho(false);
|
||||
QString line = inputTextStream.readLine();
|
||||
QString line = in.readLine();
|
||||
setStdinEcho(true);
|
||||
|
||||
// The new line was also not echoed, but we do want to echo it.
|
||||
outputTextStream << "\n";
|
||||
outputTextStream.flush();
|
||||
out << endl;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* A valid and running event loop is needed to use the global QClipboard,
|
||||
* so we need to use this from the CLI.
|
||||
*/
|
||||
int Utils::clipText(const QString& text)
|
||||
int clipText(const QString& text)
|
||||
{
|
||||
QTextStream err(Utils::STDERR);
|
||||
|
||||
QString programName = "";
|
||||
QStringList arguments;
|
||||
|
@ -98,16 +141,18 @@ int Utils::clipText(const QString& text)
|
|||
#endif
|
||||
|
||||
if (programName.isEmpty()) {
|
||||
qCritical("No program defined for clipboard manipulation");
|
||||
err << QObject::tr("No program defined for clipboard manipulation");
|
||||
err.flush();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QProcess* clipProcess = new QProcess(nullptr);
|
||||
auto* clipProcess = new QProcess(nullptr);
|
||||
clipProcess->start(programName, arguments);
|
||||
clipProcess->waitForStarted();
|
||||
|
||||
if (clipProcess->state() != QProcess::Running) {
|
||||
qCritical("Unable to start program %s", qPrintable(programName));
|
||||
err << QObject::tr("Unable to start program %1").arg(programName);
|
||||
err.flush();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -120,3 +165,5 @@ int Utils::clipText(const QString& text)
|
|||
|
||||
return clipProcess->exitCode();
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
|
|
|
@ -19,13 +19,18 @@
|
|||
#define KEEPASSXC_UTILS_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QTextStream>
|
||||
|
||||
class Utils
|
||||
namespace Utils
|
||||
{
|
||||
public:
|
||||
static void setStdinEcho(bool enable);
|
||||
static QString getPassword();
|
||||
static int clipText(const QString& text);
|
||||
extern FILE* STDOUT;
|
||||
extern FILE* STDERR;
|
||||
extern FILE* STDIN;
|
||||
|
||||
void setStdinEcho(bool enable);
|
||||
QString getPassword();
|
||||
void setNextPassword(const QString& password);
|
||||
int clipText(const QString& text);
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_UTILS_H
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include <cli/Command.h>
|
||||
|
||||
#include "config-keepassx.h"
|
||||
#include "core/Tools.h"
|
||||
#include "core/Bootstrap.h"
|
||||
#include "crypto/Crypto.h"
|
||||
|
||||
#if defined(WITH_ASAN) && defined(WITH_LSAN)
|
||||
|
@ -34,17 +34,17 @@
|
|||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#ifdef QT_NO_DEBUG
|
||||
Tools::disableCoreDumps();
|
||||
#endif
|
||||
|
||||
if (!Crypto::init()) {
|
||||
qFatal("Fatal error while testing the cryptographic functions:\n%s", qPrintable(Crypto::errorString()));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setApplicationVersion(KEEPASSX_VERSION);
|
||||
QCoreApplication::setApplicationVersion(KEEPASSX_VERSION);
|
||||
|
||||
#ifdef QT_NO_DEBUG
|
||||
Bootstrap::bootstrapApplication();
|
||||
#endif
|
||||
|
||||
QTextStream out(stdout);
|
||||
QStringList arguments;
|
||||
|
@ -69,7 +69,7 @@ int main(int argc, char** argv)
|
|||
// recognized by this parser.
|
||||
parser.parse(arguments);
|
||||
|
||||
if (parser.positionalArguments().size() < 1) {
|
||||
if (parser.positionalArguments().empty()) {
|
||||
if (parser.isSet("version")) {
|
||||
// Switch to parser.showVersion() when available (QT 5.4).
|
||||
out << KEEPASSX_VERSION << endl;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue