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:
Janek Bevendorff 2018-09-29 19:00:47 +02:00
parent 18b22834c1
commit 113c8eb702
67 changed files with 2259 additions and 1250 deletions

View file

@ -16,19 +16,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QCommandLineParser>
#include <QFile>
#include <QTextStream>
#include <QCommandLineParser>
#include "config-keepassx.h"
#include "core/Config.h"
#include "core/Bootstrap.h"
#include "core/Tools.h"
#include "core/Translator.h"
#include "core/Config.h"
#include "crypto/Crypto.h"
#include "gui/Application.h"
#include "gui/MainWindow.h"
#include "gui/MessageBox.h"
#include "cli/Utils.h"
#if defined(WITH_ASAN) && defined(WITH_LSAN)
@ -45,55 +44,29 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin)
#endif
#endif
static inline void earlyQNetworkAccessManagerWorkaround()
{
// When QNetworkAccessManager is instantiated it regularly starts polling
// all network interfaces to see if anything changes and if so, what. This
// creates a latency spike every 10 seconds on Mac OS 10.12+ and Windows 7 >=
// when on a wifi connection.
// So here we disable it for lack of better measure.
// This will also cause this message: QObject::startTimer: Timers cannot
// have negative intervals
// For more info see:
// - https://bugreports.qt.io/browse/QTBUG-40332
// - https://bugreports.qt.io/browse/QTBUG-46015
qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
}
int main(int argc, char** argv)
{
#ifdef QT_NO_DEBUG
Tools::disableCoreDumps();
#endif
Tools::setupSearchPaths();
earlyQNetworkAccessManagerWorkaround();
Application app(argc, argv);
Application::setApplicationName("keepassxc");
Application::setApplicationVersion(KEEPASSX_VERSION);
// don't set organizationName as that changes the return value of
// QStandardPaths::writableLocation(QDesktopServices::DataLocation)
Bootstrap::bootstrapApplication();
QCommandLineParser parser;
parser.setApplicationDescription(
QCoreApplication::translate("main", "KeePassXC - cross-platform password manager"));
parser.addPositionalArgument(
"filename",
QCoreApplication::translate("main", "filenames of the password databases to open (*.kdbx)"),
"[filename(s)]");
parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassXC - cross-platform password manager"));
parser.addPositionalArgument("filename",
QCoreApplication::translate("main", "filenames of the password databases to open (*.kdbx)"), "[filename(s)]");
QCommandLineOption configOption(
"config", QCoreApplication::translate("main", "path to a custom config file"), "config");
QCommandLineOption keyfileOption(
"keyfile", QCoreApplication::translate("main", "key file of the database"), "keyfile");
QCommandLineOption pwstdinOption("pw-stdin",
QCoreApplication::translate("main", "read password of the database from stdin"));
QCoreApplication::translate("main", "read password of the database from stdin"));
// This is needed under Windows where clients send --parent-window parameter with Native Messaging connect method
QCommandLineOption parentWindowOption(QStringList() << "pw"
<< "parent-window",
QCoreApplication::translate("main", "Parent window handle"),
"handle");
QCommandLineOption parentWindowOption(
QStringList() << "pw" << "parent-window", QCoreApplication::translate("main", "Parent window handle"), "handle");
QCommandLineOption helpOption = parser.addHelpOption();
QCommandLineOption versionOption = parser.addVersionOption();
@ -115,9 +88,7 @@ int main(int argc, char** argv)
if (!fileNames.isEmpty()) {
app.sendFileNamesToRunningInstance(fileNames);
}
qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.")
.toUtf8()
.constData();
qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.").toUtf8().constData();
return 0;
}
@ -135,46 +106,14 @@ int main(int argc, char** argv)
Config::createConfigFromFile(parser.value(configOption));
}
Translator::installTranslators();
#ifdef Q_OS_MAC
// Don't show menu icons on OSX
QApplication::setAttribute(Qt::AA_DontShowIconsInMenus);
#endif
MainWindow mainWindow;
app.setMainWindow(&mainWindow);
QObject::connect(&app, SIGNAL(anotherInstanceStarted()), &mainWindow, SLOT(bringToFront()));
QObject::connect(&app, SIGNAL(applicationActivated()), &mainWindow, SLOT(bringToFront()));
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
QObject::connect(&app, SIGNAL(quitSignalReceived()), &mainWindow, SLOT(appExit()), Qt::DirectConnection);
// start minimized if configured
bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool();
bool minimizeToTray = config()->get("GUI/MinimizeToTray").toBool();
#ifndef Q_OS_LINUX
if (minimizeOnStartup) {
#else
// On some Linux systems, the window should NOT be minimized and hidden (i.e. not shown), at
// the same time (which would happen if both minimize on startup and minimize to tray are set)
// since otherwise it causes problems on restore as seen on issue #1595. Hiding it is enough.
if (minimizeOnStartup && !minimizeToTray) {
#endif
mainWindow.setWindowState(Qt::WindowMinimized);
}
if (!(minimizeOnStartup && minimizeToTray)) {
mainWindow.show();
}
if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) {
const QStringList fileNames = config()->get("LastOpenedDatabases").toStringList();
for (const QString& filename : fileNames) {
if (!filename.isEmpty() && QFile::exists(filename)) {
mainWindow.openDatabase(filename);
}
}
}
Bootstrap::restoreMainWindowState(mainWindow);
const bool pwstdin = parser.isSet(pwstdinOption);
for (const QString& filename : fileNames) {
@ -193,7 +132,7 @@ int main(int argc, char** argv)
}
}
int exitCode = app.exec();
int exitCode = Application::exec();
#if defined(WITH_ASAN) && defined(WITH_LSAN)
// do leak check here to prevent massive tail of end-of-process leak errors from third-party libraries