Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/controller/commands.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ class CommandType
NONE = -1
};

CommandType() = default;
constexpr CommandType(const CommandTypeEnum _value) : value(_value) {}
CommandType() noexcept = default;
constexpr CommandType(const CommandTypeEnum _value) noexcept : value(_value) {}

constexpr bool operator==(CommandTypeEnum other) const { return value == other; }
constexpr bool operator!=(CommandTypeEnum other) const { return value != other; }
Expand Down
235 changes: 109 additions & 126 deletions src/controller/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace
const QString RESP_TECH_ERROR = QStringLiteral("ERR_WEBEID_NATIVE_FATAL");
const QString RESP_USER_CANCEL = QStringLiteral("ERR_WEBEID_USER_CANCELLED");

QVariantMap makeErrorObject(const QString& errorCode, const QString& errorMessage)
QVariantMap makeErrorObject(const QString& errorCode, const QString& errorMessage) noexcept
{
return {{QStringLiteral("error"),
QVariantMap {
Expand All @@ -53,8 +53,8 @@ QVariantMap makeErrorObject(const QString& errorCode, const QString& errorMessag

} // namespace

void Controller::run()
{
void Controller::run() noexcept
try {
// If a command is passed, the application is in command-line mode, else in stdin/stdout mode.
const bool isInCommandLineMode = bool(command);
isInStdinMode = !isInCommandLineMode;
Expand All @@ -63,41 +63,38 @@ void Controller::run()
<< QCoreApplication::applicationVersion() << "running in"
<< (isInStdinMode ? "stdin/stdout" : "command-line") << "mode";

try {
// TODO: cut out stdin mode separate class to avoid bugs in safari mode
if (isInStdinMode) {
// In stdin/stdout mode we first output the version as required by the WebExtension
// and then wait for the actual command.
writeResponseToStdOut(
isInStdinMode,
{{QStringLiteral("version"), QCoreApplication::applicationVersion()}},
"get-version");

command = readCommandFromStdin();
}

REQUIRE_NON_NULL(command)
switch (command->first) {
case CommandType::ABOUT:
WebEidUI::showAboutPage();
return;
case CommandType::QUIT:
// If quit is requested, respond with empty JSON object and quit immediately.
qInfo() << "Quit requested, exiting";
writeResponseToStdOut(true, {}, "quit");
emit quit();
return;
default:
break;
}

commandHandler = getCommandHandler(*command);

startCommandExecution();

} catch (const std::exception& error) {
onCriticalFailure(error.what());
// TODO: cut out stdin mode separate class to avoid bugs in safari mode
if (isInStdinMode) {
// In stdin/stdout mode we first output the version as required by the WebExtension
// and then wait for the actual command.
writeResponseToStdOut(isInStdinMode,
{{QStringLiteral("version"), QCoreApplication::applicationVersion()}},
"get-version");

command = readCommandFromStdin();
}

REQUIRE_NON_NULL(command)
switch (command->first) {
case CommandType::ABOUT:
WebEidUI::showAboutPage();
return;
case CommandType::QUIT:
// If quit is requested, respond with empty JSON object and quit immediately.
qInfo() << "Quit requested, exiting";
writeResponseToStdOut(true, {}, "quit");
emit quit();
return;
default:
break;
}

commandHandler = getCommandHandler(*command);

startCommandExecution();

} catch (const std::exception& error) {
onCriticalFailure(error.what());
}

void Controller::startCommandExecution()
Expand All @@ -121,7 +118,7 @@ void Controller::startCommandExecution()
waitForCardThread->start();
}

void Controller::createWindow()
void Controller::createWindow() noexcept
{
window = WebEidUI::createAndShowDialog(commandHandler->commandType());
connect(this, &Controller::statusUpdate, window, &WebEidUI::onSmartCardStatusUpdate);
Expand All @@ -135,53 +132,46 @@ void Controller::createWindow()
}

void Controller::onCardsAvailable(
const std::vector<electronic_id::ElectronicID::ptr>& availableEids)
{
try {
REQUIRE_NON_NULL(commandHandler)
REQUIRE_NON_NULL(window)
REQUIRE_NOT_EMPTY_CONTAINS_NON_NULL_PTRS(availableEids)
const std::vector<electronic_id::ElectronicID::ptr>& availableEids) noexcept
try {
REQUIRE_NON_NULL(commandHandler)
REQUIRE_NON_NULL(window)
REQUIRE_NOT_EMPTY_CONTAINS_NON_NULL_PTRS(availableEids)

for (const auto& card : availableEids) {
const auto protocol =
card->smartcard().protocol() == SmartCard::Protocol::T0 ? "T=0" : "T=1";
qInfo() << "Card" << card->name() << "in reader" << card->smartcard().readerName()
<< "using protocol" << protocol;
}
for (const auto& card : availableEids) {
const auto protocol =
card->smartcard().protocol() == SmartCard::Protocol::T0 ? "T=0" : "T=1";
qInfo() << "Card" << card->name() << "in reader" << card->smartcard().readerName()
<< "using protocol" << protocol;
}

window->showWaitingForCardPage(commandHandler->commandType());
window->showWaitingForCardPage(commandHandler->commandType());

commandHandler->connectSignals(window);
commandHandler->connectSignals(window);

runCommandHandler(availableEids);
runCommandHandler(availableEids);

} catch (const std::exception& error) {
onCriticalFailure(error.what());
}
} catch (const std::exception& error) {
onCriticalFailure(error.what());
}

void Controller::runCommandHandler(const std::vector<ElectronicID::ptr>& availableEids)
{
try {
auto* commandHandlerRunThread =
new CommandHandlerRunThread(this, *commandHandler, availableEids);
connectRetry(commandHandlerRunThread);

// When the command handler run thread retrieves certificates successfully, call
// onCertificatesLoaded() that starts card event monitoring while user enters the PIN.
connect(commandHandler.get(), &CommandHandler::singleCertificateReady, this,
&Controller::onCertificatesLoaded);
connect(commandHandler.get(), &CommandHandler::multipleCertificatesReady, this,
&Controller::onCertificatesLoaded);

commandHandlerRunThread->start();

} catch (const std::exception& error) {
onCriticalFailure(error.what());
}
auto* commandHandlerRunThread =
new CommandHandlerRunThread(this, *commandHandler, availableEids);
connectRetry(commandHandlerRunThread);

// When the command handler run thread retrieves certificates successfully, call
// onCertificatesLoaded() that starts card event monitoring while user enters the PIN.
connect(commandHandler.get(), &CommandHandler::singleCertificateReady, this,
&Controller::onCertificatesLoaded);
connect(commandHandler.get(), &CommandHandler::multipleCertificatesReady, this,
&Controller::onCertificatesLoaded);

commandHandlerRunThread->start();
}

void Controller::onCertificatesLoaded()
void Controller::onCertificatesLoaded() noexcept
{
auto* cardEventMonitorThread = new CardEventMonitorThread(this, commandType());
connect(this, &Controller::stopCardEventMonitorThread, cardEventMonitorThread,
Expand All @@ -192,7 +182,7 @@ void Controller::onCertificatesLoaded()
cardEventMonitorThread->start();
}

void Controller::disposeUI()
void Controller::disposeUI() noexcept
{
if (window) {
window->disconnect();
Expand All @@ -202,60 +192,49 @@ void Controller::disposeUI()
}
}

void Controller::onConfirmCommandHandler(const EidCertificateAndPinInfo& certAndPinInfo)
{
void Controller::onConfirmCommandHandler(const EidCertificateAndPinInfo& certAndPinInfo) noexcept
try {
emit stopCardEventMonitorThread();

try {
auto* commandHandlerConfirmThread =
new CommandHandlerConfirmThread(this, *commandHandler, window, certAndPinInfo);
connect(commandHandlerConfirmThread, &CommandHandlerConfirmThread::completed, this,
&Controller::onCommandHandlerConfirmCompleted);
connectRetry(commandHandlerConfirmThread);
auto* commandHandlerConfirmThread =
new CommandHandlerConfirmThread(this, *commandHandler, window, certAndPinInfo);
connect(commandHandlerConfirmThread, &CommandHandlerConfirmThread::completed, this,
&Controller::onCommandHandlerConfirmCompleted);
connectRetry(commandHandlerConfirmThread);

commandHandlerConfirmThread->start();
commandHandlerConfirmThread->start();

} catch (const std::exception& error) {
onCriticalFailure(error.what());
}
} catch (const std::exception& error) {
onCriticalFailure(error.what());
}

void Controller::onCommandHandlerConfirmCompleted(const QVariantMap& res)
{
REQUIRE_NON_NULL(window)

void Controller::onCommandHandlerConfirmCompleted(const QVariantMap& res) noexcept
try {
qDebug() << "Command completed";

// Schedule application exit when the UI dialog is destroyed.
connect(window, &WebEidUI::destroyed, this, &Controller::exit);
_result = res;
writeResponseToStdOut(isInStdinMode, res, commandHandler->commandType());

try {
_result = res;
writeResponseToStdOut(isInStdinMode, res, commandHandler->commandType());
} catch (const std::exception& error) {
qCritical() << "Command" << std::string(commandType())
<< "fatal error while writing response to stdout:" << error;
}

window->quit();
exit();
} catch (const std::exception& error) {
qCritical() << "Command" << std::string(commandType())
<< "fatal error while writing response to stdout:" << error;
}

void Controller::onRetry()
{
try {
// Dispose the UI, it will be re-created during next execution.
disposeUI();
// Command handler signals are still connected, disconnect them so that they can be
// reconnected during next execution.
commandHandler->disconnect();
// Before restarting, wait until child threads finish.
waitForChildThreads();

startCommandExecution();

} catch (const std::exception& error) {
onCriticalFailure(error.what());
}
void Controller::onRetry() noexcept
try {
// Dispose the UI, it will be re-created during next execution.
disposeUI();
// Command handler signals are still connected, disconnect them so that they can be
// reconnected during next execution.
commandHandler->disconnect();
// Before restarting, wait until child threads finish.
waitForChildThreads();

startCommandExecution();

} catch (const std::exception& error) {
onCriticalFailure(error.what());
}

void Controller::connectRetry(const ControllerChildThread* childThread)
Expand All @@ -267,7 +246,7 @@ void Controller::connectRetry(const ControllerChildThread* childThread)
connect(childThread, &ControllerChildThread::cancel, this, &Controller::onDialogCancel);
}

void Controller::onDialogOK(const EidCertificateAndPinInfo& certAndPinInfo)
void Controller::onDialogOK(const EidCertificateAndPinInfo& certAndPinInfo) noexcept
{
if (commandHandler) {
onConfirmCommandHandler(certAndPinInfo);
Expand All @@ -277,16 +256,18 @@ void Controller::onDialogOK(const EidCertificateAndPinInfo& certAndPinInfo)
}
}

void Controller::onDialogCancel()
{
void Controller::onDialogCancel() noexcept
try {
qDebug() << "User cancelled";
_result = makeErrorObject(RESP_USER_CANCEL, QStringLiteral("User cancelled"));
writeResponseToStdOut(isInStdinMode, _result, commandType());
exit();
} catch (const std::exception& e) {
onCriticalFailure(e.what());
}

void Controller::onCriticalFailure(const QString& error)
{
void Controller::onCriticalFailure(const QString& error) noexcept
try {
emit stopCardEventMonitorThread();
qCritical() << "Exiting due to command" << std::string(commandType())
<< "fatal error:" << error;
Expand All @@ -304,16 +285,18 @@ void Controller::onCriticalFailure(const QString& error)
writeResponseToStdOut(isInStdinMode, _result, commandType());
}
exit();
} catch (const std::exception& e) {
qCritical() << "Failed to write stdout" << e.what();
}

void Controller::exit()
void Controller::exit() noexcept
{
disposeUI();
waitForChildThreads();
emit quit();
}

void Controller::waitForChildThreads()
void Controller::waitForChildThreads() noexcept
{
for (auto* thread : findChildren<QThread*>()) {
qDebug() << "Interrupting thread" << uintptr_t(thread);
Expand All @@ -324,7 +307,7 @@ void Controller::waitForChildThreads()
}
}

CommandType Controller::commandType()
CommandType Controller::commandType() const noexcept
{
return commandHandler ? commandHandler->commandType() : CommandType(CommandType::INSERT_CARD);
}
Loading
Loading