diff --git a/src/dialogs/adduseriddialog.cpp b/src/dialogs/adduseriddialog.cpp index a69db08ce..f320cea70 100644 --- a/src/dialogs/adduseriddialog.cpp +++ b/src/dialogs/adduseriddialog.cpp @@ -1,232 +1,225 @@ /* -*- 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 "view/errorlabel.h" #include "view/formtextinput.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 { std::unique_ptr> nameInput; std::unique_ptr> emailInput; QLabel *resultLabel; QDialogButtonBox *buttonBox; } ui; public: explicit Private(AddUserIDDialog *qq) : q{qq} { q->setWindowTitle(i18nc("title:window", "Add User ID")); auto mainLayout = new QVBoxLayout{q}; mainLayout->addWidget(new QLabel{i18n("Enter a name and/or an email address to use for the user ID."), q}); auto gridLayout = new QGridLayout; int row = -1; const KConfigGroup config{KSharedConfig::openConfig(), "CertificateCreationWizard"}; { ui.nameInput = FormTextInput::create(q); ui.nameInput->label()->setText(i18nc("@label", "Name:")); const auto regexp = config.readEntry(QLatin1String("NAME_regex")); - ui.nameInput->setValidator(regexp.isEmpty() ? Validation::pgpName(Validation::Optional, q) - : Validation::pgpName(regexp, Validation::Optional, q)); + ui.nameInput->setValidator(regexp.isEmpty() ? Validation::simpleName(Validation::Optional, q) + : Validation::simpleName(regexp, Validation::Optional, q)); const auto additionalRule = regexp.isEmpty() ? QString{} : i18n("Additionally, the name must adhere to rules set by your organization."); ui.nameInput->setToolTip(xi18n( - "If a name is given, then it has to satisfy the following rules:" - "" - "The name must be at least 5 characters long." - "The first character must not be a digit." - "The name must not contain any of the following characters: <, >, @." - "" - "%1" - "", + "The name must not contain any of the following characters: <, >, @. %1", additionalRule)); ui.nameInput->setErrorMessage(i18n("Error: The entered name is not valid.")); row++; gridLayout->addWidget(ui.nameInput->label(), row, 0, 1, 1); gridLayout->addWidget(ui.nameInput->widget(), row, 1, 1, 1); row++; gridLayout->addWidget(ui.nameInput->errorLabel(), row, 0, 1, 2); } connect(ui.nameInput->widget(), &QLineEdit::textChanged, q, [this]() { updateResultLabel(); }); { ui.emailInput = FormTextInput::create(q); ui.emailInput->label()->setText(i18nc("@label", "Email:")); const auto regexp = config.readEntry(QLatin1String("EMAIL_regex")); ui.emailInput->setValidator(regexp.isEmpty() ? Validation::email(Validation::Optional, q) : Validation::email(regexp, Validation::Optional, q)); if (regexp.isEmpty()) { ui.emailInput->setToolTip(xi18n("If an email address is given, then it has to be a valid address.")); } else { ui.emailInput->setToolTip(xi18n("If an email address is given, then it has to be a valid address " "and it must satisfy to rules set by your organization.")); } ui.emailInput->setErrorMessage(i18n("Error: The entered email address is not valid.")); row++; gridLayout->addWidget(ui.emailInput->label(), row, 0, 1, 1); gridLayout->addWidget(ui.emailInput->widget(), row, 1, 1, 1); row++; gridLayout->addWidget(ui.emailInput->errorLabel(), row, 0, 1, 2); } connect(ui.emailInput->widget(), &QLineEdit::textChanged, q, [this]() { updateResultLabel(); }); mainLayout->addLayout(gridLayout); mainLayout->addWidget(new KSeparator{Qt::Horizontal, q}); { auto label = new QLabel{i18n("This is how the new user ID will be stored in the certificate:"), q}; mainLayout->addWidget(label); } { ui.resultLabel = new QLabel{q}; ui.resultLabel->setMinimumSize(300, 0); ui.resultLabel->setAlignment(Qt::AlignCenter); ui.resultLabel->setTextFormat(Qt::PlainText); QFont font; font.setBold(true); font.setWeight(75); ui.resultLabel->setFont(font); mainLayout->addWidget(ui.resultLabel); } mainLayout->addWidget(new KSeparator{Qt::Horizontal, q}); mainLayout->addStretch(1); 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()) { errors.push_back(i18n("Name and email address cannot both be empty.")); } if (!ui.nameInput->hasAcceptableInput()) { errors.push_back(i18n("The entered name is not valid.")); } if (!ui.emailInput->hasAcceptableInput()) { errors.push_back(i18n("The entered email address is not valid.")); } if (errors.size() > 1) { KMessageBox::errorList(q, i18n("Sorry, the entered data is not acceptable."), errors); } else if (!errors.empty()) { KMessageBox::sorry(q, errors.first()); } else { q->accept(); } } void updateResultLabel() { ui.resultLabel->setText(buildUserId(name(), email())); } }; 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 d->ui.resultLabel->text(); } diff --git a/src/utils/validation.cpp b/src/utils/validation.cpp index 31ec9b455..656147814 100644 --- a/src/utils/validation.cpp +++ b/src/utils/validation.cpp @@ -1,119 +1,130 @@ /* -*- 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-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) {} 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) { if (flags & Validation::Required) { return new TrimmingValidator{QRegularExpression{regexp}, parent}; } else { return new TrimmingValidator>{QRegularExpression{regexp}, parent}; } } } QValidator *Validation::email(Flags flags, QObject *parent) { if (flags & Required) { return new TrimmingValidator{parent}; } else { return new TrimmingValidator>{parent}; } } QValidator *Validation::email(const QString &addRX, Flags flags, QObject *parent) { return new MultiValidator{email(flags), regularExpressionValidator(flags, addRX, nullptr), parent}; } QValidator *Validation::pgpName(Flags flags, QObject *parent) { // 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); } QValidator *Validation::pgpName(const QString &addRX, Flags flags, QObject *parent) { return new MultiValidator{pgpName(flags), regularExpressionValidator(flags, addRX, nullptr), parent}; } +QValidator *Validation::simpleName(Flags flags, QObject *parent) +{ + static const QString name_rx{QLatin1String{"[^<>@]*"}}; + return regularExpressionValidator(flags, name_rx, parent); +} + +QValidator *Validation::simpleName(const QString &additionalRegExp, Flags flags, QObject *parent) +{ + return new MultiValidator{simpleName(flags), regularExpressionValidator(flags, additionalRegExp, nullptr), parent}; +} + QValidator *Validation::pgpComment(Flags flags, QObject *parent) { // this regular expression is modeled after gnupg/g10/keygen.c:ask_user_id: static const QString comment_rx{QLatin1String{"[^()]*"}}; return regularExpressionValidator(flags, comment_rx, parent); } QValidator *Validation::pgpComment(const QString &addRX, Flags flags, QObject *parent) { return new MultiValidator{pgpComment(flags), regularExpressionValidator(flags, addRX, nullptr), parent}; } diff --git a/src/utils/validation.h b/src/utils/validation.h index 3b76abc60..6d9f33d1e 100644 --- a/src/utils/validation.h +++ b/src/utils/validation.h @@ -1,35 +1,55 @@ /* -*- 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-License-Identifier: GPL-2.0-or-later */ #pragma once class QObject; class QString; class QValidator; namespace Kleo { namespace Validation { enum Flags { Optional, Required }; QValidator *email(Flags flags = Required, QObject *parent = nullptr); +/** + * 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); +/** + * 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); QValidator *pgpComment(Flags flags = Required, QObject *parent = nullptr); QValidator *email(const QString &additionalRegExp, Flags flags = Required, QObject *parent = nullptr); +/** + * 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); +/** + * 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); QValidator *pgpComment(const QString &additionalRegExp, Flags flags = Required, QObject *parent = nullptr); } }