refactor: proxy commandline arguments

Add a 'proxy' to forward external API calls into the main application
control flow. Right now it only supports forwarding commandline
arguments, but this construct is already useful as a starting point for
future D-Bus API support.
master
Johan Ouwerkerk 2021-02-22 19:15:02 +01:00 committed by Bhushan Shah
parent 7274dc4f25
commit ea19844300
4 changed files with 97 additions and 13 deletions

View File

@ -1,9 +1,9 @@
#
# SPDX-License-Identifier: BSD-2-Clause
# SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
# SPDX-FileCopyrightText: 2020-2021 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
#
set(Test_DEP_LIBS Qt5::Core Qt5::Concurrent Qt5::Test keysmith_lib test_lib)
set(Test_DEP_LIBS Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Test keysmith_lib test_lib)
set(app_test_SRCS
commandline-account-job.cpp

View File

@ -1,10 +1,12 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
* SPDX-FileCopyrightText: 2020-2021 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
*/
#include "cli.h"
#include "../logging_p.h"
#include "../model/qr.h"
#include "flows_p.h"
#include "state_p.h"
#include <KLocalizedString>
#include <QCommandLineOption>
@ -16,10 +18,7 @@ namespace app
{
void CommandLineOptions::addOptions(QCommandLineParser &parser)
{
parser.addPositionalArgument(
QStringLiteral("<uri>"),
i18nc("@info (<uri> placeholder)", "Optional account to add, formatted as otpauth:// URI (e.g. from a QR code)")
);
Proxy::addOptions(parser);
}
CommandLineOptions::CommandLineOptions(QCommandLineParser &parser, bool parseOk, QObject *parent) :
@ -107,4 +106,67 @@ namespace app
qCDebug(logger) << "Failed to signal result of processing the URI passed on the commandline";
}
}
Proxy::Proxy(QGuiApplication *app, QObject *parent) :
QObject(parent), m_keysmith(nullptr), m_app(app)
{
Q_ASSERT_X(m_app, Q_FUNC_INFO, "Should have a valid QGuiApplication instance");
}
void Proxy::addOptions(QCommandLineParser &parser)
{
parser.addPositionalArgument(
QStringLiteral("<uri>"),
i18nc("@info (<uri> placeholder)", "Optional account to add, formatted as otpauth:// URI (e.g. from a QR code)")
);
}
bool Proxy::parseCommandLine(QCommandLineParser &parser, const QStringList &argv)
{
// options that will be handled via UI interaction
app::Proxy::addOptions(parser);
return parser.parse(argv);
}
void Proxy::disable(void)
{
m_keysmith = nullptr;
}
bool Proxy::enable(Keysmith *keysmith)
{
Q_ASSERT_X(keysmith, Q_FUNC_INFO, "should be given a valid Keysmith instance");
if (m_keysmith) {
qCDebug(logger)
<< "Not (re)initialising proxy with new Keysmith instance:" << keysmith
<< "Already initialised with:" << m_keysmith;
return false;
}
m_keysmith = keysmith;
QObject::connect(m_keysmith, &QObject::destroyed, this, &Proxy::disable);
return true;
}
bool Proxy::proxy(const QCommandLineParser &parser, bool parsedOk)
{
if (!parsedOk) {
qCDebug(logger) << "Not proxying to Keysmith instance: invalid command line";
return false;
}
if (!m_keysmith) {
qCDebug(logger) << "Not proxying command line arguments: not initialised with a Keysmith instance";
return false;
}
auto state = flowStateOf(m_keysmith);
if (state->flowRunning()) {
qCDebug(logger) << "Not proxying command line arguments: a 'competing' flow is already running";
return false;
}
(new InitialFlow(m_keysmith))->run(parser);
return true;
}
}

View File

@ -1,15 +1,18 @@
/*
* SPDX-License-Identifier: GPL-3.0-or-later
* SPDX-FileCopyrightText: 2020 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
* SPDX-FileCopyrightText: 2020-2021 Johan Ouwerkerk <jm.ouwerkerk@gmail.com>
*/
#ifndef APP_COMMAND_LINE_H
#define APP_COMMAND_LINE_H
#include "keysmith.h"
#include "../model/input.h"
#include <QCommandLineParser>
#include <QGuiApplication>
#include <QObject>
#include <QString>
#include <QStringList>
namespace app
{
@ -56,6 +59,23 @@ namespace app
const QString m_errorText;
QCommandLineParser &m_parser;
};
class Proxy: public QObject
{
Q_OBJECT
public:
static bool parseCommandLine(QCommandLineParser &parser, const QStringList &argv);
static void addOptions(QCommandLineParser &parser);
public:
explicit Proxy(QGuiApplication *app, QObject *parent = nullptr);
bool enable(Keysmith *keysmith);
bool proxy(const QCommandLineParser &parser, bool parsedOk);
private Q_SLOTS:
void disable(void);
private:
Keysmith * m_keysmith;
QGuiApplication *m_app;
};
};
#endif

View File

@ -46,14 +46,12 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
QGuiApplication::setApplicationDisplayName(i18nc("@title", "Keysmith"));
QCommandLineParser cliParser;
// options that will be handled via UI interaction
app::CommandLineOptions::addOptions(cliParser);
// default/boilerplate options handled entirely via command line
const auto helpOption = cliParser.addHelpOption();
const auto versionOption = cliParser.addVersionOption();
bool parseOk = cliParser.parse(QCoreApplication::arguments());
bool parseOk = app::Proxy::parseCommandLine(cliParser, QCoreApplication::arguments());
/*
* First check for pure command line options and handle these.
@ -71,6 +69,8 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
return 0; // just to be explicit: showVersion() is documented to call exit()
}
app::Proxy proxy(&app);
QQmlApplicationEngine engine;
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
@ -90,11 +90,13 @@ Q_DECL_EXPORT int main(int argc, char *argv[])
qmlRegisterType<validators::IssuerValidator>("Keysmith.Validators", 1, 0, "AccountIssuerValidator");
qmlRegisterType<validators::Base32Validator>("Keysmith.Validators", 1, 0, "Base32SecretValidator");
qmlRegisterType<validators::UnsignedLongValidator>("Keysmith.Validators", 1, 0, "HOTPCounterValidator");
qmlRegisterSingletonType<app::Keysmith>("Keysmith.Application", 1, 0, "Keysmith", [](QQmlEngine *qml, QJSEngine *js) -> QObject *
qmlRegisterSingletonType<app::Keysmith>("Keysmith.Application", 1, 0, "Keysmith", [&proxy](QQmlEngine *qml, QJSEngine *js) -> QObject *
{
Q_UNUSED(js);
return new app::Keysmith(new app::Navigation(qml));
auto app = new app::Keysmith(new app::Navigation(qml));
proxy.enable(app);
return app;
});
qmlRegisterSingletonType<app::CommandLineOptions>("Keysmith.Application", 1, 0, "CommandLine", [parseOk, &cliParser](QQmlEngine *qml, QJSEngine *js) -> QObject *
{