File indexing completed on 2024-06-23 05:14:04

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     dialogs/expirydialog.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
0006     SPDX-FileCopyrightText: 2021 g10 Code GmbH
0007     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include <config-kleopatra.h>
0013 
0014 #include "expirydialog.h"
0015 
0016 #include "utils/accessibility.h"
0017 #include "utils/expiration.h"
0018 #include "utils/gui-helper.h"
0019 
0020 #include <Libkleo/Formatting>
0021 
0022 #include <KDateComboBox>
0023 #include <KLocalizedString>
0024 #include <KMessageBox>
0025 #include <KStandardGuiItem>
0026 
0027 #include <QCheckBox>
0028 #include <QDate>
0029 #include <QDialogButtonBox>
0030 #include <QHBoxLayout>
0031 #include <QLabel>
0032 #include <QPushButton>
0033 #include <QRadioButton>
0034 #include <QVBoxLayout>
0035 
0036 using namespace Kleo;
0037 using namespace Kleo::Dialogs;
0038 
0039 class ExpiryDialog::Private
0040 {
0041     friend class ::Kleo::Dialogs::ExpiryDialog;
0042     ExpiryDialog *const q;
0043 
0044 public:
0045     Private(Mode mode, ExpiryDialog *qq)
0046         : q{qq}
0047         , ui{mode, qq}
0048     {
0049         ui.neverRB->setEnabled(unlimitedValidityAllowed());
0050         ui.onRB->setEnabled(!fixedExpirationDate());
0051 
0052         connect(ui.onCB, &KDateComboBox::dateChanged, q, [this]() {
0053             slotOnDateChanged();
0054         });
0055     }
0056 
0057 private:
0058     void slotOnDateChanged();
0059 
0060 private:
0061     bool unlimitedValidityAllowed() const;
0062     bool fixedExpirationDate() const;
0063     void setInitialFocus();
0064 
0065 private:
0066     bool initialFocusWasSet = false;
0067 
0068     struct UI {
0069         QRadioButton *neverRB;
0070         QRadioButton *onRB;
0071         KDateComboBox *onCB;
0072         QCheckBox *updateSubkeysCheckBox;
0073         QLabel *primaryKeyExpirationDate;
0074         LabelHelper labelHelper;
0075 
0076         explicit UI(Mode mode, Dialogs::ExpiryDialog *qq)
0077         {
0078             auto mainLayout = new QVBoxLayout{qq};
0079 
0080             auto mainWidget = new QWidget{qq};
0081 
0082             auto vboxLayout = new QVBoxLayout{mainWidget};
0083             vboxLayout->setContentsMargins(0, 0, 0, 0);
0084 
0085             {
0086                 auto label = new QLabel{qq};
0087                 label->setText(mode == Mode::UpdateIndividualSubkey ? i18n("Please select until when the subkey should be valid:")
0088                                                                     : i18n("Please select until when the certificate should be valid:"));
0089                 vboxLayout->addWidget(label);
0090             }
0091 
0092             neverRB = new QRadioButton(i18n("Unlimited validity"), mainWidget);
0093             neverRB->setChecked(false);
0094 
0095             vboxLayout->addWidget(neverRB);
0096 
0097             {
0098                 auto hboxLayout = new QHBoxLayout;
0099 
0100                 onRB = new QRadioButton{i18n("Valid until:"), mainWidget};
0101                 onRB->setChecked(true);
0102 
0103                 hboxLayout->addWidget(onRB);
0104 
0105                 onCB = new KDateComboBox{mainWidget};
0106                 setUpExpirationDateComboBox(onCB);
0107 
0108                 hboxLayout->addWidget(onCB);
0109 
0110                 hboxLayout->addStretch(1);
0111 
0112                 vboxLayout->addLayout(hboxLayout);
0113             }
0114 
0115             primaryKeyExpirationDate = new QLabel(qq);
0116             primaryKeyExpirationDate->setVisible(false);
0117             vboxLayout->addWidget(primaryKeyExpirationDate);
0118             labelHelper.addLabel(primaryKeyExpirationDate);
0119 
0120             {
0121                 updateSubkeysCheckBox = new QCheckBox{i18n("Also update the validity period of the subkeys"), qq};
0122                 updateSubkeysCheckBox->setVisible(mode == Mode::UpdateCertificateWithSubkeys);
0123                 vboxLayout->addWidget(updateSubkeysCheckBox);
0124             }
0125 
0126             vboxLayout->addStretch(1);
0127 
0128             mainLayout->addWidget(mainWidget);
0129 
0130             auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok | QDialogButtonBox::Cancel, qq};
0131             auto okButton = buttonBox->button(QDialogButtonBox::Ok);
0132             KGuiItem::assign(okButton, KStandardGuiItem::ok());
0133             okButton->setDefault(true);
0134             okButton->setShortcut(static_cast<int>(Qt::CTRL) | static_cast<int>(Qt::Key_Return));
0135             KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
0136             qq->connect(buttonBox, &QDialogButtonBox::accepted, qq, &ExpiryDialog::accept);
0137             qq->connect(buttonBox, &QDialogButtonBox::rejected, qq, &QDialog::reject);
0138 
0139             mainLayout->addWidget(buttonBox);
0140 
0141             connect(onRB, &QRadioButton::toggled, onCB, &QWidget::setEnabled);
0142         }
0143     } ui;
0144 };
0145 
0146 void ExpiryDialog::Private::slotOnDateChanged()
0147 {
0148     ui.onRB->setAccessibleName(i18nc("Valid until DATE", "Valid until %1", Formatting::accessibleDate(ui.onCB->date())));
0149 }
0150 
0151 bool Kleo::Dialogs::ExpiryDialog::Private::unlimitedValidityAllowed() const
0152 {
0153     return !Kleo::maximumExpirationDate().isValid();
0154 }
0155 
0156 bool Kleo::Dialogs::ExpiryDialog::Private::fixedExpirationDate() const
0157 {
0158     return ui.onCB->minimumDate() == ui.onCB->maximumDate();
0159 }
0160 
0161 void ExpiryDialog::Private::setInitialFocus()
0162 {
0163     if (initialFocusWasSet) {
0164         return;
0165     }
0166     // give focus to the checked radio button
0167     (void)focusFirstCheckedButton({ui.neverRB, ui.onRB});
0168     initialFocusWasSet = true;
0169 }
0170 
0171 ExpiryDialog::ExpiryDialog(Mode mode, QWidget *p)
0172     : QDialog{p}
0173     , d{new Private{mode, this}}
0174 {
0175     setWindowTitle(i18nc("@title:window", "Change Validity Period"));
0176 }
0177 
0178 ExpiryDialog::~ExpiryDialog() = default;
0179 
0180 void ExpiryDialog::setDateOfExpiry(const QDate &date)
0181 {
0182     const QDate current = QDate::currentDate();
0183     if (date.isValid()) {
0184         d->ui.onRB->setChecked(true);
0185         if (date <= current) {
0186             d->ui.onCB->setDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
0187         } else {
0188             d->ui.onCB->setDate(date);
0189         }
0190     } else {
0191         if (d->unlimitedValidityAllowed()) {
0192             d->ui.neverRB->setChecked(true);
0193         } else {
0194             d->ui.onRB->setChecked(true);
0195         }
0196         d->ui.onCB->setDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
0197     }
0198 }
0199 
0200 QDate ExpiryDialog::dateOfExpiry() const
0201 {
0202     return d->ui.onRB->isChecked() ? d->ui.onCB->date() : QDate{};
0203 }
0204 
0205 void ExpiryDialog::setUpdateExpirationOfAllSubkeys(bool update)
0206 {
0207     d->ui.updateSubkeysCheckBox->setChecked(update);
0208 }
0209 
0210 bool ExpiryDialog::updateExpirationOfAllSubkeys() const
0211 {
0212     return d->ui.updateSubkeysCheckBox->isChecked();
0213 }
0214 
0215 void ExpiryDialog::accept()
0216 {
0217     const auto date = dateOfExpiry();
0218     if (!Kleo::isValidExpirationDate(date)) {
0219         KMessageBox::error(this, i18nc("@info", "Error: %1", Kleo::validityPeriodHint()));
0220         return;
0221     }
0222 
0223     QDialog::accept();
0224 }
0225 
0226 void ExpiryDialog::showEvent(QShowEvent *event)
0227 {
0228     d->setInitialFocus();
0229     QDialog::showEvent(event);
0230 }
0231 
0232 void ExpiryDialog::setPrimaryKey(const GpgME::Key &key)
0233 {
0234     if (!key.subkey(0).neverExpires()) {
0235         d->ui.primaryKeyExpirationDate->setText(i18n("Expiration of primary key: %1", Kleo::Formatting::expirationDateString(key)));
0236         d->ui.onCB->setMaximumDate(Kleo::Formatting::expirationDate(key));
0237         d->ui.primaryKeyExpirationDate->setVisible(true);
0238     }
0239 }
0240 
0241 #include "moc_expirydialog.cpp"