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 570ac39d..63b93498 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() @@ -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); @@ -135,53 +132,46 @@ 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) + const std::vector& 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& 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, @@ -192,7 +182,7 @@ void Controller::onCertificatesLoaded() cardEventMonitorThread->start(); } -void Controller::disposeUI() +void Controller::disposeUI() noexcept { if (window) { window->disconnect(); @@ -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) @@ -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); @@ -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; @@ -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()) { qDebug() << "Interrupting thread" << uintptr_t(thread); @@ -324,7 +307,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 e3009168..34eff573 100644 --- a/src/controller/controller.hpp +++ b/src/controller/controller.hpp @@ -43,42 +43,43 @@ 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); + 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(const std::vector& availableEids); void connectRetry(const ControllerChildThread* childThread); - void createWindow(); - void disposeUI(); - void exit(); - void waitForChildThreads(); - CommandType commandType(); + void createWindow() noexcept; + 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;