diff options
Diffstat (limited to 'debian/pinentry-tqt/pinentry-tqt-1.2.1/qt/pinentrydialog.cpp')
-rw-r--r-- | debian/pinentry-tqt/pinentry-tqt-1.2.1/qt/pinentrydialog.cpp | 793 |
1 files changed, 793 insertions, 0 deletions
diff --git a/debian/pinentry-tqt/pinentry-tqt-1.2.1/qt/pinentrydialog.cpp b/debian/pinentry-tqt/pinentry-tqt-1.2.1/qt/pinentrydialog.cpp new file mode 100644 index 00000000..17e87e6c --- /dev/null +++ b/debian/pinentry-tqt/pinentry-tqt-1.2.1/qt/pinentrydialog.cpp @@ -0,0 +1,793 @@ +/* pinentrydialog.cpp - A (not yet) secure Qt 4 dialog for PIN entry. + * Copyright (C) 2002, 2008 Klarälvdalens Datakonsult AB (KDAB) + * Copyright 2007 Ingo Klöcker + * Copyright 2016 Intevation GmbH + * Copyright (C) 2021, 2022 g10 Code GmbH + * + * Written by Steffen Hansen <steffen@klaralvdalens-datakonsult.se>. + * Modified by Andre Heinecke <aheinecke@intevation.de> + * Software engineering by Ingo Klöcker <dev@ingo-kloecker.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "pinentrydialog.h" + +#include "accessibility.h" +#include "capslock.h" +#include "pinlineedit.h" +#include "util.h" + +#include <QGridLayout> +#include <QProgressBar> +#include <QApplication> +#include <QFontMetrics> +#include <QStyle> +#include <QPainter> +#include <QPushButton> +#include <QDialogButtonBox> +#include <QKeyEvent> +#include <QLabel> +#include <QPalette> +#include <QLineEdit> +#include <QAction> +#include <QCheckBox> +#include <QHBoxLayout> +#include <QVBoxLayout> +#include <QMessageBox> +#include <QRegularExpression> +#include <QAccessible> + +#include <QDebug> + +#ifdef Q_OS_WIN +#include <windows.h> +#if QT_VERSION >= 0x050700 +#include <QtPlatformHeaders/QWindowsWindowFunctions> +#endif +#endif + +void raiseWindow(QWidget *w) +{ +#ifdef Q_OS_WIN +#if QT_VERSION >= 0x050700 + QWindowsWindowFunctions::setWindowActivationBehavior( + QWindowsWindowFunctions::AlwaysActivateWindow); +#endif +#endif + w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); + w->activateWindow(); + w->raise(); +} + +QPixmap applicationIconPixmap(const QIcon &overlayIcon) +{ + QPixmap pm = qApp->windowIcon().pixmap(48, 48); + + if (!overlayIcon.isNull()) { + QPainter painter(&pm); + const int emblemSize = 22; + painter.drawPixmap(pm.width() - emblemSize, 0, + overlayIcon.pixmap(emblemSize, emblemSize)); + } + + return pm; +} + +namespace +{ + +class TextLabel : public QLabel +{ +public: + using QLabel::QLabel; + +protected: + void focusInEvent(QFocusEvent *ev) override; +}; + +void TextLabel::focusInEvent(QFocusEvent *ev) +{ + QLabel::focusInEvent(ev); + + // if the text label gets focus, then select its text; this is a workaround + // for missing focus indicators for labels in many Qt styles + const Qt::FocusReason reason = ev->reason(); + const auto isKeyboardFocusEvent = reason == Qt::TabFocusReason + || reason == Qt::BacktabFocusReason + || reason == Qt::ShortcutFocusReason; + if (!text().isEmpty() && isKeyboardFocusEvent) { + Accessibility::selectLabelText(this); + } +} + +} + +void PinEntryDialog::slotTimeout() +{ + _timed_out = true; + reject(); +} + +PinEntryDialog::PinEntryDialog(QWidget *parent, const char *name, + int timeout, bool modal, bool enable_quality_bar, + const QString &repeatString, + const QString &visibilityTT, + const QString &hideTT) + : QDialog{parent} + , _have_quality_bar{enable_quality_bar} + , mVisibilityTT{visibilityTT} + , mHideTT{hideTT} +{ + Q_UNUSED(name) + + if (modal) { + setWindowModality(Qt::ApplicationModal); + } + + QPalette redTextPalette; + redTextPalette.setColor(QPalette::WindowText, Qt::red); + + auto *const mainLayout = new QVBoxLayout{this}; + + auto *const hbox = new QHBoxLayout; + + _icon = new QLabel(this); + _icon->setPixmap(applicationIconPixmap()); + hbox->addWidget(_icon, 0, Qt::AlignVCenter | Qt::AlignLeft); + + auto *const grid = new QGridLayout; + int row = 1; + + _error = new TextLabel{this}; + _error->setTextFormat(Qt::PlainText); + _error->setTextInteractionFlags(Qt::TextSelectableByMouse); + _error->setPalette(redTextPalette); + _error->hide(); + grid->addWidget(_error, row, 1, 1, 2); + + row++; + _desc = new TextLabel{this}; + _desc->setTextFormat(Qt::PlainText); + _desc->setTextInteractionFlags(Qt::TextSelectableByMouse); + _desc->hide(); + grid->addWidget(_desc, row, 1, 1, 2); + + row++; + mCapsLockHint = new TextLabel{this}; + mCapsLockHint->setTextFormat(Qt::PlainText); + mCapsLockHint->setTextInteractionFlags(Qt::TextSelectableByMouse); + mCapsLockHint->setPalette(redTextPalette); + mCapsLockHint->setAlignment(Qt::AlignCenter); + mCapsLockHint->setVisible(false); + grid->addWidget(mCapsLockHint, row, 1, 1, 2); + + row++; + { + _prompt = new QLabel(this); + _prompt->setTextFormat(Qt::PlainText); + _prompt->setTextInteractionFlags(Qt::TextSelectableByMouse); + _prompt->hide(); + grid->addWidget(_prompt, row, 1); + + const auto l = new QHBoxLayout; + _edit = new PinLineEdit(this); + _edit->setMaxLength(256); + _edit->setMinimumWidth(_edit->fontMetrics().averageCharWidth()*20 + 48); + _edit->setEchoMode(QLineEdit::Password); + _prompt->setBuddy(_edit); + l->addWidget(_edit, 1); + + if (!repeatString.isNull()) { + mGenerateButton = new QPushButton{this}; + mGenerateButton->setIcon(QIcon(QLatin1String(":/icons/password-generate"))); + mGenerateButton->setVisible(false); + l->addWidget(mGenerateButton); + } + grid->addLayout(l, row, 2); + } + + /* Set up the show password action */ + const QIcon visibilityIcon = QIcon(QLatin1String(":/icons/visibility.svg")); + const QIcon hideIcon = QIcon(QLatin1String(":/icons/hint.svg")); +#if QT_VERSION >= 0x050200 + if (!visibilityIcon.isNull() && !hideIcon.isNull()) { + mVisiActionEdit = _edit->addAction(visibilityIcon, QLineEdit::TrailingPosition); + mVisiActionEdit->setVisible(false); + mVisiActionEdit->setToolTip(mVisibilityTT); + } else +#endif + { + if (!mVisibilityTT.isNull()) { + row++; + mVisiCB = new QCheckBox{mVisibilityTT, this}; + grid->addWidget(mVisiCB, row, 1, 1, 2, Qt::AlignLeft); + } + } + + row++; + mConstraintsHint = new TextLabel{this}; + mConstraintsHint->setTextFormat(Qt::PlainText); + mConstraintsHint->setTextInteractionFlags(Qt::TextSelectableByMouse); + mConstraintsHint->setVisible(false); + grid->addWidget(mConstraintsHint, row, 2); + + row++; + mFormattedPassphraseHintSpacer = new QLabel{this}; + mFormattedPassphraseHintSpacer->setVisible(false); + mFormattedPassphraseHint = new TextLabel{this}; + mFormattedPassphraseHint->setTextFormat(Qt::PlainText); + mFormattedPassphraseHint->setTextInteractionFlags(Qt::TextSelectableByMouse); + mFormattedPassphraseHint->setVisible(false); + grid->addWidget(mFormattedPassphraseHintSpacer, row, 1); + grid->addWidget(mFormattedPassphraseHint, row, 2); + + if (!repeatString.isNull()) { + row++; + auto repeatLabel = new QLabel{this}; + repeatLabel->setTextFormat(Qt::PlainText); + repeatLabel->setTextInteractionFlags(Qt::TextSelectableByMouse); + repeatLabel->setText(repeatString); + grid->addWidget(repeatLabel, row, 1); + + mRepeat = new PinLineEdit(this); + mRepeat->setMaxLength(256); + mRepeat->setEchoMode(QLineEdit::Password); + repeatLabel->setBuddy(mRepeat); + grid->addWidget(mRepeat, row, 2); + + row++; + mRepeatError = new TextLabel{this}; + mRepeatError->setTextFormat(Qt::PlainText); + mRepeatError->setTextInteractionFlags(Qt::TextSelectableByMouse); + mRepeatError->setPalette(redTextPalette); + mRepeatError->hide(); + grid->addWidget(mRepeatError, row, 2); + } + + if (enable_quality_bar) { + row++; + _quality_bar_label = new QLabel(this); + _quality_bar_label->setTextFormat(Qt::PlainText); + _quality_bar_label->setTextInteractionFlags(Qt::TextSelectableByMouse); + _quality_bar_label->setAlignment(Qt::AlignVCenter); + grid->addWidget(_quality_bar_label, row, 1); + + _quality_bar = new QProgressBar(this); + _quality_bar->setAlignment(Qt::AlignCenter); + _quality_bar_label->setBuddy(_quality_bar); + grid->addWidget(_quality_bar, row, 2); + } + + hbox->addLayout(grid, 1); + mainLayout->addLayout(hbox); + + QDialogButtonBox *const buttons = new QDialogButtonBox(this); + buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + _ok = buttons->button(QDialogButtonBox::Ok); + _cancel = buttons->button(QDialogButtonBox::Cancel); + + if (style()->styleHint(QStyle::SH_DialogButtonBox_ButtonsHaveIcons)) { + _ok->setIcon(style()->standardIcon(QStyle::SP_DialogOkButton)); + _cancel->setIcon(style()->standardIcon(QStyle::SP_DialogCancelButton)); + } + + mainLayout->addStretch(1); + mainLayout->addWidget(buttons); + mainLayout->setSizeConstraint(QLayout::SetFixedSize); + + if (timeout > 0) { + _timer = new QTimer(this); + connect(_timer, &QTimer::timeout, this, &PinEntryDialog::slotTimeout); + _timer->start(timeout * 1000); + } + + connect(buttons, &QDialogButtonBox::accepted, + this, &PinEntryDialog::onAccept); + connect(buttons, &QDialogButtonBox::rejected, + this, &QDialog::reject); + connect(_edit, &QLineEdit::textChanged, + this, &PinEntryDialog::updateQuality); + connect(_edit, &QLineEdit::textChanged, + this, &PinEntryDialog::textChanged); + connect(_edit, &PinLineEdit::backspacePressed, + this, &PinEntryDialog::onBackspace); + if (mGenerateButton) { + connect(mGenerateButton, &QPushButton::clicked, + this, &PinEntryDialog::generatePin); + } + if (mVisiActionEdit) { + connect(mVisiActionEdit, &QAction::triggered, + this, &PinEntryDialog::toggleVisibility); + } + if (mVisiCB) { + connect(mVisiCB, &QCheckBox::toggled, + this, &PinEntryDialog::toggleVisibility); + } + if (mRepeat) { + connect(mRepeat, &QLineEdit::textChanged, + this, &PinEntryDialog::textChanged); + } + + auto capsLockWatcher = new CapsLockWatcher{this}; + connect(capsLockWatcher, &CapsLockWatcher::stateChanged, + this, [this] (bool locked) { + mCapsLockHint->setVisible(locked); + }); + + connect(qApp, &QApplication::focusChanged, + this, &PinEntryDialog::focusChanged); + connect(qApp, &QApplication::applicationStateChanged, + this, &PinEntryDialog::checkCapsLock); + checkCapsLock(); + +#ifndef QT_NO_ACCESSIBILITY + QAccessible::installActivationObserver(this); + accessibilityActiveChanged(QAccessible::isActive()); +#endif + +#if QT_VERSION >= 0x050000 + /* This is mostly an issue on Windows where this results + in the pinentry popping up nicely with an animation and + comes to front. It is not ifdefed for Windows only since + window managers on Linux like KWin can also have this + result in an animation when the pinentry is shown and + not just popping it up. + */ + if (qApp->platformName() != QLatin1String("wayland")) { + setWindowState(Qt::WindowMinimized); + QTimer::singleShot(0, this, [this] () { + raiseWindow(this); + }); + } +#else + activateWindow(); + raise(); +#endif +} + +PinEntryDialog::~PinEntryDialog() +{ +#ifndef QT_NO_ACCESSIBILITY + QAccessible::removeActivationObserver(this); +#endif +} + +void PinEntryDialog::keyPressEvent(QKeyEvent *e) +{ + const auto returnPressed = + (!e->modifiers() && (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)) + || (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter); + if (returnPressed && _edit->hasFocus() && mRepeat) { + // if the user pressed Return in the first input field, then move the + // focus to the repeat input field and prevent further event processing + // by QDialog (which would trigger the default button) + mRepeat->setFocus(); + e->ignore(); + return; + } + + QDialog::keyPressEvent(e); +} + +void PinEntryDialog::keyReleaseEvent(QKeyEvent *event) +{ + QDialog::keyReleaseEvent(event); + checkCapsLock(); +} + +void PinEntryDialog::showEvent(QShowEvent *event) +{ + QDialog::showEvent(event); + _edit->setFocus(); +} + +void PinEntryDialog::setDescription(const QString &txt) +{ + _desc->setVisible(!txt.isEmpty()); + _desc->setText(txt); + Accessibility::setDescription(_desc, txt); + _icon->setPixmap(applicationIconPixmap()); + setError(QString()); +} + +QString PinEntryDialog::description() const +{ + return _desc->text(); +} + +void PinEntryDialog::setError(const QString &txt) +{ + if (!txt.isNull()) { + _icon->setPixmap(applicationIconPixmap(QIcon{QStringLiteral(":/icons/data-error.svg")})); + } + _error->setText(txt); + Accessibility::setDescription(_error, txt); + _error->setVisible(!txt.isEmpty()); +} + +QString PinEntryDialog::error() const +{ + return _error->text(); +} + +void PinEntryDialog::setPin(const QString &txt) +{ + _edit->setPin(txt); +} + +QString PinEntryDialog::pin() const +{ + return _edit->pin(); +} + +void PinEntryDialog::setPrompt(const QString &txt) +{ + _prompt->setText(txt); + _prompt->setVisible(!txt.isEmpty()); + if (txt.contains("PIN")) + _disable_echo_allowed = false; +} + +QString PinEntryDialog::prompt() const +{ + return _prompt->text(); +} + +void PinEntryDialog::setOkText(const QString &txt) +{ + _ok->setText(txt); + Accessibility::setDescription(_ok, txt); + _ok->setVisible(!txt.isEmpty()); +} + +void PinEntryDialog::setCancelText(const QString &txt) +{ + _cancel->setText(txt); + Accessibility::setDescription(_cancel, txt); + _cancel->setVisible(!txt.isEmpty()); +} + +void PinEntryDialog::setQualityBar(const QString &txt) +{ + if (_have_quality_bar) { + _quality_bar_label->setText(txt); + Accessibility::setDescription(_quality_bar_label, txt); + } +} + +void PinEntryDialog::setQualityBarTT(const QString &txt) +{ + if (_have_quality_bar) { + _quality_bar->setToolTip(txt); + } +} + +void PinEntryDialog::setGenpinLabel(const QString &txt) +{ + if (!mGenerateButton) { + return; + } + mGenerateButton->setVisible(!txt.isEmpty()); + if (!txt.isEmpty()) { + Accessibility::setName(mGenerateButton, txt); + } +} + +void PinEntryDialog::setGenpinTT(const QString &txt) +{ + if (mGenerateButton) { + mGenerateButton->setToolTip(txt); + } +} + +void PinEntryDialog::setCapsLockHint(const QString &txt) +{ + mCapsLockHint->setText(txt); +} + +void PinEntryDialog::setFormattedPassphrase(const PinEntryDialog::FormattedPassphraseOptions &options) +{ + mFormatPassphrase = options.formatPassphrase; + mFormattedPassphraseHint->setTextFormat(Qt::RichText); + mFormattedPassphraseHint->setText(QLatin1String("<html>") + options.hint.toHtmlEscaped() + QLatin1String("</html>")); + Accessibility::setName(mFormattedPassphraseHint, options.hint); + toggleFormattedPassphrase(); +} + +void PinEntryDialog::setConstraintsOptions(const ConstraintsOptions &options) +{ + mEnforceConstraints = options.enforce; + mConstraintsHint->setText(options.shortHint); + if (!options.longHint.isEmpty()) { + mConstraintsHint->setToolTip(QLatin1String("<html>") + + options.longHint.toHtmlEscaped().replace(QLatin1String("\n\n"), QLatin1String("<br>")) + + QLatin1String("</html>")); + Accessibility::setDescription(mConstraintsHint, options.longHint); + } + mConstraintsErrorTitle = options.errorTitle; + + mConstraintsHint->setVisible(mEnforceConstraints && !options.shortHint.isEmpty()); +} + +void PinEntryDialog::toggleFormattedPassphrase() +{ + const bool enableFormatting = mFormatPassphrase && _edit->echoMode() == QLineEdit::Normal; + _edit->setFormattedPassphrase(enableFormatting); + if (mRepeat) { + mRepeat->setFormattedPassphrase(enableFormatting); + const bool hintAboutToBeHidden = mFormattedPassphraseHint->isVisible() && !enableFormatting; + if (hintAboutToBeHidden) { + // set hint spacer to current height of hint label before hiding the hint + mFormattedPassphraseHintSpacer->setMinimumHeight(mFormattedPassphraseHint->height()); + mFormattedPassphraseHintSpacer->setVisible(true); + } else if (enableFormatting) { + mFormattedPassphraseHintSpacer->setVisible(false); + } + mFormattedPassphraseHint->setVisible(enableFormatting); + } +} + +void PinEntryDialog::onBackspace() +{ + cancelTimeout(); + + if (_disable_echo_allowed) { + _edit->setEchoMode(QLineEdit::NoEcho); + if (mRepeat) { + mRepeat->setEchoMode(QLineEdit::NoEcho); + } + } +} + +void PinEntryDialog::updateQuality(const QString &txt) +{ + int length; + int percent; + QPalette pal; + + _disable_echo_allowed = false; + + if (!_have_quality_bar || !_pinentry_info) { + return; + } + const QByteArray utf8_pin = txt.toUtf8(); + const char *pin = utf8_pin.constData(); + length = strlen(pin); + percent = length ? pinentry_inq_quality(_pinentry_info, pin, length) : 0; + if (!length) { + _quality_bar->reset(); + } else { + pal = _quality_bar->palette(); + if (percent < 0) { + pal.setColor(QPalette::Highlight, QColor("red")); + percent = -percent; + } else { + pal.setColor(QPalette::Highlight, QColor("green")); + } + _quality_bar->setPalette(pal); + _quality_bar->setValue(percent); + } +} + +void PinEntryDialog::setPinentryInfo(pinentry_t peinfo) +{ + _pinentry_info = peinfo; +} + +void PinEntryDialog::focusChanged(QWidget *old, QWidget *now) +{ + // Grab keyboard. It might be a little weird to do it here, but it works! + // Previously this code was in showEvent, but that did not work in Qt4. + if (!_pinentry_info || _pinentry_info->grab) { + if (_grabbed && old && (old == _edit || old == mRepeat)) { + old->releaseKeyboard(); + _grabbed = false; + } + if (!_grabbed && now && (now == _edit || now == mRepeat)) { + now->grabKeyboard(); + _grabbed = true; + } + } +} + +void PinEntryDialog::textChanged(const QString &text) +{ + Q_UNUSED(text); + + cancelTimeout(); + + if (mVisiActionEdit && sender() == _edit) { + mVisiActionEdit->setVisible(!_edit->pin().isEmpty()); + } + if (mGenerateButton) { + mGenerateButton->setVisible( + _edit->pin().isEmpty() +#ifndef QT_NO_ACCESSIBILITY + && !mGenerateButton->accessibleName().isEmpty() +#endif + ); + } +} + +void PinEntryDialog::generatePin() +{ + unique_malloced_ptr<char> pin{pinentry_inq_genpin(_pinentry_info)}; + if (pin) { + if (_edit->echoMode() == QLineEdit::Password) { + if (mVisiActionEdit) { + mVisiActionEdit->trigger(); + } + if (mVisiCB) { + mVisiCB->setChecked(true); + } + } + const auto pinStr = QString::fromUtf8(pin.get()); + _edit->setPin(pinStr); + mRepeat->setPin(pinStr); + // explicitly focus the first input field and select the generated password + _edit->setFocus(); + _edit->selectAll(); + } +} + +void PinEntryDialog::toggleVisibility() +{ + if (sender() != mVisiCB) { + if (_edit->echoMode() == QLineEdit::Password) { + if (mVisiActionEdit) { + mVisiActionEdit->setIcon(QIcon(QLatin1String(":/icons/hint.svg"))); + mVisiActionEdit->setToolTip(mHideTT); + } + _edit->setEchoMode(QLineEdit::Normal); + if (mRepeat) { + mRepeat->setEchoMode(QLineEdit::Normal); + } + } else { + if (mVisiActionEdit) { + mVisiActionEdit->setIcon(QIcon(QLatin1String(":/icons/visibility.svg"))); + mVisiActionEdit->setToolTip(mVisibilityTT); + } + _edit->setEchoMode(QLineEdit::Password); + if (mRepeat) { + mRepeat->setEchoMode(QLineEdit::Password); + } + } + } else { + if (mVisiCB->isChecked()) { + if (mRepeat) { + mRepeat->setEchoMode(QLineEdit::Normal); + } + _edit->setEchoMode(QLineEdit::Normal); + } else { + if (mRepeat) { + mRepeat->setEchoMode(QLineEdit::Password); + } + _edit->setEchoMode(QLineEdit::Password); + } + } + toggleFormattedPassphrase(); +} + +QString PinEntryDialog::repeatedPin() const +{ + if (mRepeat) { + return mRepeat->pin(); + } + return QString(); +} + +bool PinEntryDialog::timedOut() const +{ + return _timed_out; +} + +void PinEntryDialog::setRepeatErrorText(const QString &err) +{ + if (mRepeatError) { + mRepeatError->setText(err); + } +} + +void PinEntryDialog::cancelTimeout() +{ + if (_timer) { + _timer->stop(); + } +} + +void PinEntryDialog::checkCapsLock() +{ + const auto state = capsLockState(); + if (state != LockState::Unknown) { + mCapsLockHint->setVisible(state == LockState::On); + } +} + +void PinEntryDialog::onAccept() +{ + cancelTimeout(); + + if (mRepeat && mRepeat->pin() != _edit->pin()) { +#ifndef QT_NO_ACCESSIBILITY + if (QAccessible::isActive()) { + QMessageBox::information(this, mRepeatError->text(), mRepeatError->text()); + } else +#endif + { + mRepeatError->setVisible(true); + } + return; + } + + const auto result = checkConstraints(); + if (result != PassphraseNotOk) { + accept(); + } +} + +#ifndef QT_NO_ACCESSIBILITY +void PinEntryDialog::accessibilityActiveChanged(bool active) +{ + // Allow text labels to get focus if accessibility is active + const auto focusPolicy = active ? Qt::StrongFocus : Qt::ClickFocus; + _error->setFocusPolicy(focusPolicy); + _desc->setFocusPolicy(focusPolicy); + mCapsLockHint->setFocusPolicy(focusPolicy); + mConstraintsHint->setFocusPolicy(focusPolicy); + mFormattedPassphraseHint->setFocusPolicy(focusPolicy); + if (mRepeatError) { + mRepeatError->setFocusPolicy(focusPolicy); + } +} +#endif + +PinEntryDialog::PassphraseCheckResult PinEntryDialog::checkConstraints() +{ + if (!mEnforceConstraints) { + return PassphraseNotChecked; + } + + const auto passphrase = _edit->pin().toUtf8(); + unique_malloced_ptr<char> error{pinentry_inq_checkpin( + _pinentry_info, passphrase.constData(), passphrase.size())}; + + if (!error) { + return PassphraseOk; + } + + const auto messageLines = QString::fromUtf8(QByteArray::fromPercentEncoding(error.get())).split(QChar{'\n'}); + if (messageLines.isEmpty()) { + // shouldn't happen because pinentry_inq_checkpin() either returns NULL or a non-empty string + return PassphraseOk; + } + const auto firstLine = messageLines.first(); + const auto indexOfFirstNonEmptyAdditionalLine = messageLines.indexOf(QRegularExpression{QStringLiteral(".*\\S.*")}, 1); + const auto additionalLines = indexOfFirstNonEmptyAdditionalLine > 0 ? messageLines.mid(indexOfFirstNonEmptyAdditionalLine).join(QChar{'\n'}) : QString{}; + QMessageBox messageBox{this}; + messageBox.setIcon(QMessageBox::Information); + messageBox.setWindowTitle(mConstraintsErrorTitle); + messageBox.setText(firstLine); + messageBox.setInformativeText(additionalLines); + messageBox.setStandardButtons(QMessageBox::Ok); + messageBox.exec(); + return PassphraseNotOk; +} + +#include "pinentrydialog.moc" |