diff --git a/.github/workflows/cmake-linux-fedora.yml b/.github/workflows/cmake-linux-fedora.yml index b9dcf253..49a42c12 100644 --- a/.github/workflows/cmake-linux-fedora.yml +++ b/.github/workflows/cmake-linux-fedora.yml @@ -14,13 +14,13 @@ jobs: container: fedora:${{ matrix.container }} strategy: matrix: - container: [41, 42, 43] + container: [42, 43] steps: - name: Install Deps run: dnf install -y --setopt=install_weak_deps=False git gcc-c++ cmake rpm-build openssl-devel pcsc-lite-devel qt6-qtsvg-devel qt6-qttools-devel gtest-devel - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: recursive diff --git a/src/controller/CMakeLists.txt b/src/controller/CMakeLists.txt index 582c8798..6a30eae8 100644 --- a/src/controller/CMakeLists.txt +++ b/src/controller/CMakeLists.txt @@ -22,7 +22,6 @@ add_library(controller STATIC logging.cpp logging.hpp qeid.hpp - retriableerror.cpp retriableerror.hpp threads/cardeventmonitorthread.hpp threads/commandhandlerconfirmthread.hpp diff --git a/src/controller/commands.cpp b/src/controller/commands.cpp index d484bbbc..4b068637 100644 --- a/src/controller/commands.cpp +++ b/src/controller/commands.cpp @@ -22,7 +22,7 @@ #include "commands.hpp" -#include "magic_enum/magic_enum.hpp" +#include #include #include @@ -51,5 +51,5 @@ CommandType commandNameToCommandType(const QString& cmdName) CommandType::operator std::string() const { - return std::string(magic_enum::enum_name(value)); + return QMetaEnum::fromType().valueToKey(value); } diff --git a/src/controller/commands.hpp b/src/controller/commands.hpp index b46ad429..5174aab7 100644 --- a/src/controller/commands.hpp +++ b/src/controller/commands.hpp @@ -29,28 +29,28 @@ class CommandType { + Q_GADGET public: - enum CommandTypeEnum { + enum CommandTypeEnum : quint8 { + NONE, INSERT_CARD, GET_SIGNING_CERTIFICATE, AUTHENTICATE, SIGN, QUIT, ABOUT, - NONE = -1 }; + Q_ENUM(CommandTypeEnum) - CommandType() = default; - constexpr CommandType(const CommandTypeEnum _value) : value(_value) {} + constexpr CommandType(CommandTypeEnum _value = NONE) noexcept : value(_value) {} - constexpr bool operator==(CommandTypeEnum other) const { return value == other; } - constexpr bool operator!=(CommandTypeEnum other) const { return value != other; } - constexpr operator CommandTypeEnum() const { return value; } + constexpr bool operator==(CommandTypeEnum other) const noexcept { return value == other; } + constexpr operator CommandTypeEnum() const noexcept { return value; } operator std::string() const; private: - CommandTypeEnum value = NONE; + CommandTypeEnum value; }; extern const QString CMDLINE_GET_SIGNING_CERTIFICATE; diff --git a/src/controller/controller.cpp b/src/controller/controller.cpp index 570ac39d..260244ad 100644 --- a/src/controller/controller.cpp +++ b/src/controller/controller.cpp @@ -233,7 +233,7 @@ void Controller::onCommandHandlerConfirmCompleted(const QVariantMap& res) _result = res; writeResponseToStdOut(isInStdinMode, res, commandHandler->commandType()); } catch (const std::exception& error) { - qCritical() << "Command" << std::string(commandType()) + qCritical() << "Command" << commandType() << "fatal error while writing response to stdout:" << error; } @@ -288,8 +288,7 @@ void Controller::onDialogCancel() void Controller::onCriticalFailure(const QString& error) { emit stopCardEventMonitorThread(); - qCritical() << "Exiting due to command" << std::string(commandType()) - << "fatal error:" << error; + qCritical() << "Exiting due to command" << commandType() << "fatal error:" << error; _result = makeErrorObject(RESP_TECH_ERROR, QStringLiteral("Technical error, see application logs")); disposeUI(); diff --git a/src/controller/logging.hpp b/src/controller/logging.hpp index 60819862..5ca69218 100644 --- a/src/controller/logging.hpp +++ b/src/controller/logging.hpp @@ -26,17 +26,13 @@ void setupLogging(); -inline QDebug operator<<(QDebug out, const std::string& s) +#if QT_VERSION < QT_VERSION_CHECK(6, 5, 0) +template +inline QDebug operator<<(QDebug out, const std::basic_string &s) { - out << QString::fromStdString(s); - return out; -} - -inline QDebug operator<<(QDebug out, const std::wstring& s) -{ - out << QString::fromStdWString(s); - return out; + return out << QUtf8StringView(s); } +#endif inline QDebug operator<<(QDebug out, const std::exception& e) { diff --git a/src/controller/retriableerror.cpp b/src/controller/retriableerror.cpp deleted file mode 100644 index 9b6c083f..00000000 --- a/src/controller/retriableerror.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2020-2024 Estonian Information System Authority - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "retriableerror.hpp" - -#include -#include "magic_enum/magic_enum.hpp" - -QDebug& operator<<(QDebug& d, const RetriableError e) -{ - return d << QString::fromStdString(std::string(magic_enum::enum_name(e))); -} - -RetriableError toRetriableError(const electronic_id::AutoSelectFailed::Reason reason) -{ - using Reason = electronic_id::AutoSelectFailed::Reason; - - switch (reason) { - case Reason::SERVICE_NOT_RUNNING: - return RetriableError::SMART_CARD_SERVICE_IS_NOT_RUNNING; - case Reason::NO_READERS: - return RetriableError::NO_SMART_CARD_READERS_FOUND; - case Reason::SINGLE_READER_NO_CARD: - case Reason::MULTIPLE_READERS_NO_CARD: - return RetriableError::NO_SMART_CARDS_FOUND; - case Reason::SINGLE_READER_UNSUPPORTED_CARD: - case Reason::MULTIPLE_READERS_NO_SUPPORTED_CARD: - return RetriableError::UNSUPPORTED_CARD; - } - return RetriableError::UNKNOWN_ERROR; -} diff --git a/src/controller/retriableerror.hpp b/src/controller/retriableerror.hpp index 161173b0..c75d169c 100644 --- a/src/controller/retriableerror.hpp +++ b/src/controller/retriableerror.hpp @@ -26,92 +26,101 @@ #include "pcsc-cpp/pcsc-cpp-utils.hpp" #include -#include -enum class RetriableError { - // libpcsc-cpp - SMART_CARD_SERVICE_IS_NOT_RUNNING, - NO_SMART_CARD_READERS_FOUND, - NO_SMART_CARDS_FOUND, - FAILED_TO_COMMUNICATE_WITH_CARD_OR_READER, - SMART_CARD_WAS_REMOVED, - SMART_CARD_TRANSACTION_FAILED, - SCARD_ERROR, - // libelectronic-id - SMART_CARD_CHANGE_REQUIRED, - SMART_CARD_COMMAND_ERROR, - PKCS11_TOKEN_NOT_PRESENT, - PKCS11_TOKEN_REMOVED, - PKCS11_ERROR, - // AutoSelectFailed::Reason - UNSUPPORTED_CARD, - // CertificateReader::run - NO_VALID_CERTIFICATE_AVAILABLE, - PIN_VERIFY_DISABLED, - // default - UNKNOWN_ERROR -}; - -Q_DECLARE_METATYPE(RetriableError) +class RetriableError +{ + Q_GADGET +public: + enum Error : quint8 { + // default + UNKNOWN_ERROR, + // libpcsc-cpp + SMART_CARD_SERVICE_IS_NOT_RUNNING, + NO_SMART_CARD_READERS_FOUND, + NO_SMART_CARDS_FOUND, + FAILED_TO_COMMUNICATE_WITH_CARD_OR_READER, + SMART_CARD_WAS_REMOVED, + SMART_CARD_TRANSACTION_FAILED, + SCARD_ERROR, + // libelectronic-id + SMART_CARD_CHANGE_REQUIRED, + SMART_CARD_COMMAND_ERROR, + PKCS11_TOKEN_NOT_PRESENT, + PKCS11_TOKEN_REMOVED, + PKCS11_ERROR, + // AutoSelectFailed::Reason + UNSUPPORTED_CARD, + // CertificateReader::run + NO_VALID_CERTIFICATE_AVAILABLE, + PIN_VERIFY_DISABLED, + }; + Q_ENUM(Error) -QDebug& operator<<(QDebug& d, const RetriableError); - -RetriableError toRetriableError(const electronic_id::AutoSelectFailed::Reason reason); + constexpr RetriableError(Error error = UNKNOWN_ERROR) : value(error) {} + constexpr explicit RetriableError(const electronic_id::AutoSelectFailed::Reason reason) + { + switch (reason) { + using enum electronic_id::AutoSelectFailed::Reason; + case SERVICE_NOT_RUNNING: + value = SMART_CARD_SERVICE_IS_NOT_RUNNING; + break; + case NO_READERS: + value = NO_SMART_CARD_READERS_FOUND; + break; + case SINGLE_READER_NO_CARD: + case MULTIPLE_READERS_NO_CARD: + value = NO_SMART_CARDS_FOUND; + break; + case SINGLE_READER_UNSUPPORTED_CARD: + case MULTIPLE_READERS_NO_SUPPORTED_CARD: + value = UNSUPPORTED_CARD; + break; + default: + value = UNKNOWN_ERROR; + } + } -// Define retriable error handling in one place so that it can be reused. + constexpr operator Error() const { return value; } -#define CATCH_PCSC_CPP_RETRIABLE_ERRORS(ERROR_HANDLER) \ - catch (const pcsc_cpp::ScardServiceNotRunningError& error) \ - { \ - ERROR_HANDLER(RetriableError::SMART_CARD_SERVICE_IS_NOT_RUNNING, error); \ - } \ - catch (const pcsc_cpp::ScardNoReadersError& error) \ - { \ - ERROR_HANDLER(RetriableError::NO_SMART_CARD_READERS_FOUND, error); \ - } \ - catch (const pcsc_cpp::ScardNoCardError& error) \ - { \ - ERROR_HANDLER(RetriableError::NO_SMART_CARDS_FOUND, error); \ - } \ - catch (const pcsc_cpp::ScardCardCommunicationFailedError& error) \ - { \ - ERROR_HANDLER(RetriableError::FAILED_TO_COMMUNICATE_WITH_CARD_OR_READER, error); \ - } \ - catch (const pcsc_cpp::ScardCardRemovedError& error) \ - { \ - ERROR_HANDLER(RetriableError::SMART_CARD_WAS_REMOVED, error); \ - } \ - catch (const pcsc_cpp::ScardTransactionFailedError& error) \ - { \ - ERROR_HANDLER(RetriableError::SMART_CARD_TRANSACTION_FAILED, error); \ - } \ - catch (const pcsc_cpp::ScardError& error) \ - { \ - ERROR_HANDLER(RetriableError::SCARD_ERROR, error); \ + static RetriableError catchRetriableError() + { + try { + throw; + } catch (const pcsc_cpp::ScardServiceNotRunningError& /*error*/) { + return SMART_CARD_SERVICE_IS_NOT_RUNNING; + } catch (const pcsc_cpp::ScardNoReadersError& /*error*/) { + return NO_SMART_CARD_READERS_FOUND; + } catch (const pcsc_cpp::ScardNoCardError& /*error*/) { + return NO_SMART_CARDS_FOUND; + } catch (const pcsc_cpp::ScardCardCommunicationFailedError& /*error*/) { + return FAILED_TO_COMMUNICATE_WITH_CARD_OR_READER; + } catch (const pcsc_cpp::ScardCardRemovedError& /*error*/) { + return SMART_CARD_WAS_REMOVED; + } catch (const pcsc_cpp::ScardTransactionFailedError& /*error*/) { + return SMART_CARD_TRANSACTION_FAILED; + } catch (const pcsc_cpp::ScardError& /*error*/) { + return SCARD_ERROR; + } catch (const electronic_id::SmartCardChangeRequiredError& /*error*/) { + return SMART_CARD_CHANGE_REQUIRED; + } catch (const electronic_id::SmartCardError& /*error*/) { + return SMART_CARD_COMMAND_ERROR; + } catch (const electronic_id::Pkcs11TokenNotPresent& /*error*/) { + return PKCS11_TOKEN_NOT_PRESENT; + } catch (const electronic_id::Pkcs11TokenRemoved& /*error*/) { + return PKCS11_TOKEN_REMOVED; + } catch (const electronic_id::Pkcs11Error& /*error*/) { + return PKCS11_ERROR; + } catch (...) { + return UNKNOWN_ERROR; + } } -#define CATCH_LIBELECTRONIC_ID_RETRIABLE_ERRORS(ERROR_HANDLER) \ - catch (const electronic_id::SmartCardChangeRequiredError& error) \ - { \ - ERROR_HANDLER(RetriableError::SMART_CARD_CHANGE_REQUIRED, error); \ - } \ - catch (const electronic_id::SmartCardError& error) \ - { \ - ERROR_HANDLER(RetriableError::SMART_CARD_COMMAND_ERROR, error); \ - } \ - catch (const electronic_id::Pkcs11TokenNotPresent& error) \ - { \ - ERROR_HANDLER(RetriableError::PKCS11_TOKEN_NOT_PRESENT, error); \ - } \ - catch (const electronic_id::Pkcs11TokenRemoved& error) \ - { \ - ERROR_HANDLER(RetriableError::PKCS11_TOKEN_REMOVED, error); \ - } \ - catch (const electronic_id::Pkcs11Error& error) \ - { \ - ERROR_HANDLER(RetriableError::PKCS11_ERROR, error); \ - } +private: + Error value; +}; #define WARN_RETRIABLE_ERROR(commandType, errorCode, error) \ qWarning().nospace() << "Command " << commandType << " retriable error " << errorCode << ": " \ << error + +Q_DECLARE_METATYPE(RetriableError) diff --git a/src/controller/threads/controllerchildthread.hpp b/src/controller/threads/controllerchildthread.hpp index a58aa6b4..ac56c7a7 100644 --- a/src/controller/threads/controllerchildthread.hpp +++ b/src/controller/threads/controllerchildthread.hpp @@ -23,9 +23,9 @@ #pragma once #include "commandhandler.hpp" -#include "retriableerror.hpp" -#include "qeid.hpp" #include "logging.hpp" +#include "qeid.hpp" +#include "retriableerror.hpp" #include #include @@ -51,11 +51,7 @@ class ControllerChildThread : public QThread } catch (const CommandHandlerVerifyPinFailed& error) { qWarning() << "Command" << commandType() << "PIN verification failed:" << error; - } - CATCH_PCSC_CPP_RETRIABLE_ERRORS(warnAndEmitRetry) - CATCH_LIBELECTRONIC_ID_RETRIABLE_ERRORS(warnAndEmitRetry) - catch (const electronic_id::VerifyPinFailed& error) - { + } catch (const electronic_id::VerifyPinFailed& error) { switch (error.status()) { using enum electronic_id::VerifyPinFailed::Status; case PIN_ENTRY_CANCEL: @@ -76,11 +72,15 @@ class ControllerChildThread : public QThread qCritical() << "Command" << commandType() << "fatal error:" << error; emit failure(error.what()); } - } - catch (const std::exception& error) - { - qCritical() << "Command" << commandType() << "fatal error:" << error; - emit failure(error.what()); + } catch (const std::exception& error) { + if (auto errorCode = RetriableError::catchRetriableError(); + errorCode != RetriableError::UNKNOWN_ERROR) { + WARN_RETRIABLE_ERROR(commandType(), errorCode, error); + emit retry(errorCode); + } else { + qCritical() << "Command" << commandType() << "fatal error:" << error; + emit failure(error.what()); + } } } @@ -88,7 +88,7 @@ class ControllerChildThread : public QThread signals: void cancel(); - void retry(const RetriableError error); + void retry(RetriableError error); void failure(const QString& error); protected: @@ -111,10 +111,4 @@ class ControllerChildThread : public QThread private: virtual void doRun() = 0; const std::string cmdType; - - void warnAndEmitRetry(const RetriableError errorCode, const std::exception& error) - { - WARN_RETRIABLE_ERROR(commandType(), errorCode, error); - emit retry(errorCode); - } }; diff --git a/src/controller/threads/waitforcardthread.hpp b/src/controller/threads/waitforcardthread.hpp index 7e26eb49..11a37349 100644 --- a/src/controller/threads/waitforcardthread.hpp +++ b/src/controller/threads/waitforcardthread.hpp @@ -36,7 +36,7 @@ class WaitForCardThread : public ControllerChildThread signals: void cardsAvailable(const std::vector& eids); - void statusUpdate(const RetriableError status); + void statusUpdate(RetriableError status); private: void doRun() override @@ -59,22 +59,17 @@ class WaitForCardThread : public ControllerChildThread emit failure(QString(__func__) + ": empty available supported card list"); } } catch (const electronic_id::AutoSelectFailed& failure) { - emit statusUpdate(toRetriableError(failure.reason())); + emit statusUpdate(RetriableError(failure.reason())); return false; - } - CATCH_PCSC_CPP_RETRIABLE_ERRORS(return warnAndEmitStatusUpdate) - CATCH_LIBELECTRONIC_ID_RETRIABLE_ERRORS(return warnAndEmitStatusUpdate) - catch (const std::exception& error) - { + } catch (const std::exception& error) { + if (auto errorCode = RetriableError::catchRetriableError(); + errorCode != RetriableError::UNKNOWN_ERROR) { + WARN_RETRIABLE_ERROR(commandType(), errorCode, error); + emit statusUpdate(errorCode); + return false; + } emit failure(error.what()); } return true; } - - bool warnAndEmitStatusUpdate(const RetriableError errorCode, const std::exception& error) - { - WARN_RETRIABLE_ERROR(commandType(), errorCode, error); - emit statusUpdate(errorCode); - return false; - } }; diff --git a/src/ui/webeiddialog.cpp b/src/ui/webeiddialog.cpp index 4f381ed8..d60e61d8 100644 --- a/src/ui/webeiddialog.cpp +++ b/src/ui/webeiddialog.cpp @@ -171,7 +171,8 @@ WebEidDialog::WebEidDialog(QWidget* parent) : WebEidUI(parent), ui(new Private) if (auto* button = qobject_cast(ui->selectionGroup->checkedButton())) { ui->lockedWarning->setHidden(button->certificateInfo().cardActive); - ui->okButton->setEnabled(currentCommand == CommandType::AUTHENTICATE || button->certificateInfo().cardActive); + ui->okButton->setEnabled(currentCommand == CommandType::AUTHENTICATE + || button->certificateInfo().cardActive); } ui->okButton->setFocus(); }); @@ -666,7 +667,8 @@ void WebEidDialog::setupPinPadProgressBarAndEmitWait(const EidCertificateAndPinI void WebEidDialog::setupPinInput(const EidCertificateAndPinInfo& certAndPinInfo) { ui->lockedWarning->setHidden(certAndPinInfo.cardActive); - setupPinPrompt(certAndPinInfo.pinInfo, currentCommand == CommandType::AUTHENTICATE || certAndPinInfo.cardActive); + setupPinPrompt(certAndPinInfo.pinInfo, + currentCommand == CommandType::AUTHENTICATE || certAndPinInfo.cardActive); // The allowed character ranges are from the SafeNet eToken guide: // 1. English uppercase letters (ASCII 0x41...0x5A). // 2. English lowercase letters (ASCII 0x61...0x7A).