diff --git a/src/controller/commands.hpp b/src/controller/commands.hpp index b46ad429..442f5e51 100644 --- a/src/controller/commands.hpp +++ b/src/controller/commands.hpp @@ -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; } diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index 61ce39df..b9bf3663 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -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 { @@ -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; @@ -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() @@ -143,43 +140,35 @@ void Controller::createWindow() } void Controller::onCardsAvailable( - const std::vector& availableEids) -{ - 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; - } - - window->showWaitingForCardPage(commandHandler->commandType()); + const std::vector& availableEids) noexcept +try { + REQUIRE_NON_NULL(commandHandler) + REQUIRE_NON_NULL(window) + REQUIRE_NOT_EMPTY_CONTAINS_NON_NULL_PTRS(availableEids) - commandHandler->connectSignals(window); + 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; + } - runCommandHandler(availableEids); + window->showWaitingForCardPage(commandHandler->commandType()); - } catch (const std::exception& error) { - onCriticalFailure(error.what()); - } -} + commandHandler->connectSignals(window); -void Controller::runCommandHandler(std::vector availableEids) noexcept -try { auto* commandHandlerRunThread = - new CommandHandlerRunThread(this, *commandHandler, std::move(availableEids)); + new CommandHandlerRunThread(this, *commandHandler, availableEids); connectRetry(commandHandlerRunThread); + commandHandlerRunThread->start(); + } catch (const std::exception& error) { onCriticalFailure(error.what()); } -void Controller::onCertificatesLoaded() -{ +void Controller::onCertificatesLoaded() noexcept +try { auto* cardEventMonitorThread = new CardEventMonitorThread(this, commandType()); connect(this, &Controller::stopCardEventMonitorThread, cardEventMonitorThread, &CardEventMonitorThread::requestInterruption); @@ -187,9 +176,11 @@ void Controller::onCertificatesLoaded() &Controller::onCriticalFailure); connect(cardEventMonitorThread, &CardEventMonitorThread::cardEvent, this, &Controller::onRetry); cardEventMonitorThread->start(); +} catch (const std::exception& error) { + onCriticalFailure(error.what()); } -void Controller::disposeUI() +void Controller::disposeUI() noexcept { if (window) { window->disconnect(); @@ -215,42 +206,32 @@ try { 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); - - 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; - } + _result = res; + writeResponseToStdOut(isInStdinMode, res, commandHandler->commandType()); - window->quit(); + exit(); +} catch (const std::exception& error) { + onCriticalFailure(error.what()); } -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) const @@ -262,7 +243,7 @@ void Controller::connectRetry(const ControllerChildThread* childThread) const connect(childThread, &ControllerChildThread::cancel, this, &Controller::onDialogCancel); } -void Controller::onDialogOK(const EidCertificateAndPinInfo& certAndPinInfo) +void Controller::onDialogOK(const EidCertificateAndPinInfo& certAndPinInfo) noexcept { if (commandHandler) { onConfirmCommandHandler(certAndPinInfo); @@ -272,16 +253,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; @@ -299,16 +282,19 @@ void Controller::onCriticalFailure(const QString& error) writeResponseToStdOut(isInStdinMode, _result, commandType()); } exit(); +} catch (const std::exception& e) { + qCritical() << "Failed to write stdout" << e.what(); + exit(); } -void Controller::exit() +void Controller::exit() noexcept { disposeUI(); waitForChildThreads(); emit quit(); } -void Controller::waitForChildThreads() +void Controller::waitForChildThreads() noexcept { for (auto* thread : findChildren()) { qDebug() << "Interrupting thread" << uintptr_t(thread); @@ -319,7 +305,7 @@ void Controller::waitForChildThreads() } } -CommandType Controller::commandType() +CommandType Controller::commandType() const noexcept { return commandHandler ? commandHandler->commandType() : CommandType(CommandType::INSERT_CARD); } diff --git a/src/controller/controller.hpp b/src/controller/controller.hpp index 7bffe67c..0464fe6a 100644 --- a/src/controller/controller.hpp +++ b/src/controller/controller.hpp @@ -43,42 +43,42 @@ class Controller : public QObject void stopCardEventMonitorThread(); public: // slots - void run(); + void run() noexcept; // Called either directly from run() or from the monitor thread when cards are available. - void onCardsAvailable(const std::vector& availableEids); + void + onCardsAvailable(const std::vector& availableEids) noexcept; // Called when CommandHandlerRunThread finishes execution. - void onCertificatesLoaded(); + void onCertificatesLoaded() noexcept; // Called either directly from onDialogOK(). void onConfirmCommandHandler(const EidCertificateAndPinInfo& certAndPinInfo) noexcept; // Called from CommandHandlerConfirm thread. - void onCommandHandlerConfirmCompleted(const QVariantMap& result); + void onCommandHandlerConfirmCompleted(const QVariantMap& result) noexcept; // Called from the dialog when user chooses to retry errors that have occured in child threads. - void onRetry(); + void onRetry() noexcept; // User events from the dialog. - void onDialogOK(const EidCertificateAndPinInfo& certAndPinInfo); - void onDialogCancel(); + void onDialogOK(const EidCertificateAndPinInfo& certAndPinInfo) noexcept; + void onDialogCancel() noexcept; // Failure handler, reports the error and quits the application. - void onCriticalFailure(const QString& error); + void onCriticalFailure(const QString& error) noexcept; private: void startCommandExecution(); - void runCommandHandler(std::vector availableEids) noexcept; void connectRetry(const ControllerChildThread* childThread) const; void createWindow(); - void disposeUI(); - void exit(); - void waitForChildThreads(); - CommandType commandType(); + void disposeUI() noexcept; + void exit() noexcept; + void waitForChildThreads() noexcept; + CommandType commandType() const noexcept; CommandWithArgumentsPtr command; - CommandHandler::ptr commandHandler = nullptr; + CommandHandler::ptr commandHandler; // As the Qt::WA_DeleteOnClose flag is set, the dialog is deleted automatically. observer_ptr window = nullptr; QVariantMap _result;