File indexing completed on 2024-12-22 05:01:10

0001 /*
0002     identitydialog.cpp
0003 
0004     This file is part of KMail, the KDE mail client.
0005     SPDX-FileCopyrightText: 2002 Marc Mutz <mutz@kde.org>
0006     SPDX-FileCopyrightText: 2014-2024 Laurent Montel <montel@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-only
0009 */
0010 
0011 #include "identitydialog.h"
0012 #include "identityaddvcarddialog.h"
0013 #include "identityeditvcarddialog.h"
0014 #include "identityfolderrequester.h"
0015 #include "identityinvalidfolder.h"
0016 
0017 #include <QGpgME/Job>
0018 #include <QGpgME/Protocol>
0019 
0020 #include <KIdentityManagementCore/IdentityManager>
0021 
0022 // other KMail headers:
0023 #include "settings/kmailsettings.h"
0024 #include "xfaceconfigurator.h"
0025 #include <KEditListWidget>
0026 #include <MailCommon/FolderRequester>
0027 
0028 #include <MailCommon/MailKernel>
0029 
0030 #include "job/addressvalidationjob.h"
0031 #include <MessageComposer/Kleo_Util>
0032 #include <MessageComposer/MessageComposerSettings>
0033 #include <MessageCore/StringUtil>
0034 #include <Sonnet/DictionaryComboBox>
0035 #include <TemplateParser/TemplatesConfiguration>
0036 #include <templateparser/templatesconfiguration_kfg.h>
0037 // other kdepim headers:
0038 #include <KIdentityManagementCore/Identity>
0039 #include <KIdentityManagementWidgets/SignatureConfigurator>
0040 
0041 #include <PimCommon/PimUtil>
0042 #include <TextAutoCorrectionWidgets/AutoCorrectionLanguage>
0043 
0044 #include <KLineEditEventHandler>
0045 #include <PimCommonAkonadi/AddresseeLineEdit>
0046 // libkleopatra:
0047 #include <Libkleo/DefaultKeyFilter>
0048 #include <Libkleo/DefaultKeyGenerationJob>
0049 #include <Libkleo/KeySelectionCombo>
0050 #include <Libkleo/ProgressDialog>
0051 
0052 // gpgme++
0053 #include <gpgme++/keygenerationresult.h>
0054 
0055 #include <KEmailAddress>
0056 #include <MailTransport/Transport>
0057 #include <MailTransport/TransportComboBox>
0058 #include <MailTransport/TransportManager>
0059 using MailTransport::TransportManager;
0060 
0061 // other KDE headers:
0062 #include "kmail_debug.h"
0063 #include <KEmailValidator>
0064 #include <KLocalizedString>
0065 #include <KMessageBox>
0066 #include <QComboBox>
0067 #include <QIcon>
0068 #include <QPushButton>
0069 #include <QTabWidget>
0070 
0071 // Qt headers:
0072 #include <QCheckBox>
0073 #include <QDir>
0074 #include <QFile>
0075 #include <QFileInfo>
0076 #include <QFormLayout>
0077 #include <QHostInfo>
0078 #include <QLabel>
0079 #include <QToolButton>
0080 #include <QVBoxLayout>
0081 
0082 // other headers:
0083 #include <algorithm>
0084 #include <gpgme++/key.h>
0085 #include <iterator>
0086 
0087 #include <Akonadi/CollectionFetchJob>
0088 #include <Akonadi/CollectionModifyJob>
0089 #include <Akonadi/EntityDisplayAttribute>
0090 #include <Akonadi/SpecialMailCollections>
0091 #include <QDialogButtonBox>
0092 #include <QGroupBox>
0093 #include <QStandardPaths>
0094 
0095 using namespace MailTransport;
0096 using namespace MailCommon;
0097 
0098 namespace KMail
0099 {
0100 class KeySelectionCombo : public Kleo::KeySelectionCombo
0101 {
0102     Q_OBJECT
0103 
0104 public:
0105     enum KeyType {
0106         SigningKey,
0107         EncryptionKey,
0108     };
0109 
0110     explicit KeySelectionCombo(KeyType keyType, GpgME::Protocol protocol, QWidget *parent);
0111     ~KeySelectionCombo() override;
0112 
0113     void setIdentity(const QString &name, const QString &email);
0114 
0115     void init() override;
0116 
0117 private:
0118     void onCustomItemSelected(const QVariant &type);
0119     QString mEmail;
0120     QString mName;
0121     const KeyType mKeyType;
0122     const GpgME::Protocol mProtocol;
0123 };
0124 
0125 class KeyGenerationJob : public QGpgME::Job
0126 {
0127     Q_OBJECT
0128 
0129 public:
0130     explicit KeyGenerationJob(const QString &name, const QString &email, KeySelectionCombo *parent);
0131     ~KeyGenerationJob() override;
0132 
0133     void slotCancel() override;
0134     void start();
0135 
0136 private:
0137     void keyGenerated(const GpgME::KeyGenerationResult &result);
0138     const QString mName;
0139     const QString mEmail;
0140     QGpgME::Job *mJob = nullptr;
0141 };
0142 
0143 KeyGenerationJob::KeyGenerationJob(const QString &name, const QString &email, KeySelectionCombo *parent)
0144     : QGpgME::Job(parent)
0145     , mName(name)
0146     , mEmail(email)
0147 {
0148 }
0149 
0150 KeyGenerationJob::~KeyGenerationJob() = default;
0151 
0152 void KeyGenerationJob::slotCancel()
0153 {
0154     if (mJob) {
0155         mJob->slotCancel();
0156     }
0157 }
0158 
0159 void KeyGenerationJob::start()
0160 {
0161     auto job = new Kleo::DefaultKeyGenerationJob(this);
0162     connect(job, &Kleo::DefaultKeyGenerationJob::result, this, &KeyGenerationJob::keyGenerated);
0163     job->start(mEmail, mName);
0164     mJob = job;
0165 }
0166 
0167 void KeyGenerationJob::keyGenerated(const GpgME::KeyGenerationResult &result)
0168 {
0169     mJob = nullptr;
0170     if (result.error()) {
0171         KMessageBox::error(qobject_cast<QWidget *>(parent()),
0172                            i18n("Error while generating new key pair: %1", QString::fromUtf8(result.error().asString())),
0173                            i18n("Key Generation Error"));
0174         Q_EMIT done();
0175         return;
0176     }
0177 
0178     auto combo = qobject_cast<KeySelectionCombo *>(parent());
0179     combo->setDefaultKey(QLatin1StringView(result.fingerprint()));
0180     connect(combo, &KeySelectionCombo::keyListingFinished, this, &KeyGenerationJob::done);
0181     combo->refreshKeys();
0182 }
0183 
0184 KeySelectionCombo::KeySelectionCombo(KeyType keyType, GpgME::Protocol protocol, QWidget *parent)
0185     : Kleo::KeySelectionCombo(parent)
0186     , mKeyType(keyType)
0187     , mProtocol(protocol)
0188 {
0189 }
0190 
0191 KeySelectionCombo::~KeySelectionCombo() = default;
0192 
0193 void KeySelectionCombo::setIdentity(const QString &name, const QString &email)
0194 {
0195     mName = name;
0196     mEmail = email;
0197     setIdFilter(email);
0198 }
0199 
0200 void KeySelectionCombo::init()
0201 {
0202     Kleo::KeySelectionCombo::init();
0203 
0204     std::shared_ptr<Kleo::DefaultKeyFilter> keyFilter(new Kleo::DefaultKeyFilter);
0205     keyFilter->setIsOpenPGP(mProtocol == GpgME::OpenPGP ? Kleo::DefaultKeyFilter::Set : Kleo::DefaultKeyFilter::NotSet);
0206     if (mKeyType == SigningKey) {
0207         keyFilter->setCanSign(Kleo::DefaultKeyFilter::Set);
0208         keyFilter->setHasSecret(Kleo::DefaultKeyFilter::Set);
0209     } else {
0210         keyFilter->setCanEncrypt(Kleo::DefaultKeyFilter::Set);
0211     }
0212     setKeyFilter(keyFilter);
0213     prependCustomItem(QIcon(), i18n("No key"), QStringLiteral("no-key"));
0214     if (mProtocol == GpgME::OpenPGP) {
0215         appendCustomItem(QIcon::fromTheme(QStringLiteral("password-generate")), i18n("Generate a new key pair"), QStringLiteral("generate-new-key"));
0216     }
0217 
0218     connect(this, &KeySelectionCombo::customItemSelected, this, &KeySelectionCombo::onCustomItemSelected);
0219 }
0220 
0221 void KeySelectionCombo::onCustomItemSelected(const QVariant &type)
0222 {
0223     if (type == QLatin1StringView("no-key")) {
0224         return;
0225     } else if (type == QLatin1StringView("generate-new-key")) {
0226         auto job = new KeyGenerationJob(mName, mEmail, this);
0227         auto dlg = new Kleo::ProgressDialog(job, i18n("Generating new key pair..."), parentWidget());
0228         dlg->setModal(true);
0229         setEnabled(false);
0230         connect(job, &KeyGenerationJob::done, this, [this]() {
0231             setEnabled(true);
0232         });
0233         job->start();
0234     }
0235 }
0236 
0237 IdentityDialog::IdentityDialog(QWidget *parent)
0238     : QDialog(parent)
0239 {
0240     setWindowTitle(i18nc("@title:window", "Edit Identity"));
0241     auto mainLayout = new QVBoxLayout(this);
0242 
0243     auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, this);
0244     connect(buttonBox->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &IdentityDialog::slotHelp);
0245     QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
0246     okButton->setDefault(true);
0247     okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
0248     connect(buttonBox, &QDialogButtonBox::accepted, this, &IdentityDialog::slotAccepted);
0249     connect(buttonBox, &QDialogButtonBox::rejected, this, &IdentityDialog::reject);
0250 
0251     //
0252     // Tab Widget: General
0253     //
0254     auto page = new QWidget(this);
0255     mainLayout->addWidget(page);
0256     mainLayout->addWidget(buttonBox);
0257     auto vlay = new QVBoxLayout(page);
0258     vlay->setContentsMargins({});
0259     mTabWidget = new QTabWidget(page);
0260     mTabWidget->setObjectName(QLatin1StringView("config-identity-tab"));
0261     vlay->addWidget(mTabWidget);
0262 
0263     auto tab = new QWidget(mTabWidget);
0264     mTabWidget->addTab(tab, i18nc("@title:tab General identity settings.", "General"));
0265 
0266     auto formLayout = new QFormLayout(tab);
0267 
0268     // "Name" line edit and label:
0269     mNameEdit = new QLineEdit(tab);
0270     KLineEditEventHandler::catchReturnKey(mNameEdit);
0271     auto label = new QLabel(i18n("&Your name:"), tab);
0272     formLayout->addRow(label, mNameEdit);
0273     label->setBuddy(mNameEdit);
0274 
0275     QString msg = i18n(
0276         "<qt><h3>Your name</h3>"
0277         "<p>This field should contain your name as you would like "
0278         "it to appear in the email header that is sent out;</p>"
0279         "<p>if you leave this blank your real name will not "
0280         "appear, only the email address.</p></qt>");
0281     label->setWhatsThis(msg);
0282     mNameEdit->setWhatsThis(msg);
0283 
0284     // "Organization" line edit and label:
0285     mOrganizationEdit = new QLineEdit(tab);
0286     KLineEditEventHandler::catchReturnKey(mOrganizationEdit);
0287     label = new QLabel(i18n("Organi&zation:"), tab);
0288     formLayout->addRow(label, mOrganizationEdit);
0289     label->setBuddy(mOrganizationEdit);
0290 
0291     msg = i18n(
0292         "<qt><h3>Organization</h3>"
0293         "<p>This field should have the name of your organization "
0294         "if you would like it to be shown in the email header that "
0295         "is sent out.</p>"
0296         "<p>It is safe (and normal) to leave this blank.</p></qt>");
0297     label->setWhatsThis(msg);
0298     mOrganizationEdit->setWhatsThis(msg);
0299 
0300     // "Email Address" line edit and label:
0301     // (row 3: spacer)
0302     mEmailEdit = new QLineEdit(tab);
0303     KLineEditEventHandler::catchReturnKey(mEmailEdit);
0304     label = new QLabel(i18n("&Email address:"), tab);
0305     formLayout->addRow(label, mEmailEdit);
0306     label->setBuddy(mEmailEdit);
0307 
0308     msg = i18n(
0309         "<qt><h3>Email address</h3>"
0310         "<p>This field should have your full email address.</p>"
0311         "<p>This address is the primary one, used for all outgoing mail. "
0312         "If you have more than one address, either create a new identity, "
0313         "or add additional alias addresses in the field below.</p>"
0314         "<p>If you leave this blank, or get it wrong, people "
0315         "will have trouble replying to you.</p></qt>");
0316     label->setWhatsThis(msg);
0317     mEmailEdit->setWhatsThis(msg);
0318 
0319     auto emailValidator = new KEmailValidator(this);
0320     mEmailEdit->setValidator(emailValidator);
0321 
0322     // "Email Aliases" string text edit and label:
0323     mAliasEdit = new KEditListWidget(tab);
0324 
0325     auto emailValidator1 = new KEmailValidator(this);
0326     mAliasEdit->lineEdit()->setValidator(emailValidator1);
0327 
0328     label = new QLabel(i18n("Email a&liases:"), tab);
0329     formLayout->addRow(label, mAliasEdit);
0330     label->setBuddy(mAliasEdit);
0331 
0332     msg = i18n(
0333         "<qt><h3>Email aliases</h3>"
0334         "<p>This field contains alias addresses that should also "
0335         "be considered as belonging to this identity (as opposed "
0336         "to representing a different identity).</p>"
0337         "<p>Example:</p>"
0338         "<table>"
0339         "<tr><th>Primary address:</th><td>first.last@example.org</td></tr>"
0340         "<tr><th>Aliases:</th><td>first@example.org<br>last@example.org</td></tr>"
0341         "</table>"
0342         "<p>Type one alias address per line.</p></qt>");
0343     label->setToolTip(msg);
0344     mAliasEdit->setWhatsThis(msg);
0345 
0346     //
0347     // Tab Widget: Cryptography
0348     //
0349     mCryptographyTab = new QWidget(mTabWidget);
0350     mTabWidget->addTab(mCryptographyTab, i18n("Cryptography"));
0351     formLayout = new QFormLayout(mCryptographyTab);
0352 
0353     // "OpenPGP Signature Key" requester and label:
0354     mPGPSigningKeyRequester = new KeySelectionCombo(KeySelectionCombo::SigningKey, GpgME::OpenPGP, mCryptographyTab);
0355     msg = i18n(
0356         "<qt><p>The OpenPGP key you choose here will be used "
0357         "to digitally sign messages. You can also use GnuPG keys.</p>"
0358         "<p>You can leave this blank, but KMail will not be able "
0359         "to digitally sign emails using OpenPGP; "
0360         "normal mail functions will not be affected.</p>"
0361         "<p>You can find out more about keys at <a>https://www.gnupg.org</a></p></qt>");
0362     label = new QLabel(i18n("OpenPGP signing key:"), mCryptographyTab);
0363     label->setBuddy(mPGPSigningKeyRequester);
0364     mPGPSigningKeyRequester->setWhatsThis(msg);
0365     label->setWhatsThis(msg);
0366 
0367     auto vbox = new QVBoxLayout;
0368     mPGPSameKey = new QCheckBox(i18n("Use same key for encryption and signing"));
0369     vbox->addWidget(mPGPSigningKeyRequester);
0370     vbox->addWidget(mPGPSameKey);
0371     formLayout->addRow(label, vbox);
0372 
0373     connect(mPGPSameKey, &QCheckBox::toggled, this, [this, formLayout, vbox](bool checked) {
0374         mPGPEncryptionKeyRequester->setVisible(!checked);
0375         formLayout->labelForField(mPGPEncryptionKeyRequester)->setVisible(!checked);
0376         const auto label = qobject_cast<QLabel *>(formLayout->labelForField(vbox));
0377         if (checked) {
0378             label->setText(i18n("OpenPGP key:"));
0379             const auto key = mPGPSigningKeyRequester->currentKey();
0380             if (!key.isBad()) {
0381                 mPGPEncryptionKeyRequester->setCurrentKey(key);
0382             } else if (mPGPSigningKeyRequester->currentData() == QLatin1StringView("no-key")) {
0383                 mPGPEncryptionKeyRequester->setCurrentIndex(mPGPSigningKeyRequester->currentIndex());
0384             }
0385         } else {
0386             label->setText(i18n("OpenPGP signing key:"));
0387         }
0388     });
0389     connect(mPGPSigningKeyRequester, &KeySelectionCombo::currentKeyChanged, this, [&](const GpgME::Key &key) {
0390         if (mPGPSameKey->isChecked()) {
0391             mPGPEncryptionKeyRequester->setCurrentKey(key);
0392         }
0393     });
0394     connect(mPGPSigningKeyRequester, &KeySelectionCombo::customItemSelected, this, [&](const QVariant &type) {
0395         if (mPGPSameKey->isChecked() && type == QLatin1StringView("no-key")) {
0396             mPGPEncryptionKeyRequester->setCurrentIndex(mPGPSigningKeyRequester->currentIndex());
0397         }
0398     });
0399 
0400     // "OpenPGP Encryption Key" requester and label:
0401     mPGPEncryptionKeyRequester = new KeySelectionCombo(KeySelectionCombo::EncryptionKey, GpgME::OpenPGP, mCryptographyTab);
0402     msg = i18n(
0403         "<qt><p>The OpenPGP key you choose here will be used "
0404         "to encrypt messages to yourself and for the \"Attach My Public Key\" "
0405         "feature in the composer. You can also use GnuPG keys.</p>"
0406         "<p>You can leave this blank, but KMail will not be able "
0407         "to encrypt copies of outgoing messages to you using OpenPGP; "
0408         "normal mail functions will not be affected.</p>"
0409         "<p>You can find out more about keys at <a>https://www.gnupg.org</a></p></qt>");
0410     label = new QLabel(i18n("OpenPGP encryption key:"), mCryptographyTab);
0411     label->setBuddy(mPGPEncryptionKeyRequester);
0412     label->setWhatsThis(msg);
0413     mPGPEncryptionKeyRequester->setWhatsThis(msg);
0414 
0415     formLayout->addRow(label, mPGPEncryptionKeyRequester);
0416 
0417     // "S/MIME Signature Key" requester and label:
0418     mSMIMESigningKeyRequester = new KeySelectionCombo(KeySelectionCombo::SigningKey, GpgME::CMS, mCryptographyTab);
0419     msg = i18n(
0420         "<qt><p>The S/MIME (X.509) certificate you choose here will be used "
0421         "to digitally sign messages.</p>"
0422         "<p>You can leave this blank, but KMail will not be able "
0423         "to digitally sign emails using S/MIME; "
0424         "normal mail functions will not be affected.</p></qt>");
0425     label = new QLabel(i18n("S/MIME signing certificate:"), mCryptographyTab);
0426     label->setBuddy(mSMIMESigningKeyRequester);
0427     mSMIMESigningKeyRequester->setWhatsThis(msg);
0428     label->setWhatsThis(msg);
0429     formLayout->addRow(label, mSMIMESigningKeyRequester);
0430 
0431     const QGpgME::Protocol *smimeProtocol = QGpgME::smime();
0432 
0433     label->setEnabled(smimeProtocol);
0434     mSMIMESigningKeyRequester->setEnabled(smimeProtocol);
0435 
0436     // "S/MIME Encryption Key" requester and label:
0437     mSMIMEEncryptionKeyRequester = new KeySelectionCombo(KeySelectionCombo::EncryptionKey, GpgME::CMS, mCryptographyTab);
0438     msg = i18n(
0439         "<qt><p>The S/MIME certificate you choose here will be used "
0440         "to encrypt messages to yourself and for the \"Attach My Certificate\" "
0441         "feature in the composer.</p>"
0442         "<p>You can leave this blank, but KMail will not be able "
0443         "to encrypt copies of outgoing messages to you using S/MIME; "
0444         "normal mail functions will not be affected.</p></qt>");
0445     label = new QLabel(i18n("S/MIME encryption certificate:"), mCryptographyTab);
0446     label->setBuddy(mSMIMEEncryptionKeyRequester);
0447     mSMIMEEncryptionKeyRequester->setWhatsThis(msg);
0448     label->setWhatsThis(msg);
0449 
0450     formLayout->addRow(label, mSMIMEEncryptionKeyRequester);
0451 
0452     label->setEnabled(smimeProtocol);
0453     mSMIMEEncryptionKeyRequester->setEnabled(smimeProtocol);
0454 
0455     // "Preferred Crypto Message Format" combobox and label:
0456     mPreferredCryptoMessageFormat = new QComboBox(mCryptographyTab);
0457     QStringList l;
0458     l << Kleo::cryptoMessageFormatToLabel(Kleo::AutoFormat) << Kleo::cryptoMessageFormatToLabel(Kleo::InlineOpenPGPFormat)
0459       << Kleo::cryptoMessageFormatToLabel(Kleo::OpenPGPMIMEFormat) << Kleo::cryptoMessageFormatToLabel(Kleo::SMIMEFormat)
0460       << Kleo::cryptoMessageFormatToLabel(Kleo::SMIMEOpaqueFormat);
0461     mPreferredCryptoMessageFormat->addItems(l);
0462     label = new QLabel(i18nc("preferred format of encrypted messages", "Preferred format:"), mCryptographyTab);
0463     label->setBuddy(mPreferredCryptoMessageFormat);
0464 
0465     formLayout->addRow(label, mPreferredCryptoMessageFormat);
0466 
0467     mAutocrypt = new QGroupBox(i18n("Enable Autocrypt"));
0468     mAutocrypt->setCheckable(true);
0469     mAutocrypt->setChecked(true);
0470 
0471     label = new QLabel(i18n("Autocrypt:"), tab);
0472     formLayout->addRow(label, mAutocrypt);
0473 
0474     vlay = new QVBoxLayout(mAutocrypt);
0475 
0476     mAutocryptPrefer = new QCheckBox(i18n("Let others know you prefer encryption"));
0477     vlay->addWidget(mAutocryptPrefer);
0478 
0479     mOverrideDefault = new QGroupBox(i18n("Overwrite global settings for security defaults"));
0480     mOverrideDefault->setCheckable(true);
0481     mOverrideDefault->setChecked(false);
0482     label = new QLabel(i18n("Overwrite defaults:"), tab);
0483     formLayout->addRow(label, mOverrideDefault);
0484 
0485     vlay = new QVBoxLayout(mOverrideDefault);
0486 
0487     mAutoSign = new QCheckBox(i18n("Sign messages"));
0488     vlay->addWidget(mAutoSign);
0489 
0490     mAutoEncrypt = new QCheckBox(i18n("Encrypt messages when possible"));
0491     vlay->addWidget(mAutoEncrypt);
0492 
0493     mWarnNotSign = new QCheckBox(i18n("Warn when trying to send unsigned messages"));
0494     vlay->addWidget(mWarnNotSign);
0495 
0496     mWarnNotEncrypt = new QCheckBox(i18n("Warn when trying to send unencrypted messages"));
0497     vlay->addWidget(mWarnNotEncrypt);
0498 
0499     //
0500     // Tab Widget: Advanced
0501     //
0502     tab = new QWidget(mTabWidget);
0503     auto advancedMainLayout = new QVBoxLayout(tab);
0504     mIdentityInvalidFolder = new IdentityInvalidFolder(tab);
0505     advancedMainLayout->addWidget(mIdentityInvalidFolder);
0506     mTabWidget->addTab(tab, i18nc("@title:tab Advanced identity settings.", "Advanced"));
0507     formLayout = new QFormLayout;
0508     advancedMainLayout->addLayout(formLayout);
0509 
0510     // "Reply-To Address" line edit and label:
0511     mReplyToEdit = new PimCommon::AddresseeLineEdit(tab, true);
0512     mReplyToEdit->setClearButtonEnabled(true);
0513     mReplyToEdit->setObjectName(QLatin1StringView("mReplyToEdit"));
0514     label = new QLabel(i18n("&Reply-To address:"), tab);
0515     label->setBuddy(mReplyToEdit);
0516     formLayout->addRow(label, mReplyToEdit);
0517     msg = i18n(
0518         "<qt><h3>Reply-To addresses</h3>"
0519         "<p>This sets the <tt>Reply-to:</tt> header to contain a "
0520         "different email address to the normal <tt>From:</tt> "
0521         "address.</p>"
0522         "<p>This can be useful when you have a group of people "
0523         "working together in similar roles. For example, you "
0524         "might want any emails sent to have your email in the "
0525         "<tt>From:</tt> field, but any responses to go to "
0526         "a group address.</p>"
0527         "<p>If in doubt, leave this field blank.</p></qt>");
0528     label->setWhatsThis(msg);
0529     mReplyToEdit->setWhatsThis(msg);
0530     KLineEditEventHandler::catchReturnKey(mReplyToEdit);
0531 
0532     // "CC addresses" line edit and label:
0533     mCcEdit = new PimCommon::AddresseeLineEdit(tab, true);
0534     mCcEdit->setClearButtonEnabled(true);
0535     mCcEdit->setObjectName(QLatin1StringView("mCcEdit"));
0536     label = new QLabel(i18n("&CC addresses:"), tab);
0537     label->setBuddy(mCcEdit);
0538     formLayout->addRow(label, mCcEdit);
0539     KLineEditEventHandler::catchReturnKey(mCcEdit);
0540 
0541     msg = i18n(
0542         "<qt><h3>CC (Carbon Copy) addresses</h3>"
0543         "<p>The addresses that you enter here will be added to each "
0544         "outgoing mail that is sent with this identity.</p>"
0545         "<p>This is commonly used to send a copy of each sent message to "
0546         "another account of yours.</p>"
0547         "<p>To specify more than one address, use commas to separate "
0548         "the list of CC recipients.</p>"
0549         "<p>If in doubt, leave this field blank.</p></qt>");
0550     label->setWhatsThis(msg);
0551     mCcEdit->setWhatsThis(msg);
0552 
0553     // "BCC addresses" line edit and label:
0554     mBccEdit = new PimCommon::AddresseeLineEdit(tab, true);
0555     mBccEdit->setClearButtonEnabled(true);
0556     mBccEdit->setObjectName(QLatin1StringView("mBccEdit"));
0557     KLineEditEventHandler::catchReturnKey(mBccEdit);
0558     label = new QLabel(i18n("&BCC addresses:"), tab);
0559     label->setBuddy(mBccEdit);
0560     formLayout->addRow(label, mBccEdit);
0561     msg = i18n(
0562         "<qt><h3>BCC (Blind Carbon Copy) addresses</h3>"
0563         "<p>The addresses that you enter here will be added to each "
0564         "outgoing mail that is sent with this identity. They will not "
0565         "be visible to other recipients.</p>"
0566         "<p>This is commonly used to send a copy of each sent message to "
0567         "another account of yours.</p>"
0568         "<p>To specify more than one address, use commas to separate "
0569         "the list of BCC recipients.</p>"
0570         "<p>If in doubt, leave this field blank.</p></qt>");
0571     label->setWhatsThis(msg);
0572     mBccEdit->setWhatsThis(msg);
0573 
0574     // "Dictionary" combo box and label:
0575     mDictionaryCombo = new Sonnet::DictionaryComboBox(tab);
0576     label = new QLabel(i18n("D&ictionary:"), tab);
0577     label->setBuddy(mDictionaryCombo);
0578     formLayout->addRow(label, mDictionaryCombo);
0579 
0580     // "Sent-mail Folder" combo box and label:
0581     mFccFolderRequester = new IdentityFolderRequester(tab);
0582     mFccFolderRequester->setSelectFolderTitleDialog(i18n("Select Send-mail Folder"));
0583     mFccFolderRequester->setShowOutbox(false);
0584     mSentMailFolderCheck = new QCheckBox(i18n("Sent-mail &folder:"), tab);
0585     connect(mSentMailFolderCheck, &QCheckBox::toggled, mFccFolderRequester, &MailCommon::FolderRequester::setEnabled);
0586     formLayout->addRow(mSentMailFolderCheck, mFccFolderRequester);
0587 
0588     // "Drafts Folder" combo box and label:
0589     mDraftsFolderRequester = new IdentityFolderRequester(tab);
0590     mDraftsFolderRequester->setSelectFolderTitleDialog(i18n("Select Draft Folder"));
0591     mDraftsFolderRequester->setShowOutbox(false);
0592     label = new QLabel(i18n("&Drafts folder:"), tab);
0593     label->setBuddy(mDraftsFolderRequester);
0594     formLayout->addRow(label, mDraftsFolderRequester);
0595 
0596     // "Templates Folder" combo box and label:
0597     mTemplatesFolderRequester = new IdentityFolderRequester(tab);
0598     mTemplatesFolderRequester->setSelectFolderTitleDialog(i18n("Select Templates Folder"));
0599     mTemplatesFolderRequester->setShowOutbox(false);
0600     label = new QLabel(i18n("&Templates folder:"), tab);
0601     label->setBuddy(mTemplatesFolderRequester);
0602     formLayout->addRow(label, mTemplatesFolderRequester);
0603 
0604     // "Special transport" combobox and label:
0605     mTransportCheck = new QCheckBox(i18n("Outgoing Account:"), tab);
0606     mTransportCombo = new TransportComboBox(tab);
0607     mTransportCombo->setEnabled(false); // since !mTransportCheck->isChecked()
0608     formLayout->addRow(mTransportCheck, mTransportCombo);
0609 
0610     connect(mTransportCheck, &QCheckBox::toggled, mTransportCombo, &MailTransport::TransportComboBox::setEnabled);
0611 
0612     mAttachMyVCard = new QCheckBox(i18n("Attach my vCard to message"), tab);
0613     mEditVCard = new QPushButton(i18n("Create..."), tab);
0614     connect(mEditVCard, &QPushButton::clicked, this, &IdentityDialog::slotEditVcard);
0615     formLayout->addRow(mAttachMyVCard, mEditVCard);
0616     mAutoCorrectionLanguage = new TextAutoCorrectionWidgets::AutoCorrectionLanguage(tab);
0617     label = new QLabel(i18n("Autocorrection language:"), tab);
0618     label->setBuddy(mAutoCorrectionLanguage);
0619     formLayout->addRow(label, mAutoCorrectionLanguage);
0620 
0621     // "default domain" input field:
0622     auto hbox = new QHBoxLayout;
0623     mDefaultDomainEdit = new QLineEdit(tab);
0624     KLineEditEventHandler::catchReturnKey(mDefaultDomainEdit);
0625     mDefaultDomainEdit->setClearButtonEnabled(true);
0626     hbox->addWidget(mDefaultDomainEdit);
0627     auto restoreDefaultDomainName = new QToolButton;
0628     restoreDefaultDomainName->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
0629     restoreDefaultDomainName->setToolTip(i18n("Restore default domain name"));
0630     hbox->addWidget(restoreDefaultDomainName);
0631     connect(restoreDefaultDomainName, &QToolButton::clicked, this, &IdentityDialog::slotRefreshDefaultDomainName);
0632     label = new QLabel(i18n("Defaul&t domain:"), tab);
0633     label->setBuddy(mDefaultDomainEdit);
0634     formLayout->addRow(label, mDefaultDomainEdit);
0635 
0636     // and now: add QWhatsThis:
0637     msg = i18n(
0638         "<qt><p>The default domain is used to complete email "
0639         "addresses that only consist of the user's name."
0640         "</p></qt>");
0641     label->setWhatsThis(msg);
0642     mDefaultDomainEdit->setWhatsThis(msg);
0643 
0644     // the last row is a spacer
0645 
0646     //
0647     // Tab Widget: Templates
0648     //
0649     tab = new QWidget(mTabWidget);
0650     vlay = new QVBoxLayout(tab);
0651 
0652     auto tlay = new QHBoxLayout();
0653     vlay->addLayout(tlay);
0654 
0655     mCustom = new QCheckBox(i18n("&Use custom message templates for this identity"), tab);
0656     tlay->addWidget(mCustom, Qt::AlignLeft);
0657 
0658     mWidget = new TemplateParser::TemplatesConfiguration(tab, QStringLiteral("identity-templates"));
0659     mWidget->setEnabled(false);
0660 
0661     // Move the help label outside of the templates configuration widget,
0662     // so that the help can be read even if the widget is not enabled.
0663     tlay->addStretch(9);
0664     tlay->addWidget(mWidget->helpLabel(), Qt::AlignRight);
0665 
0666     vlay->addWidget(mWidget);
0667 
0668     auto btns = new QHBoxLayout();
0669     mCopyGlobal = new QPushButton(i18n("&Copy Global Templates"), tab);
0670     mCopyGlobal->setEnabled(false);
0671     btns->addWidget(mCopyGlobal);
0672     vlay->addLayout(btns);
0673     connect(mCustom, &QCheckBox::toggled, mWidget, &TemplateParser::TemplatesConfiguration::setEnabled);
0674     connect(mCustom, &QCheckBox::toggled, mCopyGlobal, &QPushButton::setEnabled);
0675     connect(mCopyGlobal, &QPushButton::clicked, this, &IdentityDialog::slotCopyGlobal);
0676     mTabWidget->addTab(tab, i18n("Templates"));
0677 
0678     //
0679     // Tab Widget: Signature
0680     //
0681     mSignatureConfigurator = new KIdentityManagementWidgets::SignatureConfigurator(mTabWidget);
0682     mTabWidget->addTab(mSignatureConfigurator, i18n("Signature"));
0683 
0684     //
0685     // Tab Widget: Picture
0686     //
0687 
0688     mXFaceConfigurator = new XFaceConfigurator(mTabWidget);
0689     mTabWidget->addTab(mXFaceConfigurator, i18n("Picture"));
0690 
0691     resize(KMailSettings::self()->identityDialogSize());
0692     mNameEdit->setFocus();
0693 
0694     connect(mTabWidget, &QTabWidget::currentChanged, this, &IdentityDialog::slotAboutToShow);
0695 }
0696 
0697 IdentityDialog::~IdentityDialog()
0698 {
0699     KMailSettings::self()->setIdentityDialogSize(size());
0700 }
0701 
0702 void IdentityDialog::slotHelp()
0703 {
0704     PimCommon::Util::invokeHelp(QStringLiteral("kmail2/configure-identity.html"));
0705 }
0706 
0707 void IdentityDialog::slotAboutToShow(int index)
0708 {
0709     QWidget *w = mTabWidget->widget(index);
0710     if (w == mCryptographyTab) {
0711         // set the configured email address as initial query of the key
0712         // requesters:
0713         const QString name = mNameEdit->text().trimmed();
0714         const QString email = mEmailEdit->text().trimmed();
0715 
0716         mPGPEncryptionKeyRequester->setIdentity(name, email);
0717         mPGPSigningKeyRequester->setIdentity(name, email);
0718         mSMIMEEncryptionKeyRequester->setIdentity(name, email);
0719         mSMIMESigningKeyRequester->setIdentity(name, email);
0720     }
0721 }
0722 
0723 void IdentityDialog::slotCopyGlobal()
0724 {
0725     mWidget->loadFromGlobal();
0726 }
0727 
0728 void IdentityDialog::slotRefreshDefaultDomainName()
0729 {
0730     mDefaultDomainEdit->setText(QHostInfo::localHostName());
0731 }
0732 
0733 void IdentityDialog::slotAccepted()
0734 {
0735     const QStringList aliases = mAliasEdit->items();
0736     for (const QString &alias : aliases) {
0737         if (alias.trimmed().isEmpty()) {
0738             continue;
0739         }
0740         if (!KEmailAddress::isValidSimpleAddress(alias)) {
0741             const QString errorMsg(KEmailAddress::simpleEmailAddressErrorMsg());
0742             KMessageBox::error(this, errorMsg, i18n("Invalid Email Alias \"%1\"", alias));
0743             return;
0744         }
0745     }
0746 
0747     // Validate email addresses
0748     const QString email = mEmailEdit->text().trimmed();
0749     if (email.isEmpty()) {
0750         KMessageBox::error(this, i18n("You must provide an email for this identity."), i18nc("@title:window", "Empty Email Address"));
0751         return;
0752     }
0753     if (!KEmailAddress::isValidSimpleAddress(email)) {
0754         const QString errorMsg(KEmailAddress::simpleEmailAddressErrorMsg());
0755         KMessageBox::error(this, errorMsg, i18n("Invalid Email Address"));
0756         return;
0757     }
0758 
0759     // Check if the 'Reply to' and 'BCC' recipients are valid
0760     const QString recipients =
0761         mReplyToEdit->text().trimmed() + QLatin1StringView(", ") + mBccEdit->text().trimmed() + QLatin1StringView(", ") + mCcEdit->text().trimmed();
0762     auto job = new AddressValidationJob(recipients, this, this);
0763     // Use default Value
0764     job->setDefaultDomain(mDefaultDomainEdit->text());
0765     job->setProperty("email", email);
0766     connect(job, &AddressValidationJob::result, this, &IdentityDialog::slotDelayedButtonClicked);
0767     job->start();
0768 }
0769 
0770 bool IdentityDialog::keyMatchesEmailAddress(const GpgME::Key &key, const QString &email_)
0771 {
0772     if (key.isNull()) {
0773         return true;
0774     }
0775     const QString email = email_.trimmed().toLower();
0776     const auto uids = key.userIDs();
0777     for (const auto &uid : uids) {
0778         QString em = QString::fromUtf8(uid.email() ? uid.email() : uid.id());
0779         if (em.isEmpty()) {
0780             continue;
0781         }
0782         if (em[0] == QLatin1Char('<')) {
0783             em = em.mid(1, em.length() - 2);
0784         }
0785         if (em.toLower() == email) {
0786             return true;
0787         }
0788     }
0789 
0790     return false;
0791 }
0792 
0793 void IdentityDialog::slotDelayedButtonClicked(KJob *job)
0794 {
0795     const AddressValidationJob *validationJob = qobject_cast<AddressValidationJob *>(job);
0796 
0797     // Abort if one of the recipient addresses is invalid
0798     if (!validationJob->isValid()) {
0799         return;
0800     }
0801 
0802     const QString email = validationJob->property("email").toString();
0803 
0804     const GpgME::Key &pgpSigningKey = mPGPSigningKeyRequester->currentKey();
0805     const GpgME::Key &pgpEncryptionKey = mPGPEncryptionKeyRequester->currentKey();
0806     const GpgME::Key &smimeSigningKey = mSMIMESigningKeyRequester->currentKey();
0807     const GpgME::Key &smimeEncryptionKey = mSMIMEEncryptionKeyRequester->currentKey();
0808 
0809     QString msg;
0810     bool err = false;
0811     if (!keyMatchesEmailAddress(pgpSigningKey, email)) {
0812         msg = i18n(
0813             "One of the configured OpenPGP signing keys does not contain "
0814             "any user ID with the configured email address for this "
0815             "identity (%1).\n"
0816             "This might result in warning messages on the receiving side "
0817             "when trying to verify signatures made with this configuration.",
0818             email);
0819         err = true;
0820     } else if (!keyMatchesEmailAddress(pgpEncryptionKey, email)) {
0821         msg = i18n(
0822             "One of the configured OpenPGP encryption keys does not contain "
0823             "any user ID with the configured email address for this "
0824             "identity (%1).",
0825             email);
0826         err = true;
0827     } else if (!keyMatchesEmailAddress(smimeSigningKey, email)) {
0828         msg = i18n(
0829             "One of the configured S/MIME signing certificates does not contain "
0830             "the configured email address for this "
0831             "identity (%1).\n"
0832             "This might result in warning messages on the receiving side "
0833             "when trying to verify signatures made with this configuration.",
0834             email);
0835         err = true;
0836     } else if (!keyMatchesEmailAddress(smimeEncryptionKey, email)) {
0837         msg = i18n(
0838             "One of the configured S/MIME encryption certificates does not contain "
0839             "the configured email address for this "
0840             "identity (%1).",
0841             email);
0842         err = true;
0843     }
0844 
0845     if (err) {
0846         if (KMessageBox::warningContinueCancel(this,
0847                                                msg,
0848                                                i18nc("@title:window", "Email Address Not Found in Key/Certificates"),
0849                                                KStandardGuiItem::cont(),
0850                                                KStandardGuiItem::cancel(),
0851                                                QStringLiteral("warn_email_not_in_certificate"))
0852             != KMessageBox::Continue) {
0853             return;
0854         }
0855     }
0856 
0857     if (mSignatureConfigurator->isSignatureEnabled() && mSignatureConfigurator->signatureType() == Signature::FromFile) {
0858         QFileInfo file(mSignatureConfigurator->filePath());
0859         if (!file.isReadable()) {
0860             KMessageBox::error(this, i18n("The signature file is not valid"));
0861             return;
0862         }
0863     }
0864 
0865     accept();
0866 }
0867 
0868 bool IdentityDialog::checkFolderExists(const QString &folderID)
0869 {
0870     const Akonadi::Collection folder = CommonKernel->collectionFromId(folderID.toLongLong());
0871     return folder.isValid();
0872 }
0873 
0874 void IdentityDialog::setIdentity(KIdentityManagementCore::Identity &ident)
0875 {
0876     setWindowTitle(i18nc("@title:window", "Edit Identity \"%1\"", ident.identityName()));
0877 
0878     // "General" tab:
0879     mNameEdit->setText(ident.fullName());
0880     mOrganizationEdit->setText(ident.organization());
0881     mEmailEdit->setText(ident.primaryEmailAddress());
0882     mAliasEdit->insertStringList(ident.emailAliases());
0883 
0884     // "Cryptography" tab:
0885     mPGPSigningKeyRequester->setDefaultKey(QLatin1StringView(ident.pgpSigningKey()));
0886     mPGPEncryptionKeyRequester->setDefaultKey(QLatin1StringView(ident.pgpEncryptionKey()));
0887 
0888     mPGPSameKey->setChecked(ident.pgpSigningKey() == ident.pgpEncryptionKey());
0889 
0890     mSMIMESigningKeyRequester->setDefaultKey(QLatin1StringView(ident.smimeSigningKey()));
0891     mSMIMEEncryptionKeyRequester->setDefaultKey(QLatin1StringView(ident.smimeEncryptionKey()));
0892 
0893     mPreferredCryptoMessageFormat->setCurrentIndex(format2cb(Kleo::stringToCryptoMessageFormat(ident.preferredCryptoMessageFormat())));
0894     mAutocrypt->setChecked(ident.autocryptEnabled());
0895     mAutocryptPrefer->setChecked(ident.autocryptPrefer());
0896     mOverrideDefault->setChecked(ident.encryptionOverride());
0897     if (!ident.encryptionOverride()) {
0898         mAutoSign->setChecked(ident.pgpAutoSign());
0899         mAutoEncrypt->setChecked(ident.pgpAutoEncrypt());
0900         mWarnNotSign->setChecked(ident.warnNotSign());
0901         mWarnNotEncrypt->setChecked(ident.warnNotEncrypt());
0902     } else {
0903         mAutoEncrypt->setChecked(MessageComposer::MessageComposerSettings::self()->cryptoAutoEncrypt());
0904         mAutoSign->setChecked(MessageComposer::MessageComposerSettings::self()->cryptoAutoSign());
0905         mWarnNotEncrypt->setChecked(MessageComposer::MessageComposerSettings::self()->cryptoWarningUnencrypted());
0906         mWarnNotSign->setChecked(MessageComposer::MessageComposerSettings::self()->cryptoWarningUnsigned());
0907     }
0908 
0909     // "Advanced" tab:
0910     mReplyToEdit->setText(ident.replyToAddr());
0911     mBccEdit->setText(ident.bcc());
0912     mCcEdit->setText(ident.cc());
0913     const int transportId = ident.transport().isEmpty() ? -1 : ident.transport().toInt();
0914     const Transport *transport = TransportManager::self()->transportById(transportId, true);
0915     mTransportCheck->setChecked(transportId != -1);
0916     mTransportCombo->setEnabled(transportId != -1);
0917     if (transport) {
0918         mTransportCombo->setCurrentTransport(transport->id());
0919     }
0920     mDictionaryCombo->setCurrentByDictionaryName(ident.dictionary());
0921 
0922     mSentMailFolderCheck->setChecked(!ident.disabledFcc());
0923     mFccFolderRequester->setEnabled(mSentMailFolderCheck->isChecked());
0924     bool foundNoExistingFolder = false;
0925     if (ident.fcc().isEmpty() || !checkFolderExists(ident.fcc())) {
0926         foundNoExistingFolder = true;
0927         mFccFolderRequester->setIsInvalidFolder(CommonKernel->sentCollectionFolder());
0928     } else {
0929         mFccFolderRequester->setCollection(Akonadi::Collection(ident.fcc().toLongLong()));
0930     }
0931     if (ident.drafts().isEmpty() || !checkFolderExists(ident.drafts())) {
0932         foundNoExistingFolder = true;
0933         mDraftsFolderRequester->setIsInvalidFolder(CommonKernel->draftsCollectionFolder());
0934     } else {
0935         mDraftsFolderRequester->setCollection(Akonadi::Collection(ident.drafts().toLongLong()));
0936     }
0937 
0938     if (ident.templates().isEmpty() || !checkFolderExists(ident.templates())) {
0939         foundNoExistingFolder = true;
0940         mTemplatesFolderRequester->setIsInvalidFolder(CommonKernel->templatesCollectionFolder());
0941     } else {
0942         mTemplatesFolderRequester->setCollection(Akonadi::Collection(ident.templates().toLongLong()));
0943     }
0944     if (foundNoExistingFolder) {
0945         mIdentityInvalidFolder->setErrorMessage(i18n("Some custom folder for identity does not exist (anymore); therefore, default folders will be used."));
0946     }
0947     mVcardFilename = ident.vCardFile();
0948 
0949     mAutoCorrectionLanguage->setLanguage(ident.autocorrectionLanguage());
0950     updateVcardButton();
0951     if (mVcardFilename.isEmpty()) {
0952         mVcardFilename =
0953             QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1Char('/') + ident.identityName() + QLatin1StringView(".vcf");
0954         QFileInfo fileInfo(mVcardFilename);
0955         QDir().mkpath(fileInfo.absolutePath());
0956     } else {
0957         // Convert path.
0958         const QString path =
0959             QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + QLatin1Char('/') + ident.identityName() + QLatin1StringView(".vcf");
0960         if (QFileInfo::exists(path) && (mVcardFilename != path)) {
0961             mVcardFilename = path;
0962         }
0963     }
0964     mAttachMyVCard->setChecked(ident.attachVcard());
0965     QString defaultDomainName = ident.defaultDomainName();
0966     if (defaultDomainName.isEmpty()) {
0967         defaultDomainName = QHostInfo::localHostName();
0968     }
0969     mDefaultDomainEdit->setText(defaultDomainName);
0970 
0971     // "Templates" tab:
0972     uint identity = ident.uoid();
0973     QString iid = TemplateParser::TemplatesConfiguration::configIdString(identity);
0974     TemplateParser::Templates t(iid);
0975     mCustom->setChecked(t.useCustomTemplates());
0976     mWidget->loadFromIdentity(identity);
0977 
0978     // "Signature" tab:
0979     mSignatureConfigurator->setImageLocation(ident);
0980     mSignatureConfigurator->setSignature(ident.signature());
0981     mXFaceConfigurator->setXFace(ident.xface());
0982     mXFaceConfigurator->setXFaceEnabled(ident.isXFaceEnabled());
0983     mXFaceConfigurator->setFace(ident.face());
0984     mXFaceConfigurator->setFaceEnabled(ident.isFaceEnabled());
0985 }
0986 
0987 void IdentityDialog::unregisterSpecialCollection(qint64 colId)
0988 {
0989     // ID is not enough to unregister a special collection, we need the
0990     // resource set as well.
0991     auto fetch = new Akonadi::CollectionFetchJob(Akonadi::Collection(colId), Akonadi::CollectionFetchJob::Base, this);
0992     connect(fetch, &Akonadi::CollectionFetchJob::collectionsReceived, this, [](const Akonadi::Collection::List &cols) {
0993         if (cols.count() != 1) {
0994             return;
0995         }
0996         Akonadi::SpecialMailCollections::self()->unregisterCollection(cols.first());
0997     });
0998 }
0999 
1000 void IdentityDialog::updateIdentity(KIdentityManagementCore::Identity &ident)
1001 {
1002     // "General" tab:
1003     ident.setFullName(mNameEdit->text());
1004     ident.setOrganization(mOrganizationEdit->text());
1005     QString email = mEmailEdit->text().trimmed();
1006     ident.setPrimaryEmailAddress(email);
1007     const QStringList aliases = mAliasEdit->items();
1008     QStringList result;
1009     for (const QString &alias : aliases) {
1010         const QString aliasTrimmed = alias.trimmed();
1011         if (aliasTrimmed.isEmpty()) {
1012             continue;
1013         }
1014         if (aliasTrimmed == email) {
1015             continue;
1016         }
1017         result.append(alias);
1018     }
1019     ident.setEmailAliases(result);
1020     // "Cryptography" tab:
1021     ident.setPGPSigningKey(mPGPSigningKeyRequester->currentKey().primaryFingerprint());
1022     ident.setPGPEncryptionKey(mPGPEncryptionKeyRequester->currentKey().primaryFingerprint());
1023     ident.setSMIMESigningKey(mSMIMESigningKeyRequester->currentKey().primaryFingerprint());
1024     ident.setSMIMEEncryptionKey(mSMIMEEncryptionKeyRequester->currentKey().primaryFingerprint());
1025     ident.setPreferredCryptoMessageFormat(QLatin1StringView(Kleo::cryptoMessageFormatToString(cb2format(mPreferredCryptoMessageFormat->currentIndex()))));
1026     ident.setAutocryptEnabled(mAutocrypt->isChecked());
1027     ident.setAutocryptPrefer(mAutocryptPrefer->isChecked());
1028     ident.setEncryptionOverride(mOverrideDefault->isChecked());
1029     ident.setPgpAutoSign(mAutoSign->isChecked());
1030     ident.setPgpAutoEncrypt(mAutoEncrypt->isChecked());
1031     ident.setWarnNotEncrypt(mWarnNotEncrypt->isChecked());
1032     ident.setWarnNotEncrypt(mWarnNotEncrypt->isChecked());
1033     // "Advanced" tab:
1034     ident.setReplyToAddr(mReplyToEdit->text());
1035     ident.setBcc(mBccEdit->text());
1036     ident.setCc(mCcEdit->text());
1037     ident.setTransport(mTransportCheck->isChecked() ? QString::number(mTransportCombo->currentTransportId()) : QString());
1038     ident.setDictionary(mDictionaryCombo->currentDictionaryName());
1039     ident.setDisabledFcc(!mSentMailFolderCheck->isChecked());
1040     Akonadi::Collection collection = mFccFolderRequester->collection();
1041     if (!ident.fcc().isEmpty()) {
1042         unregisterSpecialCollection(ident.fcc().toLongLong());
1043     }
1044     if (collection.isValid()) {
1045         ident.setFcc(QString::number(collection.id()));
1046         auto attribute = collection.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing);
1047         attribute->setIconName(QStringLiteral("mail-folder-sent"));
1048         // It will also start a CollectionModifyJob
1049         Akonadi::SpecialMailCollections::self()->registerCollection(Akonadi::SpecialMailCollections::SentMail, collection);
1050     } else {
1051         ident.setFcc(QString());
1052     }
1053 
1054     collection = mDraftsFolderRequester->collection();
1055     if (!ident.drafts().isEmpty()) {
1056         unregisterSpecialCollection(ident.drafts().toLongLong());
1057     }
1058     if (collection.isValid()) {
1059         ident.setDrafts(QString::number(collection.id()));
1060         auto attribute = collection.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing);
1061         attribute->setIconName(QStringLiteral("document-properties"));
1062         // It will also start a CollectionModifyJob
1063         Akonadi::SpecialMailCollections::self()->registerCollection(Akonadi::SpecialMailCollections::Drafts, collection);
1064     } else {
1065         ident.setDrafts(QString());
1066     }
1067 
1068     collection = mTemplatesFolderRequester->collection();
1069     if (ident.templates().isEmpty()) {
1070         unregisterSpecialCollection(ident.templates().toLongLong());
1071     }
1072     if (collection.isValid()) {
1073         ident.setTemplates(QString::number(collection.id()));
1074         auto attribute = collection.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing);
1075         attribute->setIconName(QStringLiteral("document-new"));
1076         // It will also start a CollectionModifyJob
1077         Akonadi::SpecialMailCollections::self()->registerCollection(Akonadi::SpecialMailCollections::Templates, collection);
1078         new Akonadi::CollectionModifyJob(collection);
1079     } else {
1080         ident.setTemplates(QString());
1081     }
1082     ident.setVCardFile(mVcardFilename);
1083     ident.setAutocorrectionLanguage(mAutoCorrectionLanguage->language());
1084     updateVcardButton();
1085     ident.setAttachVcard(mAttachMyVCard->isChecked());
1086     // Add default ?
1087     ident.setDefaultDomainName(mDefaultDomainEdit->text());
1088 
1089     // "Templates" tab:
1090     uint identity = ident.uoid();
1091     QString iid = TemplateParser::TemplatesConfiguration::configIdString(identity);
1092     TemplateParser::Templates t(iid);
1093     qCDebug(KMAIL_LOG) << "use custom templates for identity" << identity << ":" << mCustom->isChecked();
1094     t.setUseCustomTemplates(mCustom->isChecked());
1095     t.save();
1096     mWidget->saveToIdentity(identity);
1097 
1098     // "Signature" tab:
1099     ident.setSignature(mSignatureConfigurator->signature());
1100     ident.setXFace(mXFaceConfigurator->xface());
1101     ident.setXFaceEnabled(mXFaceConfigurator->isXFaceEnabled());
1102     ident.setFace(mXFaceConfigurator->face());
1103     ident.setFaceEnabled(mXFaceConfigurator->isFaceEnabled());
1104 }
1105 
1106 void IdentityDialog::slotEditVcard()
1107 {
1108     if (QFileInfo::exists(mVcardFilename)) {
1109         editVcard(mVcardFilename);
1110     } else {
1111         if (!MailCommon::Kernel::self()->kernelIsRegistered()) {
1112             return;
1113         }
1114         KIdentityManagementCore::IdentityManager *manager = KernelIf->identityManager();
1115 
1116         QPointer<IdentityAddVcardDialog> dlg = new IdentityAddVcardDialog(manager->shadowIdentities(), this);
1117         if (dlg->exec()) {
1118             IdentityAddVcardDialog::DuplicateMode mode = dlg->duplicateMode();
1119             switch (mode) {
1120             case IdentityAddVcardDialog::Empty:
1121                 editVcard(mVcardFilename);
1122                 break;
1123             case IdentityAddVcardDialog::ExistingEntry: {
1124                 KIdentityManagementCore::Identity ident = manager->modifyIdentityForName(dlg->duplicateVcardFromIdentity());
1125                 const QString filename = ident.vCardFile();
1126                 if (!filename.isEmpty()) {
1127                     QFile::copy(filename, mVcardFilename);
1128                 }
1129                 editVcard(mVcardFilename);
1130                 break;
1131             }
1132             case IdentityAddVcardDialog::FromExistingVCard: {
1133                 const QString filename = dlg->existingVCard().path();
1134                 if (!filename.isEmpty()) {
1135                     mVcardFilename = filename;
1136                 }
1137                 editVcard(mVcardFilename);
1138                 break;
1139             }
1140             }
1141         }
1142         delete dlg;
1143     }
1144 }
1145 
1146 void IdentityDialog::editVcard(const QString &filename)
1147 {
1148     QPointer<IdentityEditVcardDialog> dlg = new IdentityEditVcardDialog(filename, this);
1149     connect(dlg.data(), &IdentityEditVcardDialog::vcardRemoved, this, &IdentityDialog::slotVCardRemoved);
1150     if (dlg->exec()) {
1151         mVcardFilename = dlg->saveVcard();
1152     }
1153     updateVcardButton();
1154     delete dlg;
1155 }
1156 
1157 void IdentityDialog::slotVCardRemoved()
1158 {
1159     mVcardFilename.clear();
1160 }
1161 
1162 void IdentityDialog::updateVcardButton()
1163 {
1164     if (mVcardFilename.isEmpty() || !QFileInfo::exists(mVcardFilename)) {
1165         mEditVCard->setText(i18n("Create..."));
1166     } else {
1167         mEditVCard->setText(i18n("Edit..."));
1168     }
1169 }
1170 }
1171 
1172 #include "identitydialog.moc"
1173 
1174 #include "moc_identitydialog.cpp"