diff --git a/src/dialogs/adduseriddialog.cpp b/src/dialogs/adduseriddialog.cpp index 1e7615148..844aacbfd 100644 --- a/src/dialogs/adduseriddialog.cpp +++ b/src/dialogs/adduseriddialog.cpp @@ -1,268 +1,268 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/adduseriddialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "adduseriddialog.h" #include "utils/accessibility.h" #include "utils/scrollarea.h" #include "view/errorlabel.h" #include "view/formtextinput.h" #include "view/htmllabel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; namespace { QString buildUserId(const QString &name, const QString &email) { if (name.isEmpty()) { return email; } else if (email.isEmpty()) { return name; } else { return QStringLiteral("%1 <%2>").arg(name, email); } } } class AddUserIDDialog::Private { friend class ::Kleo::AddUserIDDialog; AddUserIDDialog *const q; struct { ScrollArea *scrollArea; std::unique_ptr> nameInput; std::unique_ptr> emailInput; HtmlLabel *resultLabel; QDialogButtonBox *buttonBox; } ui; LabelHelper labelHelper; public: explicit Private(AddUserIDDialog *qq) : q{qq} { q->setWindowTitle(i18nc("title:window", "Add User ID")); const KConfigGroup config{KSharedConfig::openConfig(), "CertificateCreationWizard"}; const auto attrOrder = config.readEntry("OpenPGPAttributeOrder", QStringList{}); const auto nameIsRequired = attrOrder.contains(QLatin1String{"NAME!"}, Qt::CaseInsensitive); const auto emailIsRequired = attrOrder.contains(QLatin1String{"EMAIL!"}, Qt::CaseInsensitive); auto mainLayout = new QVBoxLayout{q}; { const auto infoText = nameIsRequired || emailIsRequired ? i18n("Enter a name and an email address to use for the user ID.") : i18n("Enter a name and/or an email address to use for the user ID."); auto label = new QLabel{infoText, q}; label->setWordWrap(true); mainLayout->addWidget(label); } mainLayout->addWidget(new KSeparator{Qt::Horizontal, q}); ui.scrollArea = new ScrollArea{q}; ui.scrollArea->setFocusPolicy(Qt::NoFocus); ui.scrollArea->setFrameStyle(QFrame::NoFrame); ui.scrollArea->setBackgroundRole(q->backgroundRole()); ui.scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui.scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents); auto scrollAreaLayout = qobject_cast(ui.scrollArea->widget()->layout()); scrollAreaLayout->setContentsMargins(0, 0, 0, 0); { ui.nameInput = FormTextInput::create(q); ui.nameInput->setLabelText(i18nc("@label", "Name")); ui.nameInput->setIsRequired(nameIsRequired); ui.nameInput->setValueRequiredErrorMessage(i18n("Enter a name.")); const auto regexp = config.readEntry("NAME_regex"); if (regexp.isEmpty()) { - ui.nameInput->setValidator(std::shared_ptr{Validation::simpleName(Validation::Optional)}); + ui.nameInput->setValidator(Validation::simpleName(Validation::Optional)); ui.nameInput->setHint(i18n("Must not include <, >, and @."), i18nc("text for screen readers", "Must not include less-than sign, greater-than sign, and at sign.")); ui.nameInput->setInvalidEntryErrorMessage( i18n("The name must not include <, >, and @."), i18nc("text for screen readers", "The name must not include less-than sign, greater-than sign, and at sign.")); } else { - ui.nameInput->setValidator(std::shared_ptr{Validation::simpleName(regexp, Validation::Optional)}); + ui.nameInput->setValidator(Validation::simpleName(regexp, Validation::Optional)); ui.nameInput->setHint(i18n("Must be in the format required by your organization and " "must not include <, >, and @."), i18nc("text for screen readers", "Must be in the format required by your organization and " "must not include less-than sign, greater-than sign, and at sign.")); ui.nameInput->setInvalidEntryErrorMessage( i18n("The name must be in the format required by your organization and " "it must not include <, >, and @."), i18nc("text for screen readers", "The name must be in the format required by your organization and " "it must not include less-than sign, greater-than sign, and at sign.")); } scrollAreaLayout->addWidget(ui.nameInput->label()); scrollAreaLayout->addWidget(ui.nameInput->hintLabel()); scrollAreaLayout->addWidget(ui.nameInput->errorLabel()); scrollAreaLayout->addWidget(ui.nameInput->widget()); } connect(ui.nameInput->widget(), &QLineEdit::textChanged, q, [this]() { updateResultLabel(); }); { ui.emailInput = FormTextInput::create(q); ui.emailInput->setLabelText(i18nc("@label", "Email address")); ui.emailInput->setIsRequired(emailIsRequired); ui.emailInput->setValueRequiredErrorMessage(i18n("Enter an email address.")); const auto regexp = config.readEntry(QLatin1String("EMAIL_regex")); if (regexp.isEmpty()) { - ui.emailInput->setValidator(std::shared_ptr{Validation::email(Validation::Optional)}); + ui.emailInput->setValidator(Validation::email(Validation::Optional)); ui.emailInput->setInvalidEntryErrorMessage(i18n( "Enter an email address in the correct format, like name@example.com.")); } else { - ui.emailInput->setValidator(std::shared_ptr{Validation::email(regexp, Validation::Optional)}); + ui.emailInput->setValidator(Validation::email(regexp, Validation::Optional)); ui.emailInput->setHint(i18n( "Must be in the format required by your organization")); ui.emailInput->setInvalidEntryErrorMessage(i18n( "Enter an email address in the correct format required by your organization.")); } scrollAreaLayout->addWidget(ui.emailInput->label()); scrollAreaLayout->addWidget(ui.emailInput->hintLabel()); scrollAreaLayout->addWidget(ui.emailInput->errorLabel()); scrollAreaLayout->addWidget(ui.emailInput->widget()); } connect(ui.emailInput->widget(), &QLineEdit::textChanged, q, [this]() { updateResultLabel(); }); scrollAreaLayout->addWidget(new KSeparator{Qt::Horizontal, q}); { ui.resultLabel = new HtmlLabel{q}; ui.resultLabel->setWordWrap(true); ui.resultLabel->setFocusPolicy(Qt::ClickFocus); labelHelper.addLabel(ui.resultLabel); scrollAreaLayout->addWidget(ui.resultLabel); } scrollAreaLayout->addStretch(1); mainLayout->addWidget(ui.scrollArea); mainLayout->addWidget(new KSeparator{Qt::Horizontal, q}); ui.buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok | QDialogButtonBox::Cancel, q}; mainLayout->addWidget(ui.buttonBox); connect(ui.buttonBox, &QDialogButtonBox::accepted, q, [this]() { checkAccept(); }); connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); updateResultLabel(); } QString name() const { return ui.nameInput->widget()->text().trimmed(); } QString email() const { return ui.emailInput->widget()->text().trimmed(); } private: void checkAccept() { QStringList errors; if (ui.resultLabel->text().isEmpty() && !ui.nameInput->isRequired() && !ui.emailInput->isRequired()) { errors.push_back(i18n("Enter a name or an email address.")); } const auto nameError = ui.nameInput->currentError(); if (!nameError.isEmpty()) { errors.push_back(nameError); } const auto emailError = ui.emailInput->currentError(); if (!emailError.isEmpty()) { errors.push_back(emailError); } if (errors.size() > 1) { KMessageBox::errorList(q, i18n("There is a problem."), errors); } else if (!errors.empty()) { KMessageBox::error(q, errors.first()); } else { q->accept(); } } void updateResultLabel() { ui.resultLabel->setHtml(i18nc("@info", "
This is how the new user ID will be stored in the certificate:
" "
%1
", buildUserId(name(), email()).toHtmlEscaped())); } }; AddUserIDDialog::AddUserIDDialog(QWidget *parent, Qt::WindowFlags f) : QDialog{parent, f} , d(new Private{this}) { } AddUserIDDialog::~AddUserIDDialog() = default; void AddUserIDDialog::setName(const QString &name) { d->ui.nameInput->widget()->setText(name); } QString AddUserIDDialog::name() const { return d->name(); } void AddUserIDDialog::setEmail(const QString &email) { d->ui.emailInput->widget()->setText(email); } QString AddUserIDDialog::email() const { return d->email(); } QString AddUserIDDialog::userID() const { return buildUserId(name(), email()); } diff --git a/src/dialogs/certificatedetailsinputwidget.cpp b/src/dialogs/certificatedetailsinputwidget.cpp index bdae3ccde..801a3c23d 100644 --- a/src/dialogs/certificatedetailsinputwidget.cpp +++ b/src/dialogs/certificatedetailsinputwidget.cpp @@ -1,352 +1,349 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/certificatedetailsinputwidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "certificatedetailsinputwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Dialogs; namespace { struct Line { QString attr; QString label; QString regex; QLineEdit *edit; + std::shared_ptr validator; bool required; }; QString attributeFromKey(QString key) { return key.remove(QLatin1Char('!')); } QString attributeLabel(const QString &attr) { if (attr.isEmpty()) { return QString(); } const QString label = DN::attributeNameToLabel(attr); if (!label.isEmpty()) { return i18nc("Format string for the labels in the \"Your Personal Data\" page", "%1 (%2)", label, attr); } else { return attr; } } - QLineEdit * addRow(QGridLayout *l, const QString &label, const QString &preset, QValidator *validator, bool readonly, bool required) + QLineEdit * addRow(QGridLayout *l, const QString &label, const QString &preset, const std::shared_ptr &validator, bool readonly, bool required) { Q_ASSERT(l); auto lb = new QLabel(l->parentWidget()); lb->setText(i18nc("interpunctation for labels", "%1:", label)); auto le = new QLineEdit(l->parentWidget()); le->setText(preset); - delete le->validator(); if (validator) { - if (!validator->parent()) { - validator->setParent(le); - } - le->setValidator(validator); + le->setValidator(validator.get()); } le->setReadOnly(readonly && le->hasAcceptableInput()); auto reqLB = new QLabel(l->parentWidget()); reqLB->setText(required ? i18n("(required)") : i18n("(optional)")); const int row = l->rowCount(); l->addWidget(lb, row, 0); l->addWidget(le, row, 1); l->addWidget(reqLB, row, 2); return le; } bool hasIntermediateInput(const QLineEdit *le) { QString text = le->text(); int pos = le->cursorPosition(); const QValidator *const v = le->validator(); return v && v->validate(text, pos) == QValidator::Intermediate; } QString requirementsAreMet(const QVector &lines) { for (const Line &line : lines) { const QLineEdit *le = line.edit; if (!le) { continue; } qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking \"" << line.attr << "\" against \"" << le->text() << "\":"; if (le->text().trimmed().isEmpty()) { if (line.required) { if (line.regex.isEmpty()) { return xi18nc("@info", "%1 is required, but empty.", line.label); } else { return xi18nc("@info", "%1 is required, but empty." "Local Admin rule: %2", line.label, line.regex); } } } else if (hasIntermediateInput(le)) { if (line.regex.isEmpty()) { return xi18nc("@info", "%1 is incomplete.", line.label); } else { return xi18nc("@info", "%1 is incomplete." "Local Admin rule: %2", line.label, line.regex); } } else if (!le->hasAcceptableInput()) { if (line.regex.isEmpty()) { return xi18nc("@info", "%1 is invalid.", line.label); } else { return xi18nc("@info", "%1 is invalid." "Local Admin rule: %2", line.label, line.regex); } } } return QString(); } } class CertificateDetailsInputWidget::Private { friend class ::Kleo::Dialogs::CertificateDetailsInputWidget; CertificateDetailsInputWidget *const q; struct { QGridLayout *gridLayout; QVector lines; QLineEdit *dn; QLabel *error; } ui; public: Private(CertificateDetailsInputWidget *qq) : q(qq) { auto mainLayout = new QVBoxLayout(q); ui.gridLayout = new QGridLayout(); mainLayout->addLayout(ui.gridLayout); createForm(); mainLayout->addStretch(1); ui.dn = new QLineEdit(); ui.dn->setFrame(false); ui.dn->setAlignment(Qt::AlignCenter); ui.dn->setReadOnly(true); mainLayout->addWidget(ui.dn); ui.error = new QLabel(); { QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(ui.error->sizePolicy().hasHeightForWidth()); ui.error->setSizePolicy(sizePolicy); } { QPalette palette; QBrush brush(QColor(255, 0, 0, 255)); brush.setStyle(Qt::SolidPattern); palette.setBrush(QPalette::Active, QPalette::WindowText, brush); palette.setBrush(QPalette::Inactive, QPalette::WindowText, brush); QBrush brush1(QColor(114, 114, 114, 255)); brush1.setStyle(Qt::SolidPattern); palette.setBrush(QPalette::Disabled, QPalette::WindowText, brush1); ui.error->setPalette(palette); } ui.error->setTextFormat(Qt::RichText); // set error label to have a fixed height of two lines: ui.error->setText(QStringLiteral("2
1")); ui.error->setFixedHeight(ui.error->minimumSizeHint().height()); ui.error->clear(); mainLayout->addWidget(ui.error); // select the preset text in the first line edit if (!ui.lines.empty()) { ui.lines.first().edit->selectAll(); } // explicitly update DN and check requirements after setup is complete updateDN(); checkRequirements(); } ~Private() { // remember current attribute values as presets for next certificate KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard"); for ( const Line &line : ui.lines ) { const QString attr = attributeFromKey(line.attr); const QString value = line.edit->text().trimmed(); config.writeEntry(attr, value); } config.sync(); } void createForm() { const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard"); QStringList attrOrder = config.readEntry("DNAttributeOrder", QStringList()); if (attrOrder.empty()) { attrOrder << QStringLiteral("CN!") << QStringLiteral("EMAIL!") << QStringLiteral("L") << QStringLiteral("OU") << QStringLiteral("O") << QStringLiteral("C"); } for (const QString &rawKey : attrOrder) { const QString key = rawKey.trimmed().toUpper(); const QString attr = attributeFromKey(key); if (attr.isEmpty()) { continue; } const QString defaultPreset = (attr == QLatin1String("CN")) ? userFullName() : (attr == QLatin1String("EMAIL")) ? userEmailAddress() : QString(); const QString preset = config.readEntry(attr, defaultPreset); const bool required = key.endsWith(QLatin1Char('!')); const bool readonly = config.isEntryImmutable(attr); const QString label = config.readEntry(attr + QLatin1String("_label"), attributeLabel(attr)); const QString regex = config.readEntry(attr + QLatin1String("_regex")); - QValidator *validator = nullptr; + std::shared_ptr validator; if (attr == QLatin1String("EMAIL")) { validator = regex.isEmpty() ? Validation::email() : Validation::email(regex); } else if (!regex.isEmpty()) { - validator = new QRegularExpressionValidator{QRegularExpression{regex}, nullptr}; + validator = std::make_shared(QRegularExpression{regex}); } QLineEdit *le = addRow(ui.gridLayout, label, preset, validator, readonly, required); - const Line line = { attr, label, regex, le, required }; + const Line line = { attr, label, regex, le, validator, required }; ui.lines.push_back(line); if (attr != QLatin1String("EMAIL")) { connect(le, &QLineEdit::textChanged, [this] () { updateDN(); }); } connect(le, &QLineEdit::textChanged, [this] () { checkRequirements(); }); } } void updateDN() { ui.dn->setText(cmsDN()); } QString cmsDN() const { DN dn; for ( const Line &line : ui.lines ) { const QString text = line.edit->text().trimmed(); if (text.isEmpty()) { continue; } QString attr = attributeFromKey(line.attr); if (attr == QLatin1String("EMAIL")) { continue; } if (const char *const oid = oidForAttributeName(attr)) { attr = QString::fromUtf8(oid); } dn.append(DN::Attribute(attr, text)); } return dn.dn(); } void checkRequirements() { const QString error = requirementsAreMet(ui.lines); ui.error->setText(error); Q_EMIT q->validityChanged(error.isEmpty()); } QLineEdit * attributeWidget(const QString &attribute) { for ( const Line &line : ui.lines ) { if (attributeFromKey(line.attr) == attribute) { return line.edit; } } qCWarning(KLEOPATRA_LOG) << "CertificateDetailsInputWidget: No widget for attribute" << attribute; return nullptr; } void setAttributeValue(const QString &attribute, const QString &value) { QLineEdit *w = attributeWidget(attribute); if (w) { w->setText(value); } } QString attributeValue(const QString &attribute) { const QLineEdit *w = attributeWidget(attribute); return w ? w->text().trimmed() : QString(); } }; CertificateDetailsInputWidget::CertificateDetailsInputWidget(QWidget *parent) : QWidget(parent) , d(new Private(this)) { } CertificateDetailsInputWidget::~CertificateDetailsInputWidget() { } void CertificateDetailsInputWidget::setName(const QString &name) { d->setAttributeValue(QStringLiteral("CN"), name); } void CertificateDetailsInputWidget::setEmail(const QString &email) { d->setAttributeValue(QStringLiteral("EMAIL"), email); } QString CertificateDetailsInputWidget::email() const { return d->attributeValue(QStringLiteral("EMAIL")); } QString CertificateDetailsInputWidget::dn() const { return d->ui.dn->text(); } diff --git a/src/newcertificatewizard/enterdetailspage.cpp b/src/newcertificatewizard/enterdetailspage.cpp index d41db204c..120375648 100644 --- a/src/newcertificatewizard/enterdetailspage.cpp +++ b/src/newcertificatewizard/enterdetailspage.cpp @@ -1,541 +1,537 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/enterdetailspage.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "enterdetailspage_p.h" #include "advancedsettingsdialog_p.h" #include "utils/scrollarea.h" #include "utils/userinfo.h" #include "utils/validation.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::NewCertificateUi; using namespace GpgME; static void set_tab_order(const QList &wl) { kdtools::for_each_adjacent_pair(wl.begin(), wl.end(), &QWidget::setTabOrder); } static QString pgpLabel(const QString &attr) { if (attr == QLatin1String("NAME")) { return i18n("Name"); } if (attr == QLatin1String("EMAIL")) { return i18n("EMail"); } return QString(); } static QString attributeLabel(const QString &attr, bool pgp) { if (attr.isEmpty()) { return QString(); } const QString label = pgp ? pgpLabel(attr) : Kleo::DN::attributeNameToLabel(attr); if (!label.isEmpty()) if (pgp) { return label; } else return i18nc("Format string for the labels in the \"Your Personal Data\" page", "%1 (%2)", label, attr); else { return attr; } } static QString attributeFromKey(QString key) { return key.remove(QLatin1Char('!')); } struct EnterDetailsPage::UI { QGridLayout *gridLayout = nullptr; QLabel *nameLB = nullptr; QLineEdit *nameLE = nullptr; QLabel *nameRequiredLB = nullptr; QLabel *emailLB = nullptr; QLineEdit *emailLE = nullptr; QLabel *emailRequiredLB = nullptr; QCheckBox *withPassCB = nullptr; QLineEdit *resultLE = nullptr; QLabel *errorLB = nullptr; QPushButton *advancedPB = nullptr; UI(QWizardPage *parent) { parent->setTitle(i18nc("@title", "Enter Details")); auto mainLayout = new QVBoxLayout{parent}; const auto margins = mainLayout->contentsMargins(); mainLayout->setContentsMargins(margins.left(), 0, margins.right(), 0); auto scrollArea = new ScrollArea{parent}; scrollArea->setFocusPolicy(Qt::NoFocus); scrollArea->setFrameStyle(QFrame::NoFrame); scrollArea->setBackgroundRole(parent->backgroundRole()); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents); auto scrollAreaLayout = qobject_cast(scrollArea->widget()->layout()); scrollAreaLayout->setContentsMargins(0, margins.top(), 0, margins.bottom()); gridLayout = new QGridLayout; int row = 0; nameLB = new QLabel{i18n("Real name:"), parent}; nameLE = new QLineEdit{parent}; nameRequiredLB = new QLabel{i18n("(required)"), parent}; gridLayout->addWidget(nameLB, row, 0, 1, 1); gridLayout->addWidget(nameLE, row, 1, 1, 1); gridLayout->addWidget(nameRequiredLB, row, 2, 1, 1); row++; emailLB = new QLabel{i18n("EMail address:"), parent}; emailLE = new QLineEdit{parent}; emailRequiredLB = new QLabel{i18n("(required)"), parent}; gridLayout->addWidget(emailLB, row, 0, 1, 1); gridLayout->addWidget(emailLE, row, 1, 1, 1); gridLayout->addWidget(emailRequiredLB, row, 2, 1, 1); row++; withPassCB = new QCheckBox{i18n("Protect the generated key with a passphrase."), parent}; withPassCB->setToolTip(i18n("Encrypts the secret key with an unrecoverable passphrase. You will be asked for the passphrase during key generation.")); gridLayout->addWidget(withPassCB, row, 1, 1, 2); scrollAreaLayout->addLayout(gridLayout); auto verticalSpacer = new QSpacerItem{20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding}; scrollAreaLayout->addItem(verticalSpacer); resultLE = new QLineEdit{parent}; resultLE->setFrame(false); resultLE->setAlignment(Qt::AlignCenter); resultLE->setReadOnly(true); scrollAreaLayout->addWidget(resultLE); auto horizontalLayout = new QHBoxLayout; errorLB = new QLabel{parent}; QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(errorLB->sizePolicy().hasHeightForWidth()); errorLB->setSizePolicy(sizePolicy); QPalette palette; QBrush brush(QColor(255, 0, 0, 255)); brush.setStyle(Qt::SolidPattern); palette.setBrush(QPalette::Active, QPalette::WindowText, brush); palette.setBrush(QPalette::Inactive, QPalette::WindowText, brush); QBrush brush1(QColor(114, 114, 114, 255)); brush1.setStyle(Qt::SolidPattern); palette.setBrush(QPalette::Disabled, QPalette::WindowText, brush1); errorLB->setPalette(palette); errorLB->setTextFormat(Qt::RichText); horizontalLayout->addWidget(errorLB); advancedPB = new QPushButton{i18n("Advanced Settings..."), parent}; advancedPB->setAutoDefault(false); horizontalLayout->addWidget(advancedPB); scrollAreaLayout->addLayout(horizontalLayout); mainLayout->addWidget(scrollArea); } }; EnterDetailsPage::EnterDetailsPage(QWidget *p) : WizardPage{p} , ui{new UI{this}} , dialog{new AdvancedSettingsDialog{this}} { setObjectName(QStringLiteral("Kleo__NewCertificateUi__EnterDetailsPage")); Settings settings; if (settings.hideAdvanced()) { setSubTitle(i18n("Please enter your personal details below.")); } else { setSubTitle(i18n("Please enter your personal details below. If you want more control over the parameters, click on the Advanced Settings button.")); } ui->advancedPB->setVisible(!settings.hideAdvanced()); ui->resultLE->setFocusPolicy(Qt::NoFocus); // set errorLB to have a fixed height of two lines: ui->errorLB->setText(QStringLiteral("2
1")); ui->errorLB->setFixedHeight(ui->errorLB->minimumSizeHint().height()); ui->errorLB->clear(); connect(ui->advancedPB, &QPushButton::clicked, this, &EnterDetailsPage::slotAdvancedSettingsClicked); connect(ui->resultLE, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); // The email doesn't necessarily show up in ui->resultLE: connect(ui->emailLE, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); registerDialogPropertiesAsFields(); registerField(QStringLiteral("dn"), ui->resultLE); registerField(QStringLiteral("name"), ui->nameLE); registerField(QStringLiteral("email"), ui->emailLE); registerField(QStringLiteral("protectedKey"), ui->withPassCB); updateForm(); setCommitPage(true); setButtonText(QWizard::CommitButton, i18nc("@action", "Create")); const auto conf = QGpgME::cryptoConfig(); if (!conf) { qCWarning(KLEOPATRA_LOG) << "Failed to obtain cryptoConfig."; return; } const auto entry = getCryptoConfigEntry(conf, "gpg-agent", "enforce-passphrase-constraints"); if (entry && entry->boolValue()) { qCDebug(KLEOPATRA_LOG) << "Disabling passphrace cb because of agent config."; ui->withPassCB->setEnabled(false); ui->withPassCB->setChecked(true); } else { const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard"); ui->withPassCB->setChecked(config.readEntry("WithPassphrase", false)); ui->withPassCB->setEnabled(!config.isEntryImmutable("WithPassphrase")); } } EnterDetailsPage::~EnterDetailsPage() = default; void EnterDetailsPage::initializePage() { updateForm(); ui->withPassCB->setVisible(pgp()); dialog->setProtocol(pgp() ? OpenPGP : CMS); } void EnterDetailsPage::cleanupPage() { saveValues(); // reset protocol when navigating back to "Choose Protocol" page resetProtocol(); } void EnterDetailsPage::registerDialogPropertiesAsFields() { const QMetaObject *const mo = dialog->metaObject(); for (unsigned int i = mo->propertyOffset(), end = i + mo->propertyCount(); i != end; ++i) { const QMetaProperty mp = mo->property(i); if (mp.isValid()) { registerField(QLatin1String(mp.name()), dialog, mp.name(), SIGNAL(accepted())); } } } void EnterDetailsPage::saveValues() { for (const Line &line : std::as_const(lineList)) { savedValues[ attributeFromKey(line.attr) ] = line.edit->text().trimmed(); } } void EnterDetailsPage::clearForm() { qDeleteAll(dynamicWidgets); dynamicWidgets.clear(); lineList.clear(); ui->nameLE->hide(); ui->nameLE->clear(); ui->nameLB->hide(); ui->nameRequiredLB->hide(); ui->emailLE->hide(); ui->emailLE->clear(); ui->emailLB->hide(); ui->emailRequiredLB->hide(); } static int row_index_of(QWidget *w, QGridLayout *l) { const int idx = l->indexOf(w); int r, c, rs, cs; l->getItemPosition(idx, &r, &c, &rs, &cs); return r; } -static QLineEdit *adjust_row(QGridLayout *l, int row, const QString &label, const QString &preset, QValidator *validator, bool readonly, bool required) +static QLineEdit *adjust_row(QGridLayout *l, int row, const QString &label, const QString &preset, const std::shared_ptr &validator, bool readonly, bool required) { Q_ASSERT(l); Q_ASSERT(row >= 0); Q_ASSERT(row < l->rowCount()); auto lb = qobject_cast(l->itemAtPosition(row, 0)->widget()); Q_ASSERT(lb); auto le = qobject_cast(l->itemAtPosition(row, 1)->widget()); Q_ASSERT(le); lb->setBuddy(le); // For better accessibility auto reqLB = qobject_cast(l->itemAtPosition(row, 2)->widget()); Q_ASSERT(reqLB); lb->setText(i18nc("interpunctation for labels", "%1:", label)); le->setText(preset); reqLB->setText(required ? i18n("(required)") : i18n("(optional)")); - delete le->validator(); if (validator) { - if (!validator->parent()) { - validator->setParent(le); - } - le->setValidator(validator); + le->setValidator(validator.get()); } le->setReadOnly(readonly && le->hasAcceptableInput()); lb->show(); le->show(); reqLB->show(); return le; } static int add_row(QGridLayout *l, QList *wl) { Q_ASSERT(l); Q_ASSERT(wl); const int row = l->rowCount(); QWidget *w1, *w2, *w3; l->addWidget(w1 = new QLabel(l->parentWidget()), row, 0); l->addWidget(w2 = new QLineEdit(l->parentWidget()), row, 1); l->addWidget(w3 = new QLabel(l->parentWidget()), row, 2); wl->push_back(w1); wl->push_back(w2); wl->push_back(w3); return row; } void EnterDetailsPage::updateForm() { clearForm(); const auto settings = Kleo::Settings{}; const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard"); QStringList attrOrder = config.readEntry(pgp() ? "OpenPGPAttributeOrder" : "DNAttributeOrder", QStringList()); if (attrOrder.empty()) { if (pgp()) { attrOrder << QStringLiteral("NAME") << QStringLiteral("EMAIL"); } else { attrOrder << QStringLiteral("CN!") << QStringLiteral("L") << QStringLiteral("OU") << QStringLiteral("O") << QStringLiteral("C") << QStringLiteral("EMAIL!"); } } QList widgets; widgets.push_back(ui->nameLE); widgets.push_back(ui->emailLE); QMap lines; for (const QString &rawKey : std::as_const(attrOrder)) { const QString key = rawKey.trimmed().toUpper(); const QString attr = attributeFromKey(key); if (attr.isEmpty()) { continue; } const QString preset = savedValues.value(attr, config.readEntry(attr, QString())); const bool required = key.endsWith(QLatin1Char('!')); const bool readonly = config.isEntryImmutable(attr); const QString label = config.readEntry(attr + QLatin1String("_label"), attributeLabel(attr, pgp())); const QString regex = config.readEntry(attr + QLatin1String("_regex")); const QString placeholder = config.readEntry(attr + QLatin1String{"_placeholder"}); int row; bool known = true; - QValidator *validator = nullptr; + std::shared_ptr validator; if (attr == QLatin1String("EMAIL")) { row = row_index_of(ui->emailLE, ui->gridLayout); validator = regex.isEmpty() ? Validation::email() : Validation::email(regex); } else if (attr == QLatin1String("NAME") || attr == QLatin1String("CN")) { if ((pgp() && attr == QLatin1String("CN")) || (!pgp() && attr == QLatin1String("NAME"))) { continue; } if (pgp()) { validator = regex.isEmpty() ? Validation::pgpName() : Validation::pgpName(regex); } row = row_index_of(ui->nameLE, ui->gridLayout); } else { known = false; row = add_row(ui->gridLayout, &dynamicWidgets); } if (!validator && !regex.isEmpty()) { - validator = new QRegularExpressionValidator{QRegularExpression{regex}, nullptr}; + validator = std::make_shared(QRegularExpression{regex}); } QLineEdit *le = adjust_row(ui->gridLayout, row, label, preset, validator, readonly, required); le->setPlaceholderText(placeholder); - const Line line = { key, label, regex, le }; + const Line line = { key, label, regex, le, validator }; lines[row] = line; if (!known) { widgets.push_back(le); } // don't connect twice: disconnect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel); connect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel); } // create lineList in visual order, so requirementsAreMet() // complains from top to bottom: lineList.reserve(lines.count()); std::copy(lines.cbegin(), lines.cend(), std::back_inserter(lineList)); widgets.push_back(ui->withPassCB); widgets.push_back(ui->advancedPB); const bool prefillName = (pgp() && settings.prefillName()) || (!pgp() && settings.prefillCN()); if (ui->nameLE->text().isEmpty() && prefillName) { ui->nameLE->setText(userFullName()); } if (ui->emailLE->text().isEmpty() && settings.prefillEmail()) { ui->emailLE->setText(userEmailAddress()); } slotUpdateResultLabel(); set_tab_order(widgets); } QString EnterDetailsPage::cmsDN() const { DN dn; for (QVector::const_iterator it = lineList.begin(), end = lineList.end(); it != end; ++it) { const QString text = it->edit->text().trimmed(); if (text.isEmpty()) { continue; } QString attr = attributeFromKey(it->attr); if (attr == QLatin1String("EMAIL")) { continue; } if (const char *const oid = oidForAttributeName(attr)) { attr = QString::fromUtf8(oid); } dn.append(DN::Attribute(attr, text)); } return dn.dn(); } QString EnterDetailsPage::pgpUserID() const { return Formatting::prettyNameAndEMail(OpenPGP, QString(), ui->nameLE->text().trimmed(), ui->emailLE->text().trimmed(), QString()); } static bool has_intermediate_input(const QLineEdit *le) { QString text = le->text(); int pos = le->cursorPosition(); const QValidator *const v = le->validator(); return v && v->validate(text, pos) == QValidator::Intermediate; } static bool requirementsAreMet(const QVector &list, QString &error) { bool allEmpty = true; for (const auto &line : list) { const QLineEdit *le = line.edit; if (!le) { continue; } const QString key = line.attr; qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking" << key << "against" << le->text() << ":"; if (le->text().trimmed().isEmpty()) { if (key.endsWith(QLatin1Char('!'))) { if (line.regex.isEmpty()) { error = xi18nc("@info", "%1 is required, but empty.", line.label); } else error = xi18nc("@info", "%1 is required, but empty." "Local Admin rule: %2", line.label, line.regex); return false; } } else if (has_intermediate_input(le)) { if (line.regex.isEmpty()) { error = xi18nc("@info", "%1 is incomplete.", line.label); } else error = xi18nc("@info", "%1 is incomplete." "Local Admin rule: %2", line.label, line.regex); return false; } else if (!le->hasAcceptableInput()) { if (line.regex.isEmpty()) { error = xi18nc("@info", "%1 is invalid.", line.label); } else error = xi18nc("@info", "%1 is invalid." "Local Admin rule: %2", line.label, line.regex); return false; } else { allEmpty = false; } } // Ensure that at least one value is acceptable return !allEmpty; } bool EnterDetailsPage::isComplete() const { QString error; const bool ok = requirementsAreMet(lineList, error); ui->errorLB->setText(error); return ok; } void EnterDetailsPage::slotAdvancedSettingsClicked() { dialog->exec(); } void EnterDetailsPage::slotUpdateResultLabel() { ui->resultLE->setText(pgp() ? pgpUserID() : cmsDN()); } diff --git a/src/newcertificatewizard/enterdetailspage_p.h b/src/newcertificatewizard/enterdetailspage_p.h index ab9ebfc18..eb86257ab 100644 --- a/src/newcertificatewizard/enterdetailspage_p.h +++ b/src/newcertificatewizard/enterdetailspage_p.h @@ -1,62 +1,64 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/enterdetailspage_p.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "wizardpage_p.h" class AdvancedSettingsDialog; class QLineEdit; +class QValidator; class EnterDetailsPage : public Kleo::NewCertificateUi::WizardPage { Q_OBJECT public: struct Line { QString attr; QString label; QString regex; QLineEdit *edit; + std::shared_ptr validator; }; explicit EnterDetailsPage(QWidget *parent = nullptr); ~EnterDetailsPage() override; bool isComplete() const override; void initializePage() override; void cleanupPage() override; private: void updateForm(); void clearForm(); void saveValues(); void registerDialogPropertiesAsFields(); private: QString pgpUserID() const; QString cmsDN() const; private Q_SLOTS: void slotAdvancedSettingsClicked(); void slotUpdateResultLabel(); private: struct UI; std::unique_ptr ui; QVector lineList; QList dynamicWidgets; QMap savedValues; AdvancedSettingsDialog *dialog; }; diff --git a/src/utils/multivalidator.cpp b/src/utils/multivalidator.cpp index ac2222950..499cc1bf1 100644 --- a/src/utils/multivalidator.cpp +++ b/src/utils/multivalidator.cpp @@ -1,91 +1,67 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/multivalidator.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB + SPDX-FileCopyrightText: 2022 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "multivalidator.h" -#include - -#include +#include #include using namespace Kleo; -MultiValidator::~MultiValidator() {} - -void MultiValidator::addValidator(QValidator *vali) +MultiValidator::MultiValidator(const std::vector> &validators) + : QValidator{} + , m_validators{validators} { - if (!vali) { - return; - } - if (!vali->parent()) { - vali->setParent(this); - } - connect(vali, &QObject::destroyed, this, &MultiValidator::_kdmv_slotDestroyed); - m_validators.push_back(vali); } -void MultiValidator::addValidators(const QList &valis) +// static +std::shared_ptr Kleo::MultiValidator::create(const std::vector> &validators) { - std::for_each(valis.cbegin(), valis.cend(), - [this](QValidator *val) { addValidator(val); }); -} + Q_ASSERT(std::all_of(std::begin(validators), std::end(validators), [](const auto &v) { + return v && !v->parent(); + })); -void MultiValidator::removeValidator(QValidator *vali) -{ - if (!vali) { - return; - } - _kdmv_slotDestroyed(vali); - if (vali->parent() == this) { - delete vali; - } else { - disconnect(vali, &QObject::destroyed, this, &MultiValidator::_kdmv_slotDestroyed); - } + return std::shared_ptr{new MultiValidator{validators}}; } -void MultiValidator::removeValidators(const QList &valis) -{ - std::for_each(valis.cbegin(), valis.cend(), - [this](QValidator *val) { removeValidator(val); }); -} +MultiValidator::~MultiValidator() = default; void MultiValidator::fixup(QString &str) const { - std::for_each(m_validators.begin(), m_validators.end(), - [&str](QValidator *val) { val->fixup(str); }); + std::for_each(std::cbegin(m_validators), std::cend(m_validators), [&str](const auto &val) { + val->fixup(str); + }); } QValidator::State MultiValidator::validate(QString &str, int &pos) const { std::vector states; states.reserve(m_validators.size()); - std::transform(m_validators.begin(), m_validators.end(), + std::transform(std::cbegin(m_validators), std::cend(m_validators), std::back_inserter(states), - [&str, &pos](QValidator *val) { + [&str, &pos](const auto &val) { return val->validate(str, pos); }); - if (std::any_of(states.cbegin(), states.cend(), + + if (std::any_of(std::cbegin(states), std::cend(states), [](State state) { return state == Invalid; })) { return Invalid; } - if (std::all_of(states.cbegin(), states.cend(), + + if (std::all_of(std::cbegin(states), std::cend(states), [](State state) { return state == Acceptable; })) { return Acceptable; } - return Intermediate; -} -void MultiValidator::_kdmv_slotDestroyed(QObject *o) -{ - m_validators.erase(std::remove(m_validators.begin(), m_validators.end(), o), - m_validators.end()); + return Intermediate; } - diff --git a/src/utils/multivalidator.h b/src/utils/multivalidator.h index 2d29217dc..80e77137e 100644 --- a/src/utils/multivalidator.h +++ b/src/utils/multivalidator.h @@ -1,54 +1,45 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/multivalidator.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB + SPDX-FileCopyrightText: 2022 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include -#include + +#include +#include namespace Kleo { class MultiValidator : public QValidator { Q_OBJECT -public: - explicit MultiValidator(QObject *parent = nullptr) - : QValidator(parent) {} - explicit MultiValidator(QValidator *validator1, QValidator *validator2 = nullptr, QObject *parent = nullptr) - : QValidator(parent) - { - addValidator(validator1); - addValidator(validator2); - } - explicit MultiValidator(const QList &validators, QObject *parent = nullptr) - : QValidator(parent) - { - addValidators(validators); - } - ~MultiValidator() override; - void addValidator(QValidator *validator); - void addValidators(const QList &validators); + explicit MultiValidator(const std::vector> &validators); + +public: + /** + * Creates a combined validator from the \p validators. + * + * The validators must not be null and they must not have a parent. + */ + static std::shared_ptr create(const std::vector> &validators); - void removeValidator(QValidator *validator); - void removeValidators(const QList &validators); + ~MultiValidator() override; void fixup(QString &str) const override; State validate(QString &str, int &pos) const override; -private Q_SLOTS: - void _kdmv_slotDestroyed(QObject *); - private: - QList m_validators; + std::vector> m_validators; }; } - diff --git a/src/utils/validation.cpp b/src/utils/validation.cpp index d0f20dea2..7e7b116c8 100644 --- a/src/utils/validation.cpp +++ b/src/utils/validation.cpp @@ -1,118 +1,120 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/validation.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB + SPDX-FileCopyrightText: 2022 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "validation.h" #include #include #include "kleopatra_debug.h" #include using namespace Kleo; namespace { template class TrimmingValidator : public Validator { public: using Validator::Validator; QValidator::State validate(QString &str, int &pos) const override { auto trimmed = str.trimmed(); auto posCopy = pos; return Validator::validate(trimmed, posCopy); } }; template class EmptyIsAcceptableValidator : public Validator { public: using Validator::Validator; QValidator::State validate(QString &str, int &pos) const override { if (str.isEmpty()) { return QValidator::Acceptable; } return Validator::validate(str, pos); } }; class EMailValidator : public QValidator { public: - explicit EMailValidator(QObject *parent = nullptr) : QValidator(parent) {} + EMailValidator() : QValidator{} {} State validate(QString &str, int &pos) const override { Q_UNUSED(pos) if (KEmailAddress::isValidSimpleAddress(str)) { return Acceptable; } return Intermediate; } }; -QValidator *regularExpressionValidator(Validation::Flags flags, const QString ®exp, QObject *parent) +std::shared_ptr regularExpressionValidator(Validation::Flags flags, const QString ®exp) { if (flags & Validation::Required) { - return new TrimmingValidator{QRegularExpression{regexp}, parent}; + return std::make_shared>(QRegularExpression{regexp}); } else { - return new TrimmingValidator>{QRegularExpression{regexp}, parent}; + return std::make_shared>>(QRegularExpression{regexp}); } } } -QValidator *Validation::email(Flags flags, QObject *parent) +std::shared_ptr Validation::email(Flags flags) { if (flags & Required) { - return new TrimmingValidator{parent}; + return std::make_shared>(); } else { - return new TrimmingValidator>{parent}; + return std::make_shared>>(); } } -QValidator *Validation::email(const QString &addRX, Flags flags, QObject *parent) +std::shared_ptr Validation::email(const QString &addRX, Flags flags) { - return new MultiValidator{email(flags), regularExpressionValidator(flags, addRX, nullptr), parent}; + return MultiValidator::create({email(flags), regularExpressionValidator(flags, addRX)}); } -QValidator *Validation::pgpName(Flags flags, QObject *parent) +std::shared_ptr Validation::pgpName(Flags flags) { // this regular expression is modeled after gnupg/g10/keygen.c:ask_user_id: static const QString name_rx{QLatin1String{"[^0-9<>][^<>@]{4,}"}}; - return regularExpressionValidator(flags, name_rx, parent); + return regularExpressionValidator(flags, name_rx); } -QValidator *Validation::pgpName(const QString &addRX, Flags flags, QObject *parent) +std::shared_ptr Validation::pgpName(const QString &addRX, Flags flags) { - return new MultiValidator{pgpName(flags), regularExpressionValidator(flags, addRX, nullptr), parent}; + return MultiValidator::create({pgpName(flags), regularExpressionValidator(flags, addRX)}); } -QValidator *Validation::simpleName(Flags flags, QObject *parent) +std::shared_ptr Validation::simpleName(Flags flags) { static const QString name_rx{QLatin1String{"[^<>@]*"}}; - return regularExpressionValidator(flags, name_rx, parent); + return std::shared_ptr{regularExpressionValidator(flags, name_rx)}; } -QValidator *Validation::simpleName(const QString &additionalRegExp, Flags flags, QObject *parent) +std::shared_ptr Validation::simpleName(const QString &additionalRegExp, Flags flags) { - return new MultiValidator{simpleName(flags), regularExpressionValidator(flags, additionalRegExp, nullptr), parent}; + return MultiValidator::create({simpleName(flags), regularExpressionValidator(flags, additionalRegExp)}); } diff --git a/src/utils/validation.h b/src/utils/validation.h index d7c670e4d..c7406c2c3 100644 --- a/src/utils/validation.h +++ b/src/utils/validation.h @@ -1,53 +1,56 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/validation.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB + SPDX-FileCopyrightText: 2022 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once -class QObject; +#include + class QString; class QValidator; namespace Kleo { namespace Validation { enum Flags { Optional, Required }; -QValidator *email(Flags flags = Required, QObject *parent = nullptr); +std::shared_ptr email(Flags flags = Required); /** * Creates a validator for the name part of the user ID of an OpenPGP key with * restrictions that are necessary for usage with the edit-key interface. */ -QValidator *pgpName(Flags flags = Required, QObject *parent = nullptr); +std::shared_ptr pgpName(Flags flags = Required); /** * Creates a validator for the name part of the user ID of an OpenPGP key with * less restrictions than \ref pgpName. */ -QValidator *simpleName(Flags flags = Required, QObject *parent = nullptr); +std::shared_ptr simpleName(Flags flags = Required); -QValidator *email(const QString &additionalRegExp, Flags flags = Required, QObject *parent = nullptr); +std::shared_ptr email(const QString &additionalRegExp, Flags flags = Required); /** * Creates a validator for the name part of the user ID of an OpenPGP key with * restrictions that are necessary for usage with the edit-key interface, and * with additional restrictions imposed by \p additionalRegExp. */ -QValidator *pgpName(const QString &additionalRegExp, Flags flags = Required, QObject *parent = nullptr); +std::shared_ptr pgpName(const QString &additionalRegExp, Flags flags = Required); /** * Creates a validator for the name part of the user ID of an OpenPGP key with * less restrictions than \ref pgpName, but with additional restrictions imposed * by \p additionalRegExp. */ -QValidator *simpleName(const QString &additionalRegExp, Flags flags = Required, QObject *parent = nullptr); +std::shared_ptr simpleName(const QString &additionalRegExp, Flags flags = Required); } }