diff options
Diffstat (limited to 'src/pinentry/pinentrydialog.cpp')
-rw-r--r-- | src/pinentry/pinentrydialog.cpp | 634 |
1 files changed, 0 insertions, 634 deletions
diff --git a/src/pinentry/pinentrydialog.cpp b/src/pinentry/pinentrydialog.cpp deleted file mode 100644 index d63b0015..00000000 --- a/src/pinentry/pinentrydialog.cpp +++ /dev/null @@ -1,634 +0,0 @@ -/* 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 <[email protected]>. - * Modified by Andre Heinecke <[email protected]> - * Software engineering by Ingo Klöcker <[email protected]> - * - * 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+ - */ - -#include "pinentrydialog.h" - -#include <qnamespace.h> - -#include <QAccessible> -#include <QAction> -#include <QApplication> -#include <QCheckBox> -#include <QDebug> -#include <QDialogButtonBox> -#include <QFontMetrics> -#include <QGridLayout> -#include <QHBoxLayout> -#include <QKeyEvent> -#include <QLabel> -#include <QLineEdit> -#include <QMessageBox> -#include <QPainter> -#include <QPalette> -#include <QProgressBar> -#include <QPushButton> -#include <QRegularExpression> -#include <QStyle> -#include <QVBoxLayout> - -#include "accessibility.h" -#include "capslock/capslock.h" -#include "core/utils/MemoryUtils.h" -#include "pinentry.h" -#include "pinlineedit.h" - -void raiseWindow(QWidget *w) { - w->setWindowState((w->windowState() & ~Qt::WindowMinimized) | - Qt::WindowActive); - w->activateWindow(); - w->raise(); -} - -auto applicationIconPixmap(const QIcon &overlayIcon) -> QPixmap { - QPixmap pm = qApp->windowIcon().pixmap(48, 48); - - if (!overlayIcon.isNull()) { - QPainter painter(&pm); - const int emblem_size = 22; - painter.drawPixmap(pm.width() - emblem_size, 0, - overlayIcon.pixmap(emblem_size, emblem_size)); - } - - return pm; -} - -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); - setModal(true); - } - - QPalette red_text_palette; - red_text_palette.setColor(QPalette::WindowText, Qt::red); - - auto *const main_layout = 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 QLabel{this}; - _error->setTextFormat(Qt::PlainText); - _error->setTextInteractionFlags(Qt::TextSelectableByMouse); - _error->setPalette(red_text_palette); - _error->hide(); - grid->addWidget(_error, row, 1, 1, 2); - - row++; - _desc = new QLabel{this}; - _desc->setTextFormat(Qt::PlainText); - _desc->setTextInteractionFlags(Qt::TextSelectableByMouse); - _desc->hide(); - grid->addWidget(_desc, row, 1, 1, 2); - - row++; - mCapsLockHint = new QLabel{this}; - mCapsLockHint->setTextFormat(Qt::PlainText); - mCapsLockHint->setTextInteractionFlags(Qt::TextSelectableByMouse); - mCapsLockHint->setPalette(red_text_palette); - 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); - - auto *const 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(":password-generate.svg"))); - mGenerateButton->setVisible(false); - l->addWidget(mGenerateButton); - } - grid->addLayout(l, row, 2); - } - - /* Set up the show password action */ - const QIcon visibility_icon = QIcon(QLatin1String(":visibility.svg")); - const QIcon hide_icon = QIcon(QLatin1String(":hint.svg")); -#if QT_VERSION >= 0x050200 - if (!visibility_icon.isNull() && !hide_icon.isNull()) { - mVisiActionEdit = - _edit->addAction(visibility_icon, 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 QLabel{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 QLabel{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 *repeat_label = new QLabel{this}; - repeat_label->setTextFormat(Qt::PlainText); - repeat_label->setTextInteractionFlags(Qt::TextSelectableByMouse); - repeat_label->setText(repeatString); - grid->addWidget(repeat_label, row, 1); - - mRepeat = new PinLineEdit(this); - mRepeat->setMaxLength(256); - mRepeat->setEchoMode(QLineEdit::Password); - repeat_label->setBuddy(mRepeat); - grid->addWidget(mRepeat, row, 2); - - row++; - mRepeatError = new QLabel{this}; - mRepeatError->setTextFormat(Qt::PlainText); - mRepeatError->setTextInteractionFlags(Qt::TextSelectableByMouse); - mRepeatError->setPalette(red_text_palette); - 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); - main_layout->addLayout(hbox); - - auto *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)); - } - - main_layout->addStretch(1); - main_layout->addWidget(buttons); - main_layout->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 != nullptr) { - connect(mGenerateButton, &QPushButton::clicked, this, - &PinEntryDialog::generatePin); - } - if (mVisiActionEdit != nullptr) { - connect(mVisiActionEdit, &QAction::triggered, this, - &PinEntryDialog::toggleVisibility); - } - if (mVisiCB != nullptr) { - connect(mVisiCB, &QCheckBox::toggled, this, - &PinEntryDialog::toggleVisibility); - } - if (mRepeat != nullptr) { - connect(mRepeat, &QLineEdit::textChanged, this, - &PinEntryDialog::textChanged); - } - - auto *caps_lock_watcher = new CapsLockWatcher{this}; - connect(caps_lock_watcher, &CapsLockWatcher::stateChanged, this, - [this](bool locked) { mCapsLockHint->setVisible(locked); }); - - connect(qApp, &QApplication::focusChanged, this, - &PinEntryDialog::focusChanged); - connect(qApp, &QApplication::applicationStateChanged, this, - &PinEntryDialog::checkCapsLock); - checkCapsLock(); - - setAttribute(Qt::WA_DeleteOnClose); - setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); - - /* 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 { - raiseWindow(this); - } -} - -void PinEntryDialog::keyPressEvent(QKeyEvent *e) { - const auto return_pressed = - (!e->modifiers() && - (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return)) || - (e->modifiers() & Qt::KeypadModifier && e->key() == Qt::Key_Enter); - if (return_pressed && _edit->hasFocus() && (mRepeat != nullptr)) { - // 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); - _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(":data-error.svg")})); - } - _error->setText(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); - _ok->setVisible(!txt.isEmpty()); -} - -void PinEntryDialog::setCancelText(const QString &txt) { - _cancel->setText(txt); - _cancel->setVisible(!txt.isEmpty()); -} - -void PinEntryDialog::setQualityBar(const QString &txt) { - if (_have_quality_bar) { - _quality_bar_label->setText(txt); - } -} - -void PinEntryDialog::setQualityBarTT(const QString &txt) { - if (_have_quality_bar) { - _quality_bar->setToolTip(txt); - } -} - -void PinEntryDialog::setGenpinLabel(const QString &txt) { - if (mGenerateButton == nullptr) { - return; - } - mGenerateButton->setVisible(!txt.isEmpty()); - if (!txt.isEmpty()) { - Accessibility::setName(mGenerateButton, txt); - } -} - -void PinEntryDialog::setGenpinTT(const QString &txt) { - if (mGenerateButton != nullptr) { - 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 enable_formatting = - mFormatPassphrase && _edit->echoMode() == QLineEdit::Normal; - _edit->setFormattedPassphrase(enable_formatting); - if (mRepeat != nullptr) { - mRepeat->setFormattedPassphrase(enable_formatting); - const bool hint_about_to_be_hidden = - mFormattedPassphraseHint->isVisible() && !enable_formatting; - if (hint_about_to_be_hidden) { - // set hint spacer to current height of hint label before hiding the hint - mFormattedPassphraseHintSpacer->setMinimumHeight( - mFormattedPassphraseHint->height()); - mFormattedPassphraseHintSpacer->setVisible(true); - } else if (enable_formatting) { - mFormattedPassphraseHintSpacer->setVisible(false); - } - mFormattedPassphraseHint->setVisible(enable_formatting); - } -} - -void PinEntryDialog::onBackspace() { - cancelTimeout(); - - if (_disable_echo_allowed) { - _edit->setEchoMode(QLineEdit::NoEcho); - if (mRepeat != nullptr) { - mRepeat->setEchoMode(QLineEdit::NoEcho); - } - } -} - -void PinEntryDialog::updateQuality(const QString &txt) { - int length; - int percent; - QPalette pal; - - _disable_echo_allowed = false; - - if (!_have_quality_bar) { - return; - } - - length = txt.length(); - percent = length != 0 ? pinentry_inq_quality(txt) : 0; - if (length == 0) { - _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(struct pinentry peinfo) { - _pinentry_info = - GpgFrontend::SecureCreateUniqueObject<struct pinentry>(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 != 0)) { - if (_grabbed && (old != nullptr) && (old == _edit || old == mRepeat)) { - old->releaseKeyboard(); - _grabbed = false; - } - if (!_grabbed && (now != nullptr) && (now == _edit || now == mRepeat)) { - now->grabKeyboard(); - _grabbed = true; - } - } -} - -void PinEntryDialog::textChanged(const QString &text) { - Q_UNUSED(text); - - cancelTimeout(); - - if ((mVisiActionEdit != nullptr) && sender() == _edit) { - mVisiActionEdit->setVisible(!_edit->pin().isEmpty()); - } - if (mGenerateButton != nullptr) { - mGenerateButton->setVisible(_edit->pin().isEmpty() -#ifndef QT_NO_ACCESSIBILITY - && !mGenerateButton->accessibleName().isEmpty() -#endif - ); - } -} - -void PinEntryDialog::generatePin() { - // std::unique_ptr<char> pin{pinentry_inq_genpin(_pinentry_info.get())}; - // if (pin) { - // if (_edit->echoMode() == QLineEdit::Password) { - // if (mVisiActionEdit != nullptr) { - // mVisiActionEdit->trigger(); - // } - // if (mVisiCB != nullptr) { - // mVisiCB->setChecked(true); - // } - // } - // const auto pin_str = QString::fromUtf8(pin.get()); - // _edit->setPin(pin_str); - // mRepeat->setPin(pin_str); - // // 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 != nullptr) { - mVisiActionEdit->setIcon(QIcon(QLatin1String(":hint.svg"))); - mVisiActionEdit->setToolTip(mHideTT); - } - _edit->setEchoMode(QLineEdit::Normal); - if (mRepeat != nullptr) { - mRepeat->setEchoMode(QLineEdit::Normal); - } - } else { - if (mVisiActionEdit != nullptr) { - mVisiActionEdit->setIcon(QIcon(QLatin1String(":visibility.svg"))); - mVisiActionEdit->setToolTip(mVisibilityTT); - } - _edit->setEchoMode(QLineEdit::Password); - if (mRepeat != nullptr) { - mRepeat->setEchoMode(QLineEdit::Password); - } - } - } else { - if (mVisiCB->isChecked()) { - if (mRepeat != nullptr) { - mRepeat->setEchoMode(QLineEdit::Normal); - } - _edit->setEchoMode(QLineEdit::Normal); - } else { - if (mRepeat != nullptr) { - mRepeat->setEchoMode(QLineEdit::Password); - } - _edit->setEchoMode(QLineEdit::Password); - } - } - toggleFormattedPassphrase(); -} - -QString PinEntryDialog::repeatedPin() const { - if (mRepeat != nullptr) { - return mRepeat->pin(); - } - return QString(); -} - -bool PinEntryDialog::timedOut() const { return _timed_out; } - -void PinEntryDialog::setRepeatErrorText(const QString &err) { - if (mRepeatError != nullptr) { - mRepeatError->setText(err); - } -} - -void PinEntryDialog::cancelTimeout() { - if (_timer != nullptr) { - _timer->stop(); - } -} - -void PinEntryDialog::checkCapsLock() { - const auto state = capsLockState(); - if (state != LockState::Unknown) { - mCapsLockHint->setVisible(state == LockState::On); - } -} - -void PinEntryDialog::onAccept() { - cancelTimeout(); - - if ((mRepeat != nullptr) && mRepeat->pin() != _edit->pin()) { -#ifndef QT_NO_ACCESSIBILITY - if (QAccessible::isActive()) { - QMessageBox::information(this, mRepeatError->text(), - mRepeatError->text()); - } else -#endif - { - mRepeatError->setVisible(true); - } - return; - } - - accept(); -} |