From 632c4051e09c0ceac0bde2f5e699c89855584141 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Mon, 14 Sep 2026 08:13:01 +0300 Subject: [PATCH] Remove card widget IB-8951 Signed-off-by: Raul Metsma --- client/CDocSupport.cpp | 2 +- client/Diagnostics.cpp | 2 +- client/MainWindow.cpp | 162 +-------- client/MainWindow.h | 5 - client/MainWindow.ui | 546 +++++++++------------------- client/QCNG.cpp | 2 +- client/QCryptoBackend.cpp | 137 +++---- client/QCryptoBackend.h | 13 +- client/QPCSC.cpp | 21 +- client/QPCSC.h | 2 +- client/QPCSC_p.h | 2 +- client/QPKCS11.cpp | 2 +- client/QSmartCard.cpp | 115 +++--- client/QSmartCard.h | 28 +- client/QSmartCard_p.h | 6 - client/dialogs/AddRecipients.cpp | 5 +- client/dialogs/SigningDialog.cpp | 108 +++--- client/dialogs/SigningDialog.h | 7 +- client/dialogs/SigningDialog.ui | 58 ++- client/translations/en.ts | 47 ++- client/translations/et.ts | 47 ++- client/widgets/AddressItem.cpp | 12 +- client/widgets/AddressItem.h | 4 +- client/widgets/CardListItem.cpp | 9 + client/widgets/CardListItem.h | 1 + client/widgets/ContainerPage.ui | 4 +- client/widgets/MyEidInfo.cpp | 297 ++++++++++++--- client/widgets/MyEidInfo.h | 37 +- client/widgets/MyEidInfo.ui | 603 ++++++++++++++++++++----------- client/widgets/NoCardInfo.cpp | 63 ---- client/widgets/NoCardInfo.h | 51 --- client/widgets/NoCardInfo.ui | 75 ---- client/widgets/WarningItem.h | 2 +- 33 files changed, 1176 insertions(+), 1299 deletions(-) delete mode 100644 client/widgets/NoCardInfo.cpp delete mode 100644 client/widgets/NoCardInfo.h delete mode 100644 client/widgets/NoCardInfo.ui diff --git a/client/CDocSupport.cpp b/client/CDocSupport.cpp index 8024d421f..196660798 100644 --- a/client/CDocSupport.cpp +++ b/client/CDocSupport.cpp @@ -194,7 +194,7 @@ checkConnection() return true; } return dispatchToMain([check] { - FadeInNotification::error(Application::mainWindow()->findChild(QStringLiteral("topBar")), + FadeInNotification::error(Application::mainWindow()->findChild(QStringLiteral("content")), check.errorString()); return false; }); diff --git a/client/Diagnostics.cpp b/client/Diagnostics.cpp index c2c8b9aac..99ca0684d 100644 --- a/client/Diagnostics.cpp +++ b/client/Diagnostics.cpp @@ -93,7 +93,7 @@ void Diagnostics::generalInfo(QTextStream &s) for( const QString &readername: QPCSC::instance().readers() ) { s << "
" << readername; - QPCSCReader reader( readername, &QPCSC::instance() ); + QPCSCReader reader(readername); if( !reader.isPresent() ) { #ifndef Q_OS_WIN /* Apple 10.5.7 and pcsc-lite previous to v1.5.5 do not support 0 as protocol identifier */ diff --git a/client/MainWindow.cpp b/client/MainWindow.cpp index 089db65a3..2468c357c 100644 --- a/client/MainWindow.cpp +++ b/client/MainWindow.cpp @@ -25,7 +25,6 @@ #include "CryptoDoc.h" #include "DigiDoc.h" #include "QCryptoBackend.h" -#include "QPCSC.h" #include "Settings.h" #include "SslCertificate.h" #include "TokenData.h" @@ -37,7 +36,6 @@ #include "dialogs/SigningDialog.h" #include "dialogs/WaitDialog.h" #include "dialogs/WarningDialog.h" -#include "widgets/CardPopup.h" #include "widgets/WarningItem.h" #include "widgets/WarningList.h" @@ -57,7 +55,6 @@ MainWindow::MainWindow( QWidget *parent ) ui->setupUi(this); ui->signIntroButton->setFocus(); - ui->noReaderInfoText->setProperty("currenttext", ui->noReaderInfoText->text()); ui->version->setText(QStringLiteral("%1%2").arg(tr("Ver. "), Application::applicationVersion())); connect(ui->version, &QPushButton::clicked, this, [this] {showSettings(SettingsDialog::DiagnosticsSettings);}); @@ -79,23 +76,8 @@ MainWindow::MainWindow( QWidget *parent ) connect(ui->help, &QToolButton::clicked, qApp, &Application::openHelp); connect(ui->settings, &QToolButton::clicked, this, [this] { showSettings(SettingsDialog::GeneralSettings); }); -#ifdef Q_OS_WIN - // Add grey line on Windows in order to separate white title bar from card selection bar - QWidget *separator = new QWidget(this); - separator->setFixedHeight(1); - separator->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - separator->setStyleSheet(QStringLiteral("background-color: #D9D9D9;")); - separator->resize(8000, 1); - separator->move(mapToGlobal(ui->topBar->pos())); - separator->show(); -#endif - - // Refresh ID card info in card widget - connect(qApp->cryptoManager(), &QCryptoManager::cacheChanged, this, &MainWindow::updateSelector); - // Refresh card info on "My EID" page - connect(qApp->cryptoManager()->smartcard(), &QSmartCard::tokenChanged, this, &MainWindow::updateMyEID); - connect(qApp->cryptoManager()->smartcard(), &QSmartCard::dataChanged, this, &MainWindow::updateMyEid); + connect(ui->eidPage, &MyEidInfo::dataChanged, this, &MainWindow::updateMyEid); connect(ui->signIntroButton, &QPushButton::clicked, this, [this] { openContainer(QStringLiteral("*.bdoc *.ddoc *.asice *.sce *.asics *.scs *.edoc *.adoc%1") @@ -116,11 +98,7 @@ MainWindow::MainWindow( QWidget *parent ) ui->crypto->warningIcon(true); }); - connect(ui->infoStack, &MyEidInfo::changePinClicked, this, &MainWindow::changePinClicked); - connect(ui->cardInfo, &CardWidget::selected, ui->selector, &QToolButton::toggle); - - updateMyEID(qApp->cryptoManager()->smartcard()->tokenData()); - updateMyEid(qApp->cryptoManager()->smartcard()->data()); + updateMyEid(ui->eidPage->cardData()); } MainWindow::~MainWindow() noexcept = default; @@ -145,20 +123,12 @@ void MainWindow::changeEvent(QEvent* event) if (event->type() == QEvent::LanguageChange) { ui->retranslateUi(this); - ui->noReaderInfoText->setText(tr(ui->noReaderInfoText->property("currenttext").toByteArray())); ui->version->setText(QStringLiteral("%1%2").arg(tr("Ver. "), Application::applicationVersion())); setWindowTitle(windowFilePath().isEmpty() ? tr("DigiDoc4 Client") : FileDialog::normalized(QFileInfo(windowFilePath()).fileName())); - ui->selector->setChecked(false); } QWidget::changeEvent(event); } -void MainWindow::changePinClicked(QSmartCardData::PinType type, QSmartCard::PinAction action) -{ - if(qApp->cryptoManager()->smartcard()->pinChange(type, action, ui->topBar)) - updateMyEid(qApp->cryptoManager()->smartcard()->data()); -} - void MainWindow::closeEvent(QCloseEvent * /*event*/) { cryptoDoc.reset(); @@ -211,13 +181,6 @@ QStringList MainWindow::dropEventFiles(QDropEvent *event) return files; } -void MainWindow::mouseReleaseEvent(QMouseEvent *event) -{ - if(auto *cardPopup = findChild()) - cardPopup->deleteLater(); - QWidget::mouseReleaseEvent(event); -} - void MainWindow::navigateToPage( Pages page, const QStringList &files, bool create ) { bool navigate = true; @@ -335,7 +298,7 @@ void MainWindow::convertToCDoc() ui->cryptoContainerPage->transition(cryptoDoc.get()); selectPage(CryptoDetails); - FadeInNotification::success(ui->topBar, tr("Converted to crypto container!")); + FadeInNotification::success(ui->content, tr("Converted to crypto container!")); } void MainWindow::onCryptoAction(int action) @@ -349,13 +312,13 @@ void MainWindow::onCryptoAction(int action) break; case ContainerConvert: if(cryptoDoc && wrap(cryptoDoc->fileName(), false)) - FadeInNotification::success(ui->topBar, tr("Converted to signed document!")); + FadeInNotification::success(ui->content, tr("Converted to signed document!")); break; case DecryptContainerSuccess: - FadeInNotification::success(ui->topBar, tr("Decryption succeeded!")); + FadeInNotification::success(ui->content, tr("Decryption succeeded!")); break; case EncryptContainerSuccess: - FadeInNotification::success(ui->topBar, tr("Encryption succeeded!")); + FadeInNotification::success(ui->content, tr("Encryption succeeded!")); break; case ContainerClearWarning: ui->crypto->warningIcon(false); @@ -500,7 +463,6 @@ void MainWindow::selectPage(Pages page) ui->startScreen->setCurrentIndex(page); ui->warnings->updateWarnings(page); adjustDrops(); - updateSelector(); } void MainWindow::showEvent(QShowEvent * /*event*/) @@ -535,7 +497,7 @@ void MainWindow::sign() { if(CheckConnection check; !check.check()) { - FadeInNotification::error(ui->topBar, check.errorString()); + FadeInNotification::error(ui->content, check.errorString()); return; } @@ -571,7 +533,7 @@ void MainWindow::sign() return; ui->signContainerPage->transition(digiDoc.get()); - FadeInNotification::success(ui->topBar, tr("The container has been successfully signed!")); + FadeInNotification::success(ui->content, tr("The container has been successfully signed!")); } bool MainWindow::wrap(const QString& wrappedFile, bool pdf) @@ -597,37 +559,8 @@ bool MainWindow::wrap(const QString& wrappedFile, bool pdf) return true; } -void MainWindow::updateMyEID(const TokenData &t) -{ - updateSelector(); - ui->myEid->invalidIcon(false); - ui->myEid->warningIcon(false); - ui->warnings->closeWarnings(MyEid); - SslCertificate cert(t.cert()); - auto type = cert.type(); - ui->infoStack->setHidden(type == SslCertificate::UnknownType); - ui->noReaderInfo->setVisible(type == SslCertificate::UnknownType); - - auto setText = [this](const char *text) { - ui->noReaderInfoText->setProperty("currenttext", text); - ui->noReaderInfoText->setText(tr(text)); - }; - if(!t.isNull()) - { - setText(QT_TR_NOOP("The card in the card reader is not an Estonian ID-card")); - if(type & SslCertificate::TempelType) - ui->infoStack->update(cert); - } - else - { - ui->infoStack->clearData(); - setText(QT_TR_NOOP("Connect the card reader to your computer and insert your ID card into the reader")); - } -} - void MainWindow::updateMyEid(const QSmartCardData &data) { - ui->infoStack->update(data); ui->myEid->warningIcon(false); ui->myEid->invalidIcon(false); ui->warnings->closeWarnings(MyEid); @@ -653,19 +586,19 @@ void MainWindow::updateMyEid(const QSmartCardData &data) ui->warnings->showWarning({isPUKReplacable ? Pin1PukBlockedResetWarning : Pin1PukBlockedWarning}); else if(pin1Blocked) ui->warnings->showWarning({UnblockPin1Warning, 0, - [this]{ changePinClicked(QSmartCardData::Pin1Type, QSmartCard::UnblockWithPuk); }}); + [this]{ ui->eidPage->changePin(QSmartCardData::Pin1Type, QSmartCard::UnblockWithPuk); }}); if(pin2Locked && pin2Blocked) ui->warnings->showWarning({ActivatePin2WithPUKWarning, 0, - [this]{ changePinClicked(QSmartCardData::Pin2Type, QSmartCard::ActivateWithPuk); }}); + [this]{ ui->eidPage->changePin(QSmartCardData::Pin2Type, QSmartCard::ActivateWithPuk); }}); else if(pin2Blocked && pukBlocked) ui->warnings->showWarning({isPUKReplacable ? Pin2PukBlockedResetWarning : Pin2PukBlockedWarning}); else if(pin2Blocked) ui->warnings->showWarning({UnblockPin2Warning, 0, - [this]{ changePinClicked(QSmartCardData::Pin2Type, QSmartCard::UnblockWithPuk); }}); + [this]{ ui->eidPage->changePin(QSmartCardData::Pin2Type, QSmartCard::UnblockWithPuk); }}); else if(pin2Locked) ui->warnings->showWarning({ActivatePin2Warning, 0, - [this]{ changePinClicked(QSmartCardData::Pin2Type, QSmartCard::ActivateWithPin); }}); + [this]{ ui->eidPage->changePin(QSmartCardData::Pin2Type, QSmartCard::ActivateWithPin); }}); } const qint64 DAY = 24 * 60 * 60; @@ -686,74 +619,3 @@ void MainWindow::updateMyEid(const QSmartCardData &data) ui->warnings->showWarning({CertExpiryWarning}); } } - -void MainWindow::updateSelector() -{ - TokenData selected; - enum Filter: uint8_t { - Signing, - Decrypting, - MyEID, - } filter = Signing; - switch(ui->startScreen->currentIndex()) - { - case SignIntro: - case SignDetails: - selected = qApp->cryptoManager()->tokensign(); - filter = Signing; - break; - case CryptoIntro: - case CryptoDetails: - selected = qApp->cryptoManager()->tokenauth(); - filter = Decrypting; - break; - case MyEid: - default: - selected = qApp->cryptoManager()->smartcard()->tokenData(); - filter = MyEID; - break; - } - QVector list; - for(const TokenData &token: qApp->cryptoManager()->cache()) - { - if(token.card() == selected.card()) - continue; - if(std::any_of(list.cbegin(), list.cend(), [token](const TokenData &item) { return token.card() == item.card(); })) - continue; - SslCertificate cert(token.cert()); - if(filter == Signing && !cert.keyUsage().contains(SslCertificate::NonRepudiation)) - continue; - if(filter == Decrypting && cert.keyUsage().contains(SslCertificate::NonRepudiation)) - continue; - if(filter == MyEID && - !(cert.type() & SslCertificate::EstEidType || cert.type() & SslCertificate::DigiIDType || cert.type() & SslCertificate::TempelType)) - continue; - list.append(token); - } - ui->noCardInfo->setVisible(selected.isNull()); - ui->selector->setHidden(list.isEmpty()); - ui->selector->setChecked(false); - ui->cardInfo->setVisible(ui->noCardInfo->isHidden()); - ui->cardInfo->setCursor(ui->selector->isVisible() ? Qt::PointingHandCursor : Qt::ArrowCursor); - ui->cardInfo->update(selected, list.size() > 1); - if (!QPCSC::instance().serviceRunning()) - ui->noCardInfo->update(NoCardInfo::NoPCSC); - else if(QPCSC::instance().readers().isEmpty()) - ui->noCardInfo->update(NoCardInfo::NoReader); - else - ui->noCardInfo->update(NoCardInfo::NoCard); - disconnect(ui->selector, &QToolButton::toggled, this, nullptr); - if(list.isEmpty()) - return; - connect(ui->selector, &QToolButton::toggled, this, [this, list](bool show) { - if(show) - { - auto *cardPopup = new CardPopup(list, this); - connect(cardPopup, &CardPopup::activated, qApp->cryptoManager(), &QCryptoManager::selectCard); - connect(cardPopup, &CardPopup::activated, this, [this] { ui->selector->setChecked(false); }); - cardPopup->show(); - } - else if(auto *cardPopup = findChild()) - cardPopup->deleteLater(); - }); -} diff --git a/client/MainWindow.h b/client/MainWindow.h index b8c9e296d..5e900a7c5 100644 --- a/client/MainWindow.h +++ b/client/MainWindow.h @@ -29,7 +29,6 @@ class MainWindow; class CryptoDoc; class DigiDoc; -class TokenData; class MainWindow final : public QWidget { @@ -56,12 +55,10 @@ class MainWindow final : public QWidget void dragEnterEvent( QDragEnterEvent *event ) final; void dragLeaveEvent( QDragLeaveEvent *event ) final; void dropEvent( QDropEvent *event ) final; - void mouseReleaseEvent(QMouseEvent *event) final; void showEvent(QShowEvent *event) final; private: void adjustDrops(); - void changePinClicked(QSmartCardData::PinType type, QSmartCard::PinAction action); void convertToCDoc(); void navigateToPage(Pages page, const QStringList &files = QStringList(), bool create = true); void onCryptoAction(int action); @@ -69,8 +66,6 @@ class MainWindow final : public QWidget void openContainer(const QString &filter); void resetDigiDoc(std::unique_ptr &&doc); void sign(); - void updateSelector(); - void updateMyEID(const TokenData &t); void updateMyEid(const QSmartCardData &data); bool wrap(const QString& wrappedFile, bool pdf); diff --git a/client/MainWindow.ui b/client/MainWindow.ui index 072855801..f1000daac 100644 --- a/client/MainWindow.ui +++ b/client/MainWindow.ui @@ -23,17 +23,10 @@ QLabel { color: #07142A; } -#logo, #navBar { -background-color: #003168; -border-right: 1px solid #E7EAEF; -} -#topBar, #startScreen, #warnings { +#startScreen, #warnings { background-color: #FFFFFF; } -#warnings { -border-top: 1px solid #E7EAEF; -} -#signPage, #cryptoPage, #noReaderInfo { +#signPage, #cryptoPage { background-image: url(:/images/bg_lovid.svg); background-repeat: repeat-x; background-position: bottom; @@ -54,13 +47,16 @@ background-color: #2B66A6; #signIntroButton:pressed, #cryptoIntroButton:pressed { background-color: #215081; } -#signIntroLabel, #cryptoIntroLabel, #noReaderInfoText { +#signIntroLabel, #cryptoIntroLabel { font-family: Roboto, Helvetica; font-size: 18px; font-weight: 400; } - + + + 0 + 0 @@ -73,68 +69,7 @@ font-weight: 400; 0 - - 0 - - - - - #label { -color: #FFFFFF; -font-family: Roboto, Helvetica; -font-size: 9px; -font-weight: 700; -} - - - - 6 - - - 8 - - - 15 - - - 8 - - - 15 - - - - - - 28 - 22 - - - - - 28 - 22 - - - - Qt::TabFocus - - - DigiDoc - - - - - - - DigiDoc4 - - - - - - - + @@ -149,7 +84,11 @@ font-weight: 700; - QToolButton { + #navBar { +background-color: #003168; +border-right: 1px solid #E7EAEF; +} +QToolButton { border: none; color: #E7EAEF; padding: 13px 0px 10px 0px; @@ -160,6 +99,12 @@ QToolButton:checked { background-color: #ffffff; color: #003168; } +#logoLabel { +color: #FFFFFF; +font-family: Roboto, Helvetica; +font-size: 9px; +font-weight: 700; +} #version, #version:pressed { border: none; color: #BFD3E8; @@ -195,6 +140,56 @@ color: #BFD3E8; 0 + + + + + 6 + + + 8 + + + 15 + + + 8 + + + 15 + + + + + + 28 + 22 + + + + + 28 + 22 + + + + Qt::TabFocus + + + DigiDoc + + + + + + + DigiDoc4 + + + + + + @@ -404,21 +399,9 @@ color: #BFD3E8; - - - - - 0 - 54 - - - - - 16777215 - 54 - - - + + + 0 @@ -426,268 +409,42 @@ color: #BFD3E8; 0 - 6 - - - 16 - - - 6 - - - - - - - - - 0 - 0 - - - - - 32 - 0 - - - - - 32 - 16777215 - - - - border: none; - - - - :/images/arrow_down.svg - :/images/arrow_down_selected.svg:/images/arrow_down.svg - - - - 24 - 24 - - - - true - - - - - - - - - - Qt::Horizontal - - - - 8 - 20 - - - - - - - - - - - - 0 - 1 - - - - - 2 - - 0 - - 1 - 0 0 - - - - - - - 0 - - - - - 40 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 20 - 159 - - - - - - - - Drag file here for signing ... - - - Qt::AlignCenter - - - - - - - PointingHandCursor - - - Load file from disk for signing or verifying - - - ... or load file from disk - - - - - - - Qt::Vertical - - - - 20 - 260 - - - - - - - - - - - 40 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - 20 - 159 - - - - - - - - Drag file here for encryption ... - - - Qt::AlignCenter + + + + + 2 - - - - - - PointingHandCursor + + 0 - - Load file from disk for encryption or decryption + + 0 - - ... or load file from disk + + 0 - - - - - - Qt::Vertical + + 0 - - - 20 - 260 - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - + + + + + + + 0 + + + 40 @@ -704,7 +461,7 @@ color: #BFD3E8; 0 - + Qt::Vertical @@ -717,36 +474,99 @@ color: #BFD3E8; - - - Qt::TabFocus - + - Connect the card reader to your computer and insert your ID card into the reader + Drag file here for signing ... Qt::AlignCenter - - true + + + + + + PointingHandCursor + + + Load file from disk for signing or verifying - - true + + ... or load file from disk - - + + + Qt::Vertical + + + + 20 + 260 + + + + + + + + + + + 40 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + - 0 - 42 + 20 + 159 + + + + + + Drag file here for encryption ... + + + Qt::AlignCenter + + + + + + + PointingHandCursor + + + Load file from disk for encryption or decryption + + + ... or load file from disk + - + Qt::Vertical @@ -760,9 +580,11 @@ color: #BFD3E8; - - - + + + + + @@ -774,12 +596,6 @@ color: #BFD3E8;
QSvgWidget
1 - - CardWidget - QWidget -
widgets/CardWidget.h
- 1 -
ContainerPage QWidget @@ -791,12 +607,6 @@ color: #BFD3E8; QWidget
widgets/MyEidInfo.h
- - NoCardInfo - QWidget -
widgets/NoCardInfo.h
- 1 -
PageIcon QToolButton diff --git a/client/QCNG.cpp b/client/QCNG.cpp index 819ae47ab..a3d5e5ffa 100644 --- a/client/QCNG.cpp +++ b/client/QCNG.cpp @@ -248,7 +248,7 @@ QList QCNG::tokens() {"3BD518008131FE7D8073C82110F4"}, // SafeNet 5110 FIPS {"3BFF9600008131FE4380318065B0846566FB12017882900085"}, // SafeNet 5110+ FIPS }; - t.setData(u"PSS"_s, usePSS.contains(QPCSCReader(reader, &QPCSC::instance()).atr())); + t.setData(u"PSS"_s, usePSS.contains(QPCSCReader(reader).atr())); } }; diff --git a/client/QCryptoBackend.cpp b/client/QCryptoBackend.cpp index fd6a033d7..60ceb2293 100644 --- a/client/QCryptoBackend.cpp +++ b/client/QCryptoBackend.cpp @@ -25,17 +25,14 @@ #endif #include "QPCSC.h" #include "QPKCS11.h" -#include "QSmartCard.h" #include "SslCertificate.h" #include -#include #include -#include +#include +#include #include -static Q_LOGGING_CATEGORY(CryptoLog, "qdigidoc4.QCryptoManager") - // TODO: Port everything to the new OpenSSL API #define OPENSSL_SUPPRESS_DEPRECATED @@ -43,15 +40,15 @@ static Q_LOGGING_CATEGORY(CryptoLog, "qdigidoc4.QCryptoManager") #include #include +#include +#include + struct QCryptoManager::Private { - QSmartCard smartcard; - TokenData auth, sign; QList cache; QReadWriteLock operationLock; QReadWriteLock lock; - QMutex sleepMutex; - QWaitCondition sleepCond; + std::atomic nextRequest {0}; RSA_METHOD *rsa_method = RSA_meth_dup(RSA_get_default_method()); EC_KEY_METHOD *ec_method = EC_KEY_METHOD_new(EC_KEY_get_default_method()); @@ -60,9 +57,8 @@ struct QCryptoManager::Private QCryptoBackend::~QCryptoBackend() { auto *manager = qApp->cryptoManager(); - manager->smartcard()->reloadCard(token, true); manager->d->operationLock.unlock(); - manager->d->sleepCond.wakeAll(); + manager->refreshToken(token); } std::expected @@ -185,10 +181,9 @@ QCryptoManager::QCryptoManager() EC_KEY_METHOD_get_sign(d->ec_method, &sign, &sign_setup, nullptr); EC_KEY_METHOD_set_sign(d->ec_method, sign, sign_setup, ecdsa_do_sign); - connect(&QPCSC::instance(), &QPCSC::cardChanged, this, [this] { - qCDebug(CryptoLog) << "Card change detected"; - d->sleepCond.wakeAll(); - }); + // Token enumeration runs on the manager thread. + moveToThread(this); + connect(&QPCSC::instance(), &QPCSC::cardChanged, this, &QCryptoManager::refresh); start(); QPCSC::instance().start(); } @@ -196,7 +191,7 @@ QCryptoManager::QCryptoManager() QCryptoManager::~QCryptoManager() { requestInterruption(); - d->sleepCond.wakeAll(); + quit(); wait(); EC_KEY_METHOD_free(d->ec_method); RSA_meth_free(d->rsa_method); @@ -205,50 +200,46 @@ QCryptoManager::~QCryptoManager() void QCryptoManager::run() { - while(!isInterruptionRequested()) - { - refresh(); - QMutexLocker locker(&d->sleepMutex); - if(isInterruptionRequested()) - break; - d->sleepCond.wait(&d->sleepMutex, 5000); - } + QTimer timer; + connect(&timer, &QTimer::timeout, this, &QCryptoManager::refresh); + timer.start(5000); + refresh(); + exec(); } QList QCryptoManager::cache() const { QReadLocker locker(&d->lock); return d->cache; } -QSmartCard *QCryptoManager::smartcard() const { return &d->smartcard; } -TokenData QCryptoManager::tokenauth() const { QReadLocker locker(&d->lock); return d->auth; } -TokenData QCryptoManager::tokensign() const { QReadLocker locker(&d->lock); return d->sign; } -void QCryptoManager::selectCard(const TokenData &token) +quint64 QCryptoManager::readCard(const TokenData &token, const QSmartCardData &previous) { - { - bool isSign = SslCertificate(token.cert()).keyUsage().contains(SslCertificate::NonRepudiation); - QWriteLocker locker(&d->lock); - if(isSign) - d->sign = token; - else - d->auth = token; - for(const TokenData &t: d->cache) - { - if(t == token || - t.card() != token.card() || - isSign == SslCertificate(t.cert()).keyUsage().contains(SslCertificate::NonRepudiation)) - continue; - if(isSign) - d->auth = t; - else - d->sign = t; - break; - } - } - d->smartcard.reloadCard(token, false); + const quint64 request = ++d->nextRequest; + QMetaObject::invokeMethod(this, [this, token, previous, request] { + QSmartCardData data; + if(isInterruptionRequested()) + return; + if(cache().contains(token)) + data = previous.isNull() ? QSmartCard::readCard(token) : QSmartCard::readCounters(previous); + Q_EMIT cardDataReady(request, data); + }, Qt::QueuedConnection); + return request; +} + +void QCryptoManager::refreshToken(const TokenData &token) +{ + QMetaObject::invokeMethod(this, [this, token] { + refresh(); + if(!token.isNull()) + Q_EMIT tokenChanged(token); + }, Qt::QueuedConnection); } void QCryptoManager::refresh() { if(!d->operationLock.tryLockForRead()) return; + auto unlock = qScopeGuard([this] { + d->operationLock.unlock(); + Q_EMIT refreshed(); + }); #ifdef Q_OS_WIN QList newCache = QCNG::tokens(); @@ -256,56 +247,18 @@ void QCryptoManager::refresh() QList newCache = QPKCS11::tokens(); #endif - QList acards, scards; - for(const TokenData &t: newCache) - { - SslCertificate c(t.cert()); - if(c.keyUsage().contains(SslCertificate::KeyEncipherment) || - c.keyUsage().contains(SslCertificate::KeyAgreement)) - acards.append(t); - if(c.keyUsage().contains(SslCertificate::NonRepudiation)) - scards.append(t); - } - bool cacheChangedFlag = false; - TokenData aold, sold, anew, snew; { QWriteLocker locker(&d->lock); - if(newCache != d->cache) + if(!std::ranges::equal(newCache, d->cache, [](const TokenData &a, const TokenData &b) { + return a == b && a.data(QStringLiteral("blocked")) == b.data(QStringLiteral("blocked")); + })) { - d->cache = std::move(newCache); + d->cache = newCache; cacheChangedFlag = true; } - - aold = d->auth; - sold = d->sign; - - if(!d->auth.isNull() && !acards.contains(d->auth)) - { - qCDebug(CryptoLog) << "Disconnected from auth card" << d->auth.card(); - d->auth.clear(); - } - if(!d->sign.isNull() && !scards.contains(d->sign)) - { - qCDebug(CryptoLog) << "Disconnected from sign card" << d->sign.card(); - d->sign.clear(); - } - - if(d->sign.isNull() && !scards.isEmpty()) - d->sign = scards.first(); - if(d->auth.isNull() && !acards.isEmpty()) - d->auth = acards.first(); - - anew = d->auth; - snew = d->sign; } if(cacheChangedFlag) - Q_EMIT cacheChanged(); - if(sold != snew) - d->smartcard.reloadCard(snew, false); - else if(aold != anew) - d->smartcard.reloadCard(anew, false); - - d->operationLock.unlock(); + Q_EMIT cacheChanged(newCache); } diff --git a/client/QCryptoBackend.h b/client/QCryptoBackend.h index b99d45079..dc1c337b8 100644 --- a/client/QCryptoBackend.h +++ b/client/QCryptoBackend.h @@ -20,6 +20,7 @@ #pragma once #include "TokenData.h" +#include "QSmartCard.h" #include #include @@ -27,7 +28,6 @@ #include -class QSmartCard; class QSslKey; class QCryptoBackend @@ -91,13 +91,14 @@ class QCryptoManager final : public QThread ~QCryptoManager() final; QList cache() const; - QSmartCard *smartcard() const; - void selectCard(const TokenData &token); - TokenData tokenauth() const; - TokenData tokensign() const; + quint64 readCard(const TokenData &token, const QSmartCardData &previous); + void refreshToken(const TokenData &token); Q_SIGNALS: - void cacheChanged(); + void cacheChanged(const QList &tokens); + void tokenChanged(const TokenData &token); + void cardDataReady(quint64 request, const QSmartCardData &data); + void refreshed(); private: friend class QCryptoBackend; diff --git a/client/QPCSC.cpp b/client/QPCSC.cpp index 42aa83445..13523a4c4 100644 --- a/client/QPCSC.cpp +++ b/client/QPCSC.cpp @@ -202,14 +202,13 @@ bool QPCSC::serviceRunning() const -QPCSCReader::QPCSCReader( const QString &reader, QPCSC *parent ) +QPCSCReader::QPCSCReader(const QString &reader) : d(new Private) { - d->d = parent->d; d->reader = reader.toUtf8(); d->state.szReader = d->reader.constData(); - if(parent->d->context) - SC(GetStatusChange, d->d->context, 0, &d->state, 1U); + if(SC(EstablishContext, DWORD(SCARD_SCOPE_USER), nullptr, nullptr, &d->context) == SCARD_S_SUCCESS) + SC(GetStatusChange, d->context, 0, &d->state, 1U); } QPCSCReader::QPCSCReader(QPCSCReader &&other) noexcept @@ -224,6 +223,8 @@ QPCSCReader::~QPCSCReader() noexcept SC(EndTransaction, d->card, SCARD_LEAVE_CARD); if(d->card) SC(Disconnect, d->card, SCARD_LEAVE_CARD); + if(d->context) + SC(ReleaseContext, d->context); delete d; } @@ -234,10 +235,18 @@ QByteArray QPCSCReader::atr() const bool QPCSCReader::connect(Connect connect, Mode mode) { - if(SC(Connect, d->d->context, d->state.szReader, connect, mode, &d->card, &d->io.dwProtocol) != SCARD_S_SUCCESS) + if(SC(Connect, d->context, d->state.szReader, connect, mode, &d->card, &d->io.dwProtocol) != SCARD_S_SUCCESS) return false; + // Diagnostics opens the reader directly even when no card is present. + if(connect == Direct) + return true; d->isTransacted = SC(BeginTransaction, d->card) == SCARD_S_SUCCESS; - return true; + if(!d->isTransacted) + { + SC(Disconnect, d->card, SCARD_LEAVE_CARD); + d->card = {}; + } + return d->isTransacted; } bool QPCSCReader::isPinPad() const diff --git a/client/QPCSC.h b/client/QPCSC.h index aa7397bbf..5fa911202 100644 --- a/client/QPCSC.h +++ b/client/QPCSC.h @@ -95,7 +95,7 @@ class QPCSCReader final: public QObject T1 = 2 }; - explicit QPCSCReader( const QString &reader, QPCSC *parent ); + explicit QPCSCReader(const QString &reader); QPCSCReader(QPCSCReader &&) noexcept; ~QPCSCReader() noexcept final; diff --git a/client/QPCSC_p.h b/client/QPCSC_p.h index 883eda338..80629e414 100644 --- a/client/QPCSC_p.h +++ b/client/QPCSC_p.h @@ -190,7 +190,7 @@ struct QPCSCReader::Private { quint32 feature(DRIVER_FEATURES key); - QPCSC::Private *d {}; + SCARDCONTEXT context {}; SCARDHANDLE card {}; SCARD_IO_REQUEST io {SCARD_PROTOCOL_UNDEFINED, sizeof(SCARD_IO_REQUEST)}; SCARD_READERSTATE state {}; diff --git a/client/QPKCS11.cpp b/client/QPKCS11.cpp index 8f8248515..0832ae90c 100644 --- a/client/QPKCS11.cpp +++ b/client/QPKCS11.cpp @@ -164,7 +164,7 @@ std::shared_ptr QPKCS11Library::current() }; for(const QString &reader: QPCSC::instance().readers()) { - QPCSCReader r(reader, &QPCSC::instance()); + QPCSCReader r(reader); if(!r.isPresent()) continue; QByteArray atr = r.atr(); diff --git a/client/QSmartCard.cpp b/client/QSmartCard.cpp index 46c9b5f8f..41059a0bf 100644 --- a/client/QSmartCard.cpp +++ b/client/QSmartCard.cpp @@ -19,7 +19,9 @@ #include "QSmartCard_p.h" +#include "Application.h" #include "IKValidator.h" +#include "QCryptoBackend.h" #include "Settings.h" #include "Utils.h" #include "dialogs/PinPopup.h" @@ -50,7 +52,7 @@ bool QSmartCardData::operator ==(const QSmartCardData &other) const QString QSmartCardData::card() const { return d->card; } bool QSmartCardData::isNull() const -{ return d->data.isEmpty() && d->authCert.isNull() && d->signCert.isNull(); } +{ return d->data.isEmpty() && d->authCert.isNull() && d->signCert.isNull() && d->retry.isEmpty(); } bool QSmartCardData::isPinpad() const { return d->pinpad; } bool QSmartCardData::isPUKReplacable() const { return d->pukReplacable; } bool QSmartCardData::isValid() const @@ -94,7 +96,7 @@ const QByteArray Card::VERIFY = APDU("00200000 00"); std::unique_ptr Card::card(const QString &reader) { - QPCSCReader r(reader, &QPCSC::instance()); + QPCSCReader r(reader); auto atr = r.atr(); if(IDEMIACard::isSupported(atr)) return std::make_unique(std::move(r)); @@ -491,17 +493,12 @@ bool THALESCard::updateCounters(QSmartCardDataPrivate *d) const -QSmartCard::QSmartCard(QObject *parent) - : QObject(parent) - , d(std::make_unique()) -{} - -QSmartCard::~QSmartCard() noexcept = default; - -QSmartCardData QSmartCard::data() const { return d->t; } - -bool QSmartCard::pinChange(QSmartCardData::PinType type, QSmartCard::PinAction action, QWidget* parent) +bool QSmartCard::pinChange(TokenData token, QSmartCardData current, QSmartCardData::PinType type, + QSmartCard::PinAction action, QWidget* parent) { + if(current.isNull()) + return false; + auto notify = qScopeGuard([token] { qApp->cryptoManager()->refreshToken(token); }); std::unique_ptr popup; QByteArray oldPin, newPin; QSmartCardData::PinType src = type; @@ -512,14 +509,14 @@ bool QSmartCard::pinChange(QSmartCardData::PinType type, QSmartCard::PinAction a src = QSmartCardData::PukType; } - auto card = Card::card(d->t.reader()); + auto card = Card::card(current.reader()); if(!card) { FadeInNotification::warning(parent, tr("Changing %1 failed").arg(QSmartCardData::typeString(type))); return false; } - if(d->t.isPinpad()) + if(current.isPinpad()) { QString bodyText; switch(action) @@ -537,22 +534,22 @@ bool QSmartCard::pinChange(QSmartCardData::PinType type, QSmartCard::PinAction a } PinPopup::TokenFlags flags = PinPopup::PinpadChangeFlag; - switch(d->t.retryCount(src)) + switch(current.retryCount(src)) { case 2: flags |= PinPopup::PinCountLow; break; case 1: flags |= PinPopup::PinFinalTry; break; case 0: flags |= PinPopup::PinLocked; break; default: break; } - popup.reset(new PinPopup(src, flags, d->t.authCert(), parent, bodyText)); + popup.reset(new PinPopup(src, flags, current.authCert(), parent, bodyText)); popup->open(); oldPin = card->pinTemplate({}); newPin = card->pinTemplate({}); } else { - PinUnblock p(type, action, d->t.retryCount(src), d->t.data(QSmartCardData::BirthDate).toDate(), - d->t.data(QSmartCardData::Id).toString(), d->t.isPUKReplacable(), parent); + PinUnblock p(type, action, current.retryCount(src), current.data(QSmartCardData::BirthDate).toDate(), + current.data(QSmartCardData::Id).toString(), current.isPUKReplacable(), parent); if (!p.exec()) return false; QString oldPinString = p.firstCodeText(); @@ -583,7 +580,7 @@ bool QSmartCard::pinChange(QSmartCardData::PinType type, QSmartCard::PinAction a switch(response.SW) { case 0x9000: - card->updateCounters(d->t.d); + card->updateCounters(current.d); switch(action) { using enum QSmartCard::PinAction; @@ -594,7 +591,7 @@ bool QSmartCard::pinChange(QSmartCardData::PinType type, QSmartCard::PinAction a case ChangeWithPin: case ChangeWithPuk: FadeInNotification::success(parent, tr("%1 changed!").arg(QSmartCardData::typeString(type))); - return false; // Do not update ui, there is no warning in MainWindow when PIN usage count is low + return false; // My eID refreshes counters after the session closes. case UnblockWithPuk: FadeInNotification::success(parent, tr("%1 has been changed and the certificate has been unblocked!") .arg(QSmartCardData::typeString(type))); @@ -604,15 +601,15 @@ bool QSmartCard::pinChange(QSmartCardData::PinType type, QSmartCard::PinAction a case 0x63C0: //pin retry count 0 case 0x6983: case 0x6984: - card->updateCounters(d->t.d); + card->updateCounters(current.d); FadeInNotification::warning(parent, tr("%1 blocked").arg(QSmartCardData::typeString(src))); return true; case 0x63C1: // Validate error, 1 tries left case 0x63C2: // Validate error, 2 tries left case 0x63C3: - card->updateCounters(d->t.d); + card->updateCounters(current.d); FadeInNotification::warning(parent, tr("Wrong %1 code. You can try %n more time(s).", nullptr, - d->t.retryCount(src)).arg(QSmartCardData::typeString(src))); + current.retryCount(src)).arg(QSmartCardData::typeString(src))); return false; case 0x6400: FadeInNotification::warning(parent, tr("%1 timeout").arg(QSmartCardData::typeString(type))); @@ -636,30 +633,12 @@ bool QSmartCard::pinChange(QSmartCardData::PinType type, QSmartCard::PinAction a } } -void QSmartCard::reloadCard(const TokenData &token, bool reloadCounters) +QSmartCardData QSmartCard::readCard(const TokenData &token, bool countersOnly) { - qCDebug(CLog) << "Polling"; - d->token = token; - Q_EMIT tokenChanged(token); - if(d->token.isNull() || d->token.card().isEmpty() || d->token.reader().isEmpty()) - { - qCDebug(CLog) << "Is null or no reader"; - d->t.d = new QSmartCardDataPrivate(); - return; - } - - if(d->token.card() != d->t.card()) - { - qCDebug(CLog) << "Different token"; - d->t.d = new QSmartCardDataPrivate(); - } - else if(reloadCounters) - qCDebug(CLog) << "Reload counter"; - else - return; - - QString reader = d->token.reader(); - if(d->token.reader().endsWith(QLatin1String("..."))) { + if(token.isNull() || token.card().isEmpty() || token.reader().isEmpty()) + return {}; + QString reader = token.reader(); + if(reader.endsWith(QLatin1String("..."))) { for(auto truncated = QStringView(reader).left(reader.size() - 3); const QString &test: QPCSC::instance().readers()) { if(test.startsWith(truncated)) @@ -670,26 +649,28 @@ void QSmartCard::reloadCard(const TokenData &token, bool reloadCounters) qCDebug(CLog) << "Read" << reader; auto card = Card::card(reader); if(!card || !card->reader.connect()) - return; - qCDebug(CLog) << "Read card" << d->token.card() << "info"; - QSharedDataPointer t = d->t.d; - t->card = d->token.card(); - t->reader = card->reader.name(); - t->pinpad = card->reader.isPinPad(); - t->data.clear(); - t->authCert.clear(); - t->signCert.clear(); - t->retry.clear(); - t->locked.clear(); - - if(card->loadPerso(t)) - { - d->t.d = std::move(t); - emit dataChanged(d->t); - } - else - qCDebug(CLog) << "Failed to read card info, try again next round"; + return {}; + qCDebug(CLog) << "Read card" << token.card() << "info"; + QSmartCardData data; + data.d->card = token.card(); + data.d->reader = card->reader.name(); + data.d->pinpad = card->reader.isPinPad(); + if(countersOnly ? card->updateCounters(data.d) && data.d->retry.size() == 3 : card->loadPerso(data.d)) + return data; + qCDebug(CLog) << (countersOnly ? "Failed to read card counters" : "Failed to read card info"); + return {}; } -TokenData QSmartCard::tokenData() const { return d->token; } - +QSmartCardData QSmartCard::readCounters(QSmartCardData data) +{ + const QSmartCardData previous = data; + auto card = Card::card(data.reader()); + if(!card || !card->reader.connect()) + return previous; + data.d->retry.clear(); + data.d->locked.clear(); + if(card->updateCounters(data.d) && data.d->retry.size() == 3) + return data; + qCDebug(CLog) << "Failed to read card counters"; + return previous; +} diff --git a/client/QSmartCard.h b/client/QSmartCard.h index 390a7a099..01f788b6f 100644 --- a/client/QSmartCard.h +++ b/client/QSmartCard.h @@ -19,7 +19,7 @@ #pragma once -#include +#include #include #include @@ -28,6 +28,7 @@ class SslCertificate; class QSmartCardDataPrivate; class QSslKey; class TokenData; +class QWidget; class QSmartCardData { @@ -81,9 +82,9 @@ class QSmartCardData friend class QSmartCardPrivate; }; -class QSmartCard final: public QObject +class QSmartCard final { - Q_OBJECT + Q_DECLARE_TR_FUNCTIONS(QSmartCard) public: enum PinAction : quint8 { @@ -94,22 +95,11 @@ class QSmartCard final: public QObject UnblockWithPuk, }; - explicit QSmartCard(QObject *parent = nullptr); - ~QSmartCard() noexcept final; - - QSmartCardData data() const; - TokenData tokenData() const; - void reloadCard(const TokenData &token, bool reloadCounters); - - bool pinChange(QSmartCardData::PinType type, PinAction action = ChangeWithPin, QWidget* parent = nullptr); - -Q_SIGNALS: - void tokenChanged(const TokenData &data); - void dataChanged(const QSmartCardData &data); + static bool pinChange(TokenData token, QSmartCardData current, QSmartCardData::PinType type, + PinAction action = ChangeWithPin, QWidget* parent = nullptr); + static QSmartCardData readCard(const TokenData &token, bool countersOnly = false); + static QSmartCardData readCounters(QSmartCardData data); private: - Q_DISABLE_COPY_MOVE(QSmartCard) - - struct Private; - std::unique_ptr d; + QSmartCard() = delete; }; diff --git a/client/QSmartCard_p.h b/client/QSmartCard_p.h index af62b11d8..3cd107200 100644 --- a/client/QSmartCard_p.h +++ b/client/QSmartCard_p.h @@ -80,12 +80,6 @@ struct THALESCard: public Card static const QByteArray AID; }; -struct QSmartCard::Private -{ - TokenData token; - QSmartCardData t; -}; - class QSmartCardDataPrivate: public QSharedData { public: diff --git a/client/dialogs/AddRecipients.cpp b/client/dialogs/AddRecipients.cpp index 6cb1cd340..a541befdf 100644 --- a/client/dialogs/AddRecipients.cpp +++ b/client/dialogs/AddRecipients.cpp @@ -152,12 +152,11 @@ AddRecipients::AddRecipients(ItemList* itemList, QWidget *parent) addRecipient(token.cert()); } }); - auto enableRecipientFromCard = [this] { - const auto cache = qApp->cryptoManager()->cache(); + auto enableRecipientFromCard = [this](const QList &cache) { ui->fromCard->setDisabled(std::none_of(cache.cbegin(), cache.cend(), [](const TokenData &token) { return SslCertificate(token.cert()).isEncryptionCert(); })); }; - enableRecipientFromCard(); + enableRecipientFromCard(qApp->cryptoManager()->cache()); connect(qApp->cryptoManager(), &QCryptoManager::cacheChanged, this, std::move(enableRecipientFromCard)); connect(ui->fromFile, &QPushButton::clicked, this, &AddRecipients::addRecipientFromFile); diff --git a/client/dialogs/SigningDialog.cpp b/client/dialogs/SigningDialog.cpp index 74c42c766..2bc2816ea 100644 --- a/client/dialogs/SigningDialog.cpp +++ b/client/dialogs/SigningDialog.cpp @@ -18,7 +18,6 @@ #include "signer/MobileProgress.h" #include "signer/SmartIDProgress.h" #include "widgets/CardListItem.h" -#include "widgets/NoCardInfo.h" #include #include @@ -51,9 +50,9 @@ SigningDialog::SigningDialog(DigiDoc *doc, QWidget *parent) connect(ui->tabs, &QTabBar::currentChanged, ui->tabStack, &QStackedWidget::setCurrentIndex); // ID-card tab - updateCards(); + ui->noCardIcon->load(QStringLiteral(":/images/icon_IDkaart_red.svg")); + updateCards(qApp->cryptoManager()->cache()); connect(qApp->cryptoManager(), &QCryptoManager::cacheChanged, this, &SigningDialog::updateCards); - connect(qApp->cryptoManager()->smartcard(), &QSmartCard::dataChanged, this, &SigningDialog::updateCards); connect(ui->pin, &QLineEdit::textEdited, this, [this] { setError(ui->pin, ui->errorPin, {}); ui->sign->setEnabled(ui->pin->hasAcceptableInput()); @@ -61,7 +60,7 @@ SigningDialog::SigningDialog(DigiDoc *doc, QWidget *parent) connect(ui->pin, &QLineEdit::returnPressed, ui->sign, &QPushButton::click); connect(ui->tabs, &QTabBar::currentChanged, this, [this](int index) { if(index == IDCard) - updateCards(); + updateSelection(); else ui->sign->setEnabled(true); }); @@ -172,13 +171,35 @@ void SigningDialog::changeEvent(QEvent *event) if(event->type() == QEvent::LanguageChange) { ui->retranslateUi(this); + updateNoCard(); updateTabs(); - updateSelection(std::ranges::any_of(ui->cardListContainer->findChildren(), - &CardListItem::isChecked)); + updatePinControls(); } QDialog::changeEvent(event); } +void SigningDialog::updateNoCard() +{ + if(!QPCSC::instance().serviceRunning()) + ui->noCardStatus->setText(tr("The PCSC service, required for using the ID-card, is not working. Check your computer settings.")); + else if(QPCSC::instance().readers().isEmpty()) + ui->noCardStatus->setText(tr("No readers found")); + else + ui->noCardStatus->setText(tr("No card in reader")); + ui->noCard->setAccessibleDescription(ui->noCardStatus->text()); +} + +void SigningDialog::reject() +{ + if(ui->progressPage->isVisible()) + { + if(ui->progressCancel->isVisible() && ui->progressCancel->isEnabled()) + ui->progressCancel->click(); + return; + } + QDialog::reject(); +} + void SigningDialog::updateTabs() { ui->tabs->setTabText(IDCard, tr("ID-card")); @@ -186,59 +207,28 @@ void SigningDialog::updateTabs() ui->tabs->setTabText(SmartID, tr("Smart-ID")); } -void SigningDialog::updateCards() +void SigningDialog::updateCards(QList tokens) { qDeleteAll(ui->cardListContainer->findChildren()); - auto select = [this](const TokenData &t) { - if(!(t == m_selectedToken)) { - ui->pin->clear(); - setError(ui->pin, ui->errorPin, {}); - } - m_selectedToken = t; - }; - - QList tokens = qApp->cryptoManager()->cache(); - bool localValid = std::any_of(tokens.cbegin(), tokens.cend(), - [this](const TokenData &t) { return t == m_selectedToken; }); - if(!localValid) - select(qApp->cryptoManager()->tokensign()); - - const QSmartCardData data = qApp->cryptoManager()->smartcard()->data(); bool any = false; - bool selectedUsable = false; CardListItem *selectedCard = nullptr; - for(TokenData &token : tokens) { + for(const TokenData &token : tokens) { SslCertificate c(token.cert()); if(!c.keyUsage().contains(SslCertificate::NonRepudiation)) continue; - bool blocked = token.data(QStringLiteral("blocked")).toBool() || - (!data.isNull() && data.card() == token.card() && - (data.retryCount(QSmartCardData::Pin2Type) == 0 || data.pinLocked(QSmartCardData::Pin2Type))); - token.setData(QStringLiteral("blocked"), blocked); auto *w = new CardListItem(ui->cardListContainer); - w->update(token, token.card() == m_selectedToken.card()); - connect(w, &CardListItem::selected, this, [this, select](const TokenData &t) { - select(t); - updateSelection(true); - }); + w->update(token, token == m_selectedToken); + connect(w, &CardListItem::selected, this, &SigningDialog::updateSelection); ui->cardListContainer->layout()->addWidget(w); w->show(); any = true; - if(token == m_selectedToken) { - selectedUsable = c.isValid() && !blocked; + if(token == m_selectedToken) selectedCard = w; - } } - if(!any) { - if(!QPCSC::instance().serviceRunning()) - ui->noCard->update(NoCardInfo::NoPCSC); - else if(QPCSC::instance().readers().isEmpty()) - ui->noCard->update(NoCardInfo::NoReader); - else - ui->noCard->update(NoCardInfo::NoCard); - } + if(!any) + updateNoCard(); ui->noCard->setVisible(!any); ui->cardList->setVisible(any); if(selectedCard) @@ -246,11 +236,30 @@ void SigningDialog::updateCards() if(card) list->ensureWidgetVisible(card, 0, 0); }, Qt::QueuedConnection); - updateSelection(selectedUsable); + updateSelection(); +} + +void SigningDialog::updateSelection() +{ + for(auto *w: ui->cardListContainer->findChildren()) { + if(!w->isChecked()) + continue; + if(!(w->token() == m_selectedToken)) { + ui->pin->clear(); + setError(ui->pin, ui->errorPin, {}); + } + if(currentMethod() == IDCard) + w->refresh(); + m_selectedToken = w->token(); + break; + } + updatePinControls(); } -void SigningDialog::updateSelection(bool usable) +void SigningDialog::updatePinControls() { + bool usable = std::ranges::any_of(ui->cardListContainer->findChildren(), + [](CardListItem *w) { return w->isChecked() && w->isEnabled(); }); bool isSeal = SslCertificate(m_selectedToken.cert()).type() & SslCertificate::TempelType; QString pinName = isSeal ? QStringLiteral("PIN") : QSmartCardData::typeString(QSmartCardData::Pin2Type); bool showPin = false; @@ -266,6 +275,7 @@ void SigningDialog::updateSelection(bool usable) ui->labelPin->setVisible(showPin); ui->pin->setVisible(showPin); ui->errorPin->setVisible(showPin && !ui->errorPin->text().isEmpty()); + ui->layoutPin->invalidate(); if(!showPin) ui->pin->clear(); @@ -294,7 +304,7 @@ void SigningDialog::performSign() switch(val.error()) { case QCryptoBackend::PinIncorrect: { QString msg = QCryptoBackend::errorString(QCryptoBackend::PinIncorrect); - if(QSmartCardData data = qApp->cryptoManager()->smartcard()->data(); data.card() == m_selectedToken.card()) + if(QSmartCardData data = QSmartCard::readCard(m_selectedToken, true); !data.isNull()) msg += QStringLiteral(". ") + tr("Remaining attempts: %1").arg(data.retryCount(QSmartCardData::Pin2Type)); ui->pin->clear(); setError(ui->pin, ui->errorPin, msg); @@ -334,7 +344,7 @@ void SigningDialog::performSign() showPage(ui->progressPage); MobileProgress m(pui); return m.init(code, phone) && - digiDoc->sign(m_city, m_state, m_zip, m_country, m_role, &m) ? accept() : reject(); + digiDoc->sign(m_city, m_state, m_zip, m_country, m_role, &m) ? accept() : QDialog::reject(); } case SmartID: { const QString code = ui->idCodeSID->text(); @@ -349,7 +359,7 @@ void SigningDialog::performSign() showPage(ui->progressPage); SmartIDProgress s(pui); return s.init(ui->idCountry->currentData().toString(), code, digiDoc->fileName()) && - digiDoc->sign(m_city, m_state, m_zip, m_country, m_role, &s) ? accept() : reject(); + digiDoc->sign(m_city, m_state, m_zip, m_country, m_role, &s) ? accept() : QDialog::reject(); } } } diff --git a/client/dialogs/SigningDialog.h b/client/dialogs/SigningDialog.h index 8a0b9df4e..f5238fc82 100644 --- a/client/dialogs/SigningDialog.h +++ b/client/dialogs/SigningDialog.h @@ -23,9 +23,12 @@ class SigningDialog final : public QDialog enum Method { IDCard, MobileID, SmartID }; void changeEvent(QEvent *event) final; - void updateCards(); + void reject() final; + void updateCards(QList tokens); + void updateNoCard(); void updateTabs(); - void updateSelection(bool usable); + void updateSelection(); + void updatePinControls(); void performSign(); Method currentMethod() const; bool confirmAlreadySigned(); diff --git a/client/dialogs/SigningDialog.ui b/client/dialogs/SigningDialog.ui index 6bea42efd..8b6bfa463 100644 --- a/client/dialogs/SigningDialog.ui +++ b/client/dialogs/SigningDialog.ui @@ -341,7 +341,59 @@ background: transparent; - + + + QLabel { +color: #07142A; +font-family: Roboto, Helvetica; +font-size: 14px; +font-weight: 400; +} + + + + 8 + + + 8 + + + 0 + + + 8 + + + 0 + + + + + + 24 + 24 + + + + + 24 + 24 + + + + + + + + Qt::TabFocus + + + No card in reader + + + + + @@ -808,9 +860,9 @@ background: transparent;
widgets/PinLineEdit.h
- NoCardInfo + QSvgWidget QWidget -
widgets/NoCardInfo.h
+
QSvgWidget
1
diff --git a/client/translations/en.ts b/client/translations/en.ts index 07cb0b1d3..fdb48acd9 100644 --- a/client/translations/en.ts +++ b/client/translations/en.ts @@ -1411,10 +1411,6 @@ LDAP server is unavailable. Cannot alter container Cannot alter container - - Connect the card reader to your computer and insert your ID card into the reader - Connect the card reader to your computer and insert your ID-card into the reader - Signing Signing @@ -1444,10 +1440,6 @@ LDAP server is unavailable. accessible Select my EID page - - The card in the card reader is not an Estonian ID-card - The card in the card reader is not an Estonian ID-card - My eID My eID @@ -1698,25 +1690,34 @@ LDAP server is unavailable. Country Country - - - NoCardInfo - No card in reader - No card in card reader; check if ID-card is inserted correctly + No card reader found. Connect a card reader to your computer. + No card reader found. Connect a card reader to your computer. - No readers found - No card readers found + No card in the reader. Insert your ID-card into the reader. + No card in the reader. Insert your ID-card into the reader. - Loading data - Loading data + The card in the reader is not supported. Insert an Estonian ID-card. + The card in the reader is not supported. Insert an Estonian ID-card. The PCSC service, required for using the ID-card, is not working. Check your computer settings. The PCSC service, required for using the ID-card, is not working. Check your computer settings. + + Reading card... + Reading card... + + + Failed to read card data + Failed to read card data + + + Select card + Select card + PasswordDialog @@ -2810,6 +2811,18 @@ Additional licenses and components Failed to sign document Failed to sign document + + No card in reader + No card in card reader; check if ID-card is inserted correctly + + + No readers found + No card readers found + + + The PCSC service, required for using the ID-card, is not working. Check your computer settings. + The PCSC service, required for using the ID-card, is not working. Check your computer settings. + SslCertificate diff --git a/client/translations/et.ts b/client/translations/et.ts index 0754fdb9d..359bb3912 100644 --- a/client/translations/et.ts +++ b/client/translations/et.ts @@ -1411,10 +1411,6 @@ LDAP serveriga ei saa ühendust. Cannot alter container Ümbriku salvestamine ebaõnnestus - - Connect the card reader to your computer and insert your ID card into the reader - Ühenda kaardilugeja arvutiga ja sisesta ID-kaart lugejasse - Signing Allkirjastamine @@ -1444,10 +1440,6 @@ LDAP serveriga ei saa ühendust. accessible Vali leht minu eID - - The card in the card reader is not an Estonian ID-card - Lugejas olev kaart ei ole Eesti ID-kaart - My eID Minu eID @@ -1698,25 +1690,34 @@ LDAP serveriga ei saa ühendust. Country Riik - - - NoCardInfo - No card in reader - Lugejas ei ole kaarti; kontrolli, kas ID-kaart on õiget pidi lugejas + No card reader found. Connect a card reader to your computer. + Kaardilugejat ei leitud. Ühenda kaardilugeja arvutiga. - No readers found - Ühtegi kiipkaardilugejat pole ühendatud + No card in the reader. Insert your ID-card into the reader. + Lugejas ei ole kaarti. Sisesta ID-kaart lugejasse. - Loading data - Loen andmeid + The card in the reader is not supported. Insert an Estonian ID-card. + Lugejas olevat kaarti ei toetata. Sisesta Eesti ID-kaart. The PCSC service, required for using the ID-card, is not working. Check your computer settings. ID-kaardi kasutamiseks vajalik PCSC teenus ei tööta. Kontrolli arvuti seadeid. + + Reading card... + Kaardi lugemine... + + + Failed to read card data + Kaardi andmete lugemine ebaõnnestus + + + Select card + Vali kaart + PasswordDialog @@ -2810,6 +2811,18 @@ Täiendavad litsentsid ja komponendid Failed to sign document Dokumendi allkirjastamine ebaõnnestus + + No card in reader + Lugejas ei ole kaarti; kontrolli, kas ID-kaart on õiget pidi lugejas + + + No readers found + Ühtegi kiipkaardilugejat pole ühendatud + + + The PCSC service, required for using the ID-card, is not working. Check your computer settings. + ID-kaardi kasutamiseks vajalik PCSC teenus ei tööta. Kontrolli arvuti seadeid. + SslCertificate diff --git a/client/widgets/AddressItem.cpp b/client/widgets/AddressItem.cpp index e32cba0f7..e5acf9467 100644 --- a/client/widgets/AddressItem.cpp +++ b/client/widgets/AddressItem.cpp @@ -96,7 +96,7 @@ AddressItem::AddressItem(const CKey &key, Type type, QWidget *parent) setIdType(); ui->add->setVisible(type == Add); ui->remove->setVisible(type != Add); - setDecryptVisible(); + setDecryptVisible(qApp->cryptoManager()->cache()); } AddressItem::~AddressItem() @@ -140,21 +140,21 @@ void AddressItem::mouseReleaseEvent(QMouseEvent * /*event*/) { (new KeyDialog(ui->key, this))->open(); } -TokenData AddressItem::tokenForLock(const libcdoc::Lock &lock) +TokenData AddressItem::tokenForLock(const libcdoc::Lock &lock, const QList &tokens) { if(!lock.isPKI()) return {}; const auto &key = lock.getBytes(libcdoc::Lock::RCPT_KEY); QByteArray rcpt = QByteArray::fromRawData((const char*)key.data(), key.size()); - for(const TokenData &token: qApp->cryptoManager()->cache()) + for(const TokenData &token: tokens) if(SslCertificate(token.cert()).publicKeyDer() == rcpt) return token; return {}; } -void AddressItem::setDecryptVisible() +void AddressItem::setDecryptVisible(const QList &tokens) { - ui->token = tokenForLock(ui->key.lock); + ui->token = tokenForLock(ui->key.lock, tokens); ui->decrypt->setVisible(ui->state == EncryptedContainer && (ui->key.lock.type == libcdoc::Lock::PASSWORD || !ui->token.isNull())); } @@ -172,7 +172,7 @@ void AddressItem::stateChange(ContainerState state) ui->state = state; if(ui->add->isHidden()) ui->remove->setVisible(state == UnencryptedContainer); - setDecryptVisible(); + setDecryptVisible(qApp->cryptoManager()->cache()); } void AddressItem::setIdType(const SslCertificate &cert) { diff --git a/client/widgets/AddressItem.h b/client/widgets/AddressItem.h index e37b28131..e2f08d016 100644 --- a/client/widgets/AddressItem.h +++ b/client/widgets/AddressItem.h @@ -52,12 +52,12 @@ class AddressItem final : public Item private: void changeEvent(QEvent *event) final; void mouseReleaseEvent(QMouseEvent *event) final; - void setDecryptVisible(); + void setDecryptVisible(const QList &tokens); void setName(); void setIdType(); void setIdType(const SslCertificate& cert); - static TokenData tokenForLock(const libcdoc::Lock &lock); + static TokenData tokenForLock(const libcdoc::Lock &lock, const QList &tokens); struct Private; Private *ui; diff --git a/client/widgets/CardListItem.cpp b/client/widgets/CardListItem.cpp index 85da37e93..446a2e03d 100644 --- a/client/widgets/CardListItem.cpp +++ b/client/widgets/CardListItem.cpp @@ -34,6 +34,15 @@ TokenData CardListItem::token() const return t; } +void CardListItem::refresh() +{ + if(QSmartCardData data = QSmartCard::readCard(t, true); !data.isNull()) { + t.setData(QStringLiteral("blocked"), + data.retryCount(QSmartCardData::Pin2Type) == 0 || data.pinLocked(QSmartCardData::Pin2Type)); + update(t, isChecked()); + } +} + void CardListItem::paintEvent(QPaintEvent*) { QStyleOptionButton opt; diff --git a/client/widgets/CardListItem.h b/client/widgets/CardListItem.h index ac85e68fe..f7e1025c8 100644 --- a/client/widgets/CardListItem.h +++ b/client/widgets/CardListItem.h @@ -18,6 +18,7 @@ class CardListItem final: public QAbstractButton ~CardListItem() final; TokenData token() const; + void refresh(); void update(const TokenData &token, bool selected); signals: diff --git a/client/widgets/ContainerPage.ui b/client/widgets/ContainerPage.ui index ac172db3a..1375e6b0a 100644 --- a/client/widgets/ContainerPage.ui +++ b/client/widgets/ContainerPage.ui @@ -76,13 +76,13 @@ border-top: 1px solid #E7EAEF; 914 - 40 + 52 16777215 - 40 + 52 diff --git a/client/widgets/MyEidInfo.cpp b/client/widgets/MyEidInfo.cpp index 45eaea5d8..59f9c2627 100644 --- a/client/widgets/MyEidInfo.cpp +++ b/client/widgets/MyEidInfo.cpp @@ -20,25 +20,59 @@ #include "MyEidInfo.h" #include "ui_MyEidInfo.h" +#include "Application.h" +#include "CardPopup.h" +#include "QCryptoBackend.h" +#include "QPCSC.h" #include "SslCertificate.h" +#include +#include +#include #include +#include +#include + MyEidInfo::MyEidInfo( QWidget *parent ) : QWidget(parent) , ui(new Ui::MyEidInfo) { ui->setupUi( this ); connect(ui->authBox, &VerifyCert::changePinClicked, this, [this](QSmartCard::PinAction action) { - emit changePinClicked(QSmartCardData::Pin1Type, action); + changePin(QSmartCardData::Pin1Type, action); }); connect(ui->signBox, &VerifyCert::changePinClicked, this,[this](QSmartCard::PinAction action) { - emit changePinClicked(QSmartCardData::Pin2Type, action); + changePin(QSmartCardData::Pin2Type, action); }); connect(ui->pukBox, &VerifyCert::changePinClicked, this, [this](QSmartCard::PinAction action) { - emit changePinClicked(QSmartCardData::PukType, action); + changePin(QSmartCardData::PukType, action); + }); + updateSelector(); + updateData(); + connect(ui->cardInfo, &CardWidget::selected, this, &MyEidInfo::toggleCardPopup); + connect(ui->selector, &QToolButton::clicked, this, &MyEidInfo::toggleCardPopup); + auto *manager = qApp->cryptoManager(); + connect(manager, &QCryptoManager::cacheChanged, this, &MyEidInfo::updateCards); + connect(manager, &QCryptoManager::refreshed, this, [this] { + enumerated = true; + if(isVisible()) + updateEmptyState(true); + }); + connect(manager, &QCryptoManager::cardDataReady, this, [this](quint64 id, const QSmartCardData &data) { + if(id != request) + return; + request = 0; + currentData = data; + readFailed = currentData.isNull(); + updateData(); + Q_EMIT dataChanged(currentData); + }); + connect(manager, &QCryptoManager::tokenChanged, this, [this](const TokenData &token) { + if(!selectedToken.isNull() && token.card() == selectedToken.card() && token.reader() == selectedToken.reader()) + refresh(); }); - clearData(); + updateCards(manager->cache()); } MyEidInfo::~MyEidInfo() @@ -46,19 +80,50 @@ MyEidInfo::~MyEidInfo() delete ui; } -void MyEidInfo::clearData() +QSmartCardData MyEidInfo::cardData() const { return currentData; } + +void MyEidInfo::changePin(QSmartCardData::PinType type, QSmartCard::PinAction action) +{ + QSmartCard::pinChange(selectedToken, currentData, type, action, parentWidget()); +} + +void MyEidInfo::updateCards(QList tokens) +{ + tokens.removeIf(std::not_fn([](const TokenData &token) { + return SslCertificate(token.cert()).type() & + (SslCertificate::EstEidType|SslCertificate::DigiIDType|SslCertificate::TempelType); + })); + cards = std::move(tokens); + if(cards.contains(selectedToken)) + { + updateSelector(); + return; + } + auto selected = std::find_if(cards.cbegin(), cards.cend(), [this](const TokenData &token) { + return token.card() == selectedToken.card() && token.reader() == selectedToken.reader(); + }); + selectCard(selected == cards.cend() ? cards.value(0) : *selected); +} + +void MyEidInfo::selectCard(const TokenData &token) +{ + if(token == selectedToken) + return; + selectedToken = token; + currentData = {}; + readFailed = false; + request = 0; + updateSelector(); + updateData(); + Q_EMIT dataChanged(currentData); + refresh(); +} + +void MyEidInfo::refresh() { - certType = 0; - expiry = {}; - ui->valueGivenNames->clear(); - ui->valueSurname->clear(); - ui->valuePersonalCode->clear(); - ui->valueCitizenship->clear(); - ui->valueExpiryDate->clear(); - ui->valueDocument->clear(); - ui->authBox->clear(); - ui->signBox->clear(); - ui->pukBox->clear(); + if(request || selectedToken.isNull() || selectedToken.reader().isEmpty()) + return; + request = qApp->cryptoManager()->readCard(selectedToken, currentData); } void MyEidInfo::changeEvent(QEvent* event) @@ -66,14 +131,20 @@ void MyEidInfo::changeEvent(QEvent* event) if (event->type() == QEvent::LanguageChange) { ui->retranslateUi(this); - update(); + if(cardPopup) + ui->selector->setIcon(QIcon(QStringLiteral(":/images/arrow_down_selected.svg"))); + updateLabels(); + updateEmptyState(); } QWidget::changeEvent(event); } -void MyEidInfo::update() +void MyEidInfo::updateLabels() { + const SslCertificate cert = currentData.isNull() ? SslCertificate(selectedToken.cert()) : currentData.authCert(); + const auto certType = cert.type(); + const QDateTime expiry = currentData.isNull() ? cert.expiryDate() : currentData.data(QSmartCardData::Expiry).toDateTime(); ui->valueExpiryDate->setText(expiry.toString(QStringLiteral("dd.MM.yyyy"))); if(certType & SslCertificate::DigiIDType) { @@ -103,49 +174,167 @@ void MyEidInfo::update() } } -void MyEidInfo::update(const SslCertificate &cert) -{ - certType = cert.type(); - expiry = cert.expiryDate(); - bool isSign = cert.keyUsage().contains(SslCertificate::NonRepudiation); - ui->valueGivenNames->setText(cert.toString(QStringLiteral("CN"))); - ui->valueSurname->setText(cert.toString(QStringLiteral("O"))); - ui->valuePersonalCode->setText(cert.personalCode()); - ui->valueCitizenship->setText(cert.toString(QStringLiteral("C"))); - ui->valueDocument->clear(); - ui->authBox->setHidden(isSign); - ui->signBox->setVisible(isSign); - if(isSign) - ui->signBox->update(QSmartCardData::Pin2Type, cert); +void MyEidInfo::updateData() +{ + bool waiting = currentData.isNull() && + !(SslCertificate(selectedToken.cert()).type() & SslCertificate::TempelType); + ui->cardDetails->setHidden(waiting); + ui->noReaderInfo->setVisible(waiting); + updateEmptyState(); + + if(currentData.isNull()) + { + ui->valueGivenNames->clear(); + ui->valueSurname->clear(); + ui->valuePersonalCode->clear(); + ui->valueCitizenship->clear(); + ui->valueExpiryDate->clear(); + ui->valueDocument->clear(); + ui->authBox->clear(); + ui->signBox->clear(); + ui->pukBox->clear(); + SslCertificate cert(selectedToken.cert()); + if(!(cert.type() & SslCertificate::TempelType)) + return; + bool isSign = cert.keyUsage().contains(SslCertificate::NonRepudiation); + ui->valueGivenNames->setText(cert.toString(QStringLiteral("CN"))); + ui->valueSurname->setText(cert.toString(QStringLiteral("O"))); + ui->valuePersonalCode->setText(cert.personalCode()); + ui->valueCitizenship->setText(cert.toString(QStringLiteral("C"))); + ui->authBox->setHidden(isSign); + ui->signBox->setVisible(isSign); + if(isSign) + ui->signBox->update(QSmartCardData::Pin2Type, cert); + else + ui->authBox->update(QSmartCardData::Pin1Type, cert); + ui->pukBox->hide(); + } else - ui->authBox->update(QSmartCardData::Pin1Type, cert); - ui->pukBox->hide(); + { + ui->valueGivenNames->setText(currentData.data(QSmartCardData::FirstName).toString()); + ui->valueSurname->setText(currentData.data(QSmartCardData::SurName).toString()); + ui->valuePersonalCode->setText(currentData.data(QSmartCardData::Id).toString()); + ui->valueCitizenship->setText(currentData.data(QSmartCardData::Citizen).toString()); + ui->valueDocument->setText(currentData.data(QSmartCardData::DocumentId).toString()); + + ui->authBox->setVisible(!currentData.authCert().isNull()); + if(!currentData.authCert().isNull()) + ui->authBox->update(QSmartCardData::Pin1Type, currentData); + + ui->signBox->setVisible(!currentData.signCert().isNull()); + if(!currentData.signCert().isNull()) + ui->signBox->update(QSmartCardData::Pin2Type, currentData); + + ui->pukBox->show(); + ui->pukBox->update(QSmartCardData::PukType, currentData); + } - update(); + updateLabels(); } -void MyEidInfo::update(const QSmartCardData &t) +void MyEidInfo::hideEvent(QHideEvent *event) { - if(t.isNull()) - return clearData(); - certType = t.authCert().type(); - expiry = t.data(QSmartCardData::Expiry).toDateTime(); - ui->valueGivenNames->setText(t.data(QSmartCardData::FirstName).toString()); - ui->valueSurname->setText(t.data(QSmartCardData::SurName).toString()); - ui->valuePersonalCode->setText(t.data(QSmartCardData::Id).toString()); - ui->valueCitizenship->setText(t.data(QSmartCardData::Citizen).toString()); - ui->valueDocument->setText(t.data(QSmartCardData::DocumentId).toString()); + hideCardPopup(); + QWidget::hideEvent(event); +} + +bool MyEidInfo::eventFilter(QObject *watched, QEvent *event) +{ + if(cardPopup && event->type() == QEvent::MouseButtonPress) + { + auto *widget = qobject_cast(watched); + auto pos = static_cast(event)->globalPosition().toPoint(); + auto contains = [pos](QWidget *item) { + return item->isVisible() && item->rect().contains(item->mapFromGlobal(pos)); + }; + if(widget && widget->window() == window() && + !contains(cardPopup) && !contains(ui->cardInfo) && !contains(ui->selector)) + hideCardPopup(); + } + return QWidget::eventFilter(watched, event); +} + +void MyEidInfo::showEvent(QShowEvent *event) +{ + if(!event->spontaneous()) + { + refresh(); + updateEmptyState(enumerated); + } + QWidget::showEvent(event); +} + +void MyEidInfo::updateEmptyState(bool enumerationComplete) +{ + if(ui->noReaderInfo->isHidden()) + return; + if(!selectedToken.isNull()) + emptyStateText = readFailed ? QT_TR_NOOP("Failed to read card data") : QT_TR_NOOP("Reading card..."); + else if(enumerationComplete) + { + emptyStateText = QT_TR_NOOP("The card in the reader is not supported. Insert an Estonian ID-card."); + auto &pcsc = QPCSC::instance(); + const QStringList readers = pcsc.readers(); + if(!pcsc.serviceRunning()) + emptyStateText = QT_TR_NOOP("The PCSC service, required for using the ID-card, is not working. Check your computer settings."); + else if(readers.isEmpty()) + emptyStateText = QT_TR_NOOP("No card reader found. Connect a card reader to your computer."); + else if(std::none_of(readers.cbegin(), readers.cend(), [](const QString &reader) { + return QPCSCReader(reader).isPresent(); + })) + emptyStateText = QT_TR_NOOP("No card in the reader. Insert your ID-card into the reader."); + } + ui->noReaderInfoText->setText(tr(emptyStateText)); +} - ui->authBox->setVisible(!t.authCert().isNull()); - if(!t.authCert().isNull()) - ui->authBox->update(QSmartCardData::Pin1Type, t); +QList MyEidInfo::otherCards() const +{ + QList list; + for(const TokenData &token: cards) + { + if(token.card() == selectedToken.card()) + continue; + if(std::any_of(list.cbegin(), list.cend(), [token](const TokenData &item) { return token.card() == item.card(); })) + continue; + list.append(token); + } + return list; +} - ui->signBox->setVisible(!t.signCert().isNull()); - if(!t.signCert().isNull()) - ui->signBox->update(QSmartCardData::Pin2Type, t); +void MyEidInfo::toggleCardPopup() +{ + if(cardPopup) + return hideCardPopup(); + const auto list = otherCards(); + if(list.isEmpty()) + return; + cardPopup = new CardPopup(list, this); + qApp->installEventFilter(this); + connect(cardPopup, &CardPopup::activated, this, [this](const TokenData &token) { + hideCardPopup(); + selectCard(token); + }); + ui->selector->setIcon(QIcon(QStringLiteral(":/images/arrow_down_selected.svg"))); + cardPopup->show(); +} - ui->pukBox->show(); - ui->pukBox->update(QSmartCardData::PukType, t); +void MyEidInfo::hideCardPopup() +{ + if(!cardPopup) + return; + qApp->removeEventFilter(this); + cardPopup->hide(); + cardPopup->deleteLater(); + cardPopup = nullptr; + ui->selector->setIcon(QIcon(QStringLiteral(":/images/arrow_down.svg"))); +} - update(); +void MyEidInfo::updateSelector() +{ + ui->topBar->setVisible(!selectedToken.isNull()); + const auto count = otherCards().size(); + ui->selector->setHidden(count == 0); + hideCardPopup(); + ui->cardInfo->setCursor(count > 0 ? Qt::PointingHandCursor : Qt::ArrowCursor); + ui->cardInfo->update(selectedToken, count > 1); } diff --git a/client/widgets/MyEidInfo.h b/client/widgets/MyEidInfo.h index 618acf767..163d9906f 100644 --- a/client/widgets/MyEidInfo.h +++ b/client/widgets/MyEidInfo.h @@ -20,17 +20,15 @@ #pragma once #include "QSmartCard.h" +#include "TokenData.h" #include -#include - namespace Ui { class MyEidInfo; } -class SslCertificate; -class QSmartCardData; +class CardPopup; class MyEidInfo final: public QWidget { @@ -40,19 +38,36 @@ class MyEidInfo final: public QWidget explicit MyEidInfo( QWidget *parent = nullptr ); ~MyEidInfo() final; - void clearData(); - void update(const SslCertificate &cert); - void update(const QSmartCardData &t); + QSmartCardData cardData() const; + void changePin(QSmartCardData::PinType type, QSmartCard::PinAction action); Q_SIGNALS: - void changePinClicked(QSmartCardData::PinType, QSmartCard::PinAction); + void dataChanged(const QSmartCardData &data); private: void changeEvent(QEvent* event) final; - void update(); + bool eventFilter(QObject *watched, QEvent *event) final; + void hideEvent(QHideEvent *event) final; + void showEvent(QShowEvent *event) final; + void selectCard(const TokenData &token); + void refresh(); + void updateEmptyState(bool enumerationComplete = false); + QList otherCards() const; + void toggleCardPopup(); + void hideCardPopup(); + void updateSelector(); + void updateCards(QList tokens); + void updateData(); + void updateLabels(); Ui::MyEidInfo *ui; + CardPopup *cardPopup = nullptr; - QDateTime expiry; - int certType = 0; + QList cards; + TokenData selectedToken; + QSmartCardData currentData; + quint64 request = 0; + bool readFailed = false; + bool enumerated = false; + const char *emptyStateText = QT_TR_NOOP("Reading card..."); }; diff --git a/client/widgets/MyEidInfo.ui b/client/widgets/MyEidInfo.ui index 184256e78..e7951234c 100644 --- a/client/widgets/MyEidInfo.ui +++ b/client/widgets/MyEidInfo.ui @@ -4,43 +4,152 @@ MyEidInfo - - - 0 - 0 - 882 - 387 - - #authBox, #signBox, #pukBox { border: 1px solid #E7EAEF; border-radius: 8px; +} +#topBar { +background-color: #FFFFFF; +border-bottom: 1px solid #E7EAEF; +} +#noReaderInfo { +background-image: url(:/images/bg_lovid.svg); +background-repeat: repeat-x; +background-position: bottom; +} +#noReaderInfoText { +font-family: Roboto, Helvetica; +font-size: 18px; +font-weight: 400; } - + + + 0 + - 24 + 0 - 24 + 0 - 24 + 0 - 26 - - - 24 + 0 - - 40 - - - - - QLabel { + + + + + 0 + 54 + + + + + 16777215 + 54 + + + + + 0 + + + 0 + + + 6 + + + 16 + + + 6 + + + + + + + + Select card + + + + 0 + 0 + + + + + 32 + 0 + + + + + 32 + 16777215 + + + + border: none; + + + + :/images/arrow_down.svg + + + + + 24 + 24 + + + + + + + + Qt::Horizontal + + + + 8 + 20 + + + + + + + + + + + + 40 + + + 24 + + + 24 + + + 24 + + + 26 + + + + + QLabel { color: #607496; font-family: Roboto, Helvetica; font-size: 12px; @@ -67,8 +176,237 @@ color: #1A641B; background: #EAF8EA; font-weight: 400; } - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + 56 + + + 32 + + + + + 2 + + + + + Qt::TabFocus + + + Given names + + + + + + + 392 + + + Qt::TabFocus + + + valueGivenNames + + + true + + + + + + + + + 2 + + + + + Qt::TabFocus + + + Surname + + + + + + + 392 + + + Qt::TabFocus + + + valueSurname + + + true + + + + + + + + + 2 + + + + + Qt::TabFocus + + + Personal code + + + + + + + Qt::TabFocus + + + valuePersonalCode + + + + + + + + + 2 + + + + + Qt::TabFocus + + + Citizenship + + + + + + + Qt::TabFocus + + + valueCitizenship + + + + + + + + + 2 + + + + + Qt::TabFocus + + + Expiry date + + + + + + + + 0 + 0 + + + + Qt::TabFocus + + + valueExpiryDate + + + good + + + + + + + + + 2 + + + + + Qt::TabFocus + + + Document + + + + + + + Qt::TabFocus + + + valueSerialNumber + + + + + + + + + + + + 24 + + + + + + + + + + + + + + + + + + + + 0 + 0 @@ -81,210 +419,37 @@ font-weight: 400; 0 - - 56 - - - 32 - - - - - 2 + + + + Qt::TabFocus - - - - Qt::TabFocus - - - Given names - - - - - - - 392 - - - Qt::TabFocus - - - valueGivenNames - - - true - - - - - - - - - 2 + + Reading card... - - - - Qt::TabFocus - - - Surname - - - - - - - 392 - - - Qt::TabFocus - - - valueSurname - - - true - - - - - - - - - 2 + + Qt::AlignCenter - - - - Qt::TabFocus - - - Personal code - - - - - - - Qt::TabFocus - - - valuePersonalCode - - - - - - - - - 2 - - - - - Qt::TabFocus - - - Citizenship - - - - - - - Qt::TabFocus - - - valueCitizenship - - - - - - - - - 2 + + true - - - - Qt::TabFocus - - - Expiry date - - - - - - - - 0 - 0 - - - - Qt::TabFocus - - - valueExpiryDate - - - good - - - - - - - - - 2 + + true - - - - Qt::TabFocus - - - Document - - - - - - - Qt::TabFocus - - - valueSerialNumber - - - - + - - - - - - - - - + + CardWidget + QWidget +
widgets/CardWidget.h
+ 1 +
Label QLabel @@ -297,6 +462,8 @@ font-weight: 400; 1
- + + +
diff --git a/client/widgets/NoCardInfo.cpp b/client/widgets/NoCardInfo.cpp deleted file mode 100644 index 3f37c7edf..000000000 --- a/client/widgets/NoCardInfo.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * QDigiDoc4 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "NoCardInfo.h" -#include "ui_NoCardInfo.h" - -NoCardInfo::NoCardInfo( QWidget *parent ) - : QWidget(parent) - , ui(new Ui::NoCardInfo) -{ - ui->setupUi( this ); - ui->cardIcon->load(QStringLiteral(":/images/icon_IDkaart_red.svg")); -} - -NoCardInfo::~NoCardInfo() -{ - delete ui; -} - -void NoCardInfo::update(Status s) -{ - status = s; - switch(status) - { - case NoPCSC: - ui->cardStatus->setText(tr("The PCSC service, required for using the ID-card, is not working. Check your computer settings.")); - break; - case NoReader: - ui->cardStatus->setText(tr("No readers found")); - break; - case Loading: - ui->cardStatus->setText(tr("Loading data")); - break; - default: - ui->cardStatus->setText(tr("No card in reader")); - break; - } - setAccessibleDescription(ui->cardStatus->text()); -} - -void NoCardInfo::changeEvent(QEvent* event) -{ - if (event->type() == QEvent::LanguageChange) - update(status); - - QWidget::changeEvent(event); -} diff --git a/client/widgets/NoCardInfo.h b/client/widgets/NoCardInfo.h deleted file mode 100644 index 7ad3282a3..000000000 --- a/client/widgets/NoCardInfo.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * QDigiDoc4 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#pragma once - -#include - -namespace Ui { -class NoCardInfo; -} - -class NoCardInfo final : public QWidget -{ - Q_OBJECT - -public: - enum Status { - NoCard, - NoPCSC, - NoReader, - Loading - }; - - explicit NoCardInfo( QWidget *parent = nullptr ); - ~NoCardInfo() final; - - void update(Status s); - -protected: - void changeEvent(QEvent* event) final; - -private: - Ui::NoCardInfo *ui; - Status status = Loading; -}; diff --git a/client/widgets/NoCardInfo.ui b/client/widgets/NoCardInfo.ui deleted file mode 100644 index 5ec277b1a..000000000 --- a/client/widgets/NoCardInfo.ui +++ /dev/null @@ -1,75 +0,0 @@ - - - NoCardInfo - - - - 0 - 0 - 561 - 65 - - - - QLabel { -color: #07142A; -font-family: Roboto, Helvetica; -font-size: 14px; -font-weight: 400; -} - - - - 8 - - - 8 - - - 0 - - - 8 - - - 0 - - - - - - 24 - 24 - - - - - 24 - 24 - - - - - - - - Qt::TabFocus - - - No card in reader - - - - - - - - QSvgWidget - QWidget -
QtSvg/QSvgWidget.h
- 1 -
-
- - -
diff --git a/client/widgets/WarningItem.h b/client/widgets/WarningItem.h index 505567742..b465fbe81 100644 --- a/client/widgets/WarningItem.h +++ b/client/widgets/WarningItem.h @@ -53,7 +53,7 @@ struct WarningText { EmptyFileWarning, } type = NoWarning; int counter = 0; - std::function cb; + std::function cb{}; };