Minor
[kdepim.git] / kmail / src / identity / identitydialog.cpp
blobfa1f1d03c9d9646257ae344327d0d6fc28fcef42
1 /*
2 identitydialog.cpp
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2002 Marc Mutz <mutz@kde.org>
6 Copyright (c) 2014-2016 Laurent Montel <montel@kde.org>
8 KMail is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License, version 2, as
10 published by the Free Software Foundation.
12 KMail is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
30 your version.
33 #include "identitydialog.h"
34 #include "identityeditvcarddialog.h"
35 #include "identityaddvcarddialog.h"
36 #include "identityinvalidfolder.h"
37 #include "identityfolderrequester.h"
39 #include "MessageComposer/MessageComposerSettings"
41 #include <KIdentityManagement/kidentitymanagement/identitymanager.h>
43 // other KMail headers:
44 #include "xfaceconfigurator.h"
45 #include <KEditListWidget>
46 #include "mailcommon/folderrequester.h"
47 #ifndef KCM_KPIMIDENTITIES_STANDALONE
48 #include "settings/kmailsettings.h"
49 #include "kmkernel.h"
50 #endif
52 #include "mailcommon/mailkernel.h"
54 #include "job/addressvalidationjob.h"
55 #include "MessageComposer/Kleo_Util"
56 #include <MessageCore/StringUtil>
57 #include "TemplateParser/TemplatesConfiguration"
58 #include "templatesconfiguration_kfg.h"
59 // other kdepim headers:
60 #include <KIdentityManagement/kidentitymanagement/identity.h>
61 #include <KIdentityManagement/kidentitymanagement/signatureconfigurator.h>
63 #include "PimCommon/AutoCorrectionLanguage"
64 #include <PimCommon/PimUtil>
66 #include <Libkdepim/AddresseeLineEdit>
67 // libkleopatra:
68 #include <Libkleo/KeySelectionCombo>
69 #include <Libkleo/CryptoBackendFactory>
70 #include <Libkleo/DefaultKeyFilter>
71 #include <Libkleo/KeyGenerationJob>
72 #include <Libkleo/ProgressDialog>
74 // gpgme++
75 #include <gpgme++/keygenerationresult.h>
77 #include <KEmailAddress>
78 #include <Libkdepim/EmailValidator>
79 #include <MailTransport/mailtransport/transport.h>
80 #include <MailTransport/mailtransport/transportmanager.h>
81 #include <MailTransport/mailtransport/transportcombobox.h>
82 using MailTransport::TransportManager;
84 // other KDE headers:
85 #include <KLocalizedString>
86 #include <kmessagebox.h>
87 #include <kfileitem.h>
88 #include <qurl.h>
89 #include "kmail_debug.h"
90 #include <QPushButton>
91 #include <kcombobox.h>
92 #include <QTabWidget>
93 #include <QIcon>
94 #include <sonnet/dictionarycombobox.h>
95 #include <KHelpClient>
97 // Qt headers:
98 #include <QLabel>
99 #include <QCheckBox>
100 #include <QVBoxLayout>
101 #include <QGridLayout>
102 #include <QFile>
103 #include <QHostInfo>
104 #include <QToolButton>
105 #include <QFileInfo>
106 #include <QDir>
108 // other headers:
109 #include <gpgme++/key.h>
110 #include <iterator>
111 #include <algorithm>
113 #include <AkonadiCore/entitydisplayattribute.h>
114 #include <AkonadiCore/collectionmodifyjob.h>
115 #include <QStandardPaths>
116 #include <KConfigGroup>
117 #include <QDialogButtonBox>
119 using namespace KPIM;
120 using namespace MailTransport;
121 using namespace MailCommon;
123 namespace KMail
126 class KeySelectionCombo : public Kleo::KeySelectionCombo
128 Q_OBJECT
130 public:
131 enum KeyType {
132 SigningKey,
133 EncryptionKey
136 KeySelectionCombo(KeyType keyType, GpgME::Protocol protocol, QWidget *parent);
137 ~KeySelectionCombo();
139 void setIdentity(const QString &name, const QString &email);
141 void init() Q_DECL_OVERRIDE;
143 private Q_SLOTS:
144 void onCustomItemSelected(const QVariant &type);
146 private:
147 QString mEmail;
148 QString mName;
149 KeyType mKeyType;
150 GpgME::Protocol mProtocol;
154 class KeyGenerationJob : public Kleo::Job
156 Q_OBJECT
158 public:
159 KeyGenerationJob(const QString &name, const QString &email, KeySelectionCombo *parent);
160 ~KeyGenerationJob();
162 void slotCancel() Q_DECL_OVERRIDE;
163 void start();
165 private Q_SLOTS:
166 void keyGenerated(const GpgME::KeyGenerationResult &result);
168 private:
169 QString mName;
170 QString mEmail;
171 Kleo::Job *mJob;
176 KeyGenerationJob::KeyGenerationJob(const QString &name, const QString &email, KeySelectionCombo *parent)
177 : Kleo::Job(parent)
178 , mName(name)
179 , mEmail(email)
180 , mJob(Q_NULLPTR)
184 KeyGenerationJob::~KeyGenerationJob()
188 void KeyGenerationJob::slotCancel()
190 if (mJob) {
191 mJob->slotCancel();
195 void KeyGenerationJob::start()
197 const QString args = QStringLiteral("<GnupgKeyParms format=\"internal\">\n"
198 "%ask-passphrase\n"
199 "key-type: RSA\n"
200 "key-length: 2048\n"
201 "key-usage: sign\n"
202 "subkey-type: RSA\n"
203 "subkey-length: 2048\n"
204 "subkey-usage: encrypt\n"
205 "name-email: %1\n"
206 "name-real: %2\n"
207 "</GnupgKeyParms>").arg(mEmail, mName);
209 auto job = Kleo::CryptoBackendFactory::instance()->openpgp()->keyGenerationJob();
210 connect(job, &Kleo::KeyGenerationJob::result,
211 this, &KeyGenerationJob::keyGenerated);
212 job->start(args);
213 mJob = job;
216 void KeyGenerationJob::keyGenerated(const GpgME::KeyGenerationResult &result)
218 mJob = Q_NULLPTR;
219 if (result.error()) {
220 KMessageBox::error(qobject_cast<QWidget*>(parent()),
221 i18n("Error while generating new key pair: %1", QString::fromUtf8(result.error().asString())),
222 i18n("Key Generation Error"));
223 Q_EMIT done();
224 return;
227 KeySelectionCombo *combo = qobject_cast<KeySelectionCombo*>(parent());
228 combo->setDefaultKey(QLatin1String(result.fingerprint()));
229 connect(combo, &KeySelectionCombo::keyListingFinished,
230 this, &KeyGenerationJob::done);
231 combo->refreshKeys();
234 KeySelectionCombo::KeySelectionCombo(KeyType keyType, GpgME::Protocol protocol, QWidget *parent)
235 : Kleo::KeySelectionCombo(parent)
236 , mKeyType(keyType)
237 , mProtocol(protocol)
241 KeySelectionCombo::~KeySelectionCombo()
245 void KeySelectionCombo::setIdentity(const QString &name, const QString &email)
247 mName = name;
248 mEmail = email;
249 setIdFilter(email);
252 void KeySelectionCombo::init()
254 Kleo::KeySelectionCombo::init();
256 boost::shared_ptr<Kleo::DefaultKeyFilter> keyFilter(new Kleo::DefaultKeyFilter);
257 keyFilter->setIsOpenPGP(mProtocol == GpgME::OpenPGP ? Kleo::DefaultKeyFilter::Set : Kleo::DefaultKeyFilter::NotSet);
258 if (mKeyType == SigningKey) {
259 keyFilter->setCanSign(Kleo::DefaultKeyFilter::Set);
260 } else {
261 keyFilter->setCanEncrypt(Kleo::DefaultKeyFilter::Set);
263 keyFilter->setHasSecret(Kleo::DefaultKeyFilter::Set);
264 setKeyFilter(keyFilter);
266 prependCustomItem(QIcon(), i18n("No key"), QStringLiteral("no-key"));
267 if (mProtocol == GpgME::OpenPGP) {
268 appendCustomItem(QIcon::fromTheme(QStringLiteral("password-generate")),
269 i18n("Generate a new key pair"), QStringLiteral("generate-new-key"));
272 connect(this, &KeySelectionCombo::customItemSelected,
273 this, &KeySelectionCombo::onCustomItemSelected);
276 void KeySelectionCombo::onCustomItemSelected(const QVariant &type)
278 const QString typeStr = type.toString();
279 if (type == QLatin1String("no-key")) {
280 return;
281 } else if (type == QLatin1String("generate-new-key")) {
282 auto job = new KeyGenerationJob(mName, mEmail, this);
283 new Kleo::ProgressDialog(job, i18n("Generating new key pair..."), parentWidget());
284 setEnabled(false);
285 connect(job, &KeyGenerationJob::done,
286 this, [this]() { setEnabled(true); });
287 job->start();
293 IdentityDialog::IdentityDialog(QWidget *parent)
294 : QDialog(parent)
296 setWindowTitle(i18n("Edit Identity"));
297 QVBoxLayout *mainLayout = new QVBoxLayout(this);
299 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help);
300 connect(buttonBox->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &IdentityDialog::slotHelp);
301 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
302 okButton->setDefault(true);
303 okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
304 connect(buttonBox, &QDialogButtonBox::accepted, this, &IdentityDialog::slotAccepted);
305 connect(buttonBox, &QDialogButtonBox::rejected, this, &IdentityDialog::reject);
306 okButton->setDefault(true);
308 // tmp. vars:
309 QWidget *tab;
310 QLabel *label;
311 int row;
312 QGridLayout *glay;
313 QString msg;
316 // Tab Widget: General
318 row = -1;
319 QWidget *page = new QWidget(this);
320 mainLayout->addWidget(page);
321 mainLayout->addWidget(buttonBox);
322 QVBoxLayout *vlay = new QVBoxLayout(page);
323 vlay->setMargin(0);
324 mTabWidget = new QTabWidget(page);
325 mTabWidget->setObjectName(QStringLiteral("config-identity-tab"));
326 vlay->addWidget(mTabWidget);
328 tab = new QWidget(mTabWidget);
329 mTabWidget->addTab(tab, i18nc("@title:tab General identity settings.", "General"));
330 glay = new QGridLayout(tab);
331 glay->setRowStretch(3, 1);
332 glay->setColumnStretch(1, 1);
334 // "Name" line edit and label:
335 ++row;
336 mNameEdit = new KLineEdit(tab);
337 glay->addWidget(mNameEdit, row, 1);
338 label = new QLabel(i18n("&Your name:"), tab);
339 label->setBuddy(mNameEdit);
340 glay->addWidget(label, row, 0);
341 msg = i18n("<qt><h3>Your name</h3>"
342 "<p>This field should contain your name as you would like "
343 "it to appear in the email header that is sent out;</p>"
344 "<p>if you leave this blank your real name will not "
345 "appear, only the email address.</p></qt>");
346 label->setWhatsThis(msg);
347 mNameEdit->setWhatsThis(msg);
349 // "Organization" line edit and label:
350 ++row;
351 mOrganizationEdit = new KLineEdit(tab);
352 glay->addWidget(mOrganizationEdit, row, 1);
353 label = new QLabel(i18n("Organi&zation:"), tab);
354 label->setBuddy(mOrganizationEdit);
355 glay->addWidget(label, row, 0);
356 msg = i18n("<qt><h3>Organization</h3>"
357 "<p>This field should have the name of your organization "
358 "if you would like it to be shown in the email header that "
359 "is sent out.</p>"
360 "<p>It is safe (and normal) to leave this blank.</p></qt>");
361 label->setWhatsThis(msg);
362 mOrganizationEdit->setWhatsThis(msg);
364 // "Email Address" line edit and label:
365 // (row 3: spacer)
366 ++row;
367 mEmailEdit = new KLineEdit(tab);
368 glay->addWidget(mEmailEdit, row, 1);
369 label = new QLabel(i18n("&Email address:"), tab);
370 label->setBuddy(mEmailEdit);
371 glay->addWidget(label, row, 0);
372 msg = i18n("<qt><h3>Email address</h3>"
373 "<p>This field should have your full email address.</p>"
374 "<p>This address is the primary one, used for all outgoing mail. "
375 "If you have more than one address, either create a new identity, "
376 "or add additional alias addresses in the field below.</p>"
377 "<p>If you leave this blank, or get it wrong, people "
378 "will have trouble replying to you.</p></qt>");
379 label->setWhatsThis(msg);
380 mEmailEdit->setWhatsThis(msg);
382 KPIM::EmailValidator *emailValidator = new KPIM::EmailValidator(this);
383 mEmailEdit->setValidator(emailValidator);
385 // "Email Aliases" string text edit and label:
386 ++row;
387 mAliasEdit = new KEditListWidget(tab);
389 KPIM::EmailValidator *emailValidator1 = new KPIM::EmailValidator(this);
390 mAliasEdit->lineEdit()->setValidator(emailValidator1);
392 glay->addWidget(mAliasEdit, row, 1);
393 label = new QLabel(i18n("Email a&liases:"), tab);
394 label->setBuddy(mAliasEdit);
395 glay->addWidget(label, row, 0, Qt::AlignTop);
396 msg = i18n("<qt><h3>Email aliases</h3>"
397 "<p>This field contains alias addresses that should also "
398 "be considered as belonging to this identity (as opposed "
399 "to representing a different identity).</p>"
400 "<p>Example:</p>"
401 "<table>"
402 "<tr><th>Primary address:</th><td>first.last@example.org</td></tr>"
403 "<tr><th>Aliases:</th><td>first@example.org<br>last@example.org</td></tr>"
404 "</table>"
405 "<p>Type one alias address per line.</p></qt>");
406 label->setToolTip(msg);
407 mAliasEdit->setWhatsThis(msg);
410 // Tab Widget: Cryptography
412 row = -1;
413 mCryptographyTab = tab = new QWidget(mTabWidget);
414 mTabWidget->addTab(tab, i18n("Cryptography"));
415 glay = new QGridLayout(tab);
416 glay->setColumnStretch(1, 1);
418 // "OpenPGP Signature Key" requester and label:
419 ++row;
420 mPGPSigningKeyRequester = new KeySelectionCombo(KeySelectionCombo::SigningKey, GpgME::OpenPGP, tab);
421 msg = i18n("<qt><p>The OpenPGP key you choose here will be used "
422 "to digitally sign messages. You can also use GnuPG keys.</p>"
423 "<p>You can leave this blank, but KMail will not be able "
424 "to digitally sign emails using OpenPGP; "
425 "normal mail functions will not be affected.</p>"
426 "<p>You can find out more about keys at <a>http://www.gnupg.org</a></p></qt>");
427 label = new QLabel(i18n("OpenPGP signing key:"), tab);
428 label->setBuddy(mPGPSigningKeyRequester);
429 mPGPSigningKeyRequester->setWhatsThis(msg);
430 label->setWhatsThis(msg);
432 glay->addWidget(label, row, 0);
433 glay->addWidget(mPGPSigningKeyRequester, row, 1);
435 // "OpenPGP Encryption Key" requester and label:
436 ++row;
437 mPGPEncryptionKeyRequester = new KeySelectionCombo(KeySelectionCombo::EncryptionKey, GpgME::OpenPGP, tab);
438 msg = i18n("<qt><p>The OpenPGP key you choose here will be used "
439 "to encrypt messages to yourself and for the \"Attach My Public Key\" "
440 "feature in the composer. You can also use GnuPG keys.</p>"
441 "<p>You can leave this blank, but KMail will not be able "
442 "to encrypt copies of outgoing messages to you using OpenPGP; "
443 "normal mail functions will not be affected.</p>"
444 "<p>You can find out more about keys at <a>http://www.gnupg.org</a></p></qt>");
445 label = new QLabel(i18n("OpenPGP encryption key:"), tab);
446 label->setBuddy(mPGPEncryptionKeyRequester);
447 mPGPEncryptionKeyRequester->setWhatsThis(msg);
448 label->setWhatsThis(msg);
450 glay->addWidget(label, row, 0);
451 glay->addWidget(mPGPEncryptionKeyRequester, row, 1);
453 // "S/MIME Signature Key" requester and label:
454 ++row;
455 mSMIMESigningKeyRequester = new KeySelectionCombo(KeySelectionCombo::SigningKey, GpgME::CMS, tab);
456 msg = i18n("<qt><p>The S/MIME (X.509) certificate you choose here will be used "
457 "to digitally sign messages.</p>"
458 "<p>You can leave this blank, but KMail will not be able "
459 "to digitally sign emails using S/MIME; "
460 "normal mail functions will not be affected.</p></qt>");
461 label = new QLabel(i18n("S/MIME signing certificate:"), tab);
462 label->setBuddy(mSMIMESigningKeyRequester);
463 mSMIMESigningKeyRequester->setWhatsThis(msg);
464 label->setWhatsThis(msg);
465 glay->addWidget(label, row, 0);
466 glay->addWidget(mSMIMESigningKeyRequester, row, 1);
468 const Kleo::CryptoBackend::Protocol *smimeProtocol
469 = Kleo::CryptoBackendFactory::instance()->smime();
471 label->setEnabled(smimeProtocol);
472 mSMIMESigningKeyRequester->setEnabled(smimeProtocol);
474 // "S/MIME Encryption Key" requester and label:
475 ++row;
476 mSMIMEEncryptionKeyRequester = new KeySelectionCombo(KeySelectionCombo::EncryptionKey, GpgME::CMS, tab);
477 msg = i18n("<qt><p>The S/MIME certificate you choose here will be used "
478 "to encrypt messages to yourself and for the \"Attach My Certificate\" "
479 "feature in the composer.</p>"
480 "<p>You can leave this blank, but KMail will not be able "
481 "to encrypt copies of outgoing messages to you using S/MIME; "
482 "normal mail functions will not be affected.</p></qt>");
483 label = new QLabel(i18n("S/MIME encryption certificate:"), tab);
484 label->setBuddy(mSMIMEEncryptionKeyRequester);
485 mSMIMEEncryptionKeyRequester->setWhatsThis(msg);
486 label->setWhatsThis(msg);
488 glay->addWidget(label, row, 0);
489 glay->addWidget(mSMIMEEncryptionKeyRequester, row, 1);
491 label->setEnabled(smimeProtocol);
492 mSMIMEEncryptionKeyRequester->setEnabled(smimeProtocol);
494 // "Preferred Crypto Message Format" combobox and label:
495 ++row;
496 mPreferredCryptoMessageFormat = new KComboBox(tab);
497 mPreferredCryptoMessageFormat->setEditable(false);
498 QStringList l;
499 l << Kleo::cryptoMessageFormatToLabel(Kleo::AutoFormat)
500 << Kleo::cryptoMessageFormatToLabel(Kleo::InlineOpenPGPFormat)
501 << Kleo::cryptoMessageFormatToLabel(Kleo::OpenPGPMIMEFormat)
502 << Kleo::cryptoMessageFormatToLabel(Kleo::SMIMEFormat)
503 << Kleo::cryptoMessageFormatToLabel(Kleo::SMIMEOpaqueFormat);
504 mPreferredCryptoMessageFormat->addItems(l);
505 label = new QLabel(i18nc("preferred format of encrypted messages", "Preferred format:"), tab);
506 label->setBuddy(mPreferredCryptoMessageFormat);
508 glay->addWidget(label, row, 0);
509 glay->addWidget(mPreferredCryptoMessageFormat, row, 1);
511 ++row;
512 mAutoSign = new QCheckBox(i18n("Automatically sign messages"));
513 glay->addWidget(mAutoSign, row, 0);
515 ++row;
516 mAutoEncrypt = new QCheckBox(i18n("Automatically encrypt messages when possible"));
517 glay->addWidget(mAutoEncrypt, row, 0);
519 ++row;
520 glay->setRowStretch(row, 1);
523 // Tab Widget: Advanced
525 row = -1;
526 tab = new QWidget(mTabWidget);
527 QVBoxLayout *advancedMainLayout = new QVBoxLayout(tab);
528 mIdentityInvalidFolder = new IdentityInvalidFolder(tab);
529 advancedMainLayout->addWidget(mIdentityInvalidFolder);
530 mTabWidget->addTab(tab, i18nc("@title:tab Advanced identity settings.", "Advanced"));
531 glay = new QGridLayout;
532 advancedMainLayout->addLayout(glay);
533 // the last (empty) row takes all the remaining space
534 glay->setColumnStretch(1, 1);
536 // "Reply-To Address" line edit and label:
537 ++row;
538 mReplyToEdit = new KPIM::AddresseeLineEdit(tab, true);
539 mReplyToEdit->setClearButtonShown(true);
540 mReplyToEdit->setObjectName(QStringLiteral("mReplyToEdit"));
541 glay->addWidget(mReplyToEdit, row, 1);
542 label = new QLabel(i18n("&Reply-To address:"), tab);
543 label->setBuddy(mReplyToEdit);
544 glay->addWidget(label, row, 0);
545 msg = i18n("<qt><h3>Reply-To addresses</h3>"
546 "<p>This sets the <tt>Reply-to:</tt> header to contain a "
547 "different email address to the normal <tt>From:</tt> "
548 "address.</p>"
549 "<p>This can be useful when you have a group of people "
550 "working together in similar roles. For example, you "
551 "might want any emails sent to have your email in the "
552 "<tt>From:</tt> field, but any responses to go to "
553 "a group address.</p>"
554 "<p>If in doubt, leave this field blank.</p></qt>");
555 label->setWhatsThis(msg);
556 mReplyToEdit->setWhatsThis(msg);
558 // "CC addresses" line edit and label:
559 ++row;
560 mCcEdit = new KPIM::AddresseeLineEdit(tab, true);
561 mCcEdit->setClearButtonShown(true);
562 mCcEdit->setObjectName(QStringLiteral("mCcEdit"));
563 glay->addWidget(mCcEdit, row, 1);
564 label = new QLabel(i18n("&CC addresses:"), tab);
565 label->setBuddy(mCcEdit);
566 glay->addWidget(label, row, 0);
567 msg = i18n("<qt><h3>CC (Carbon Copy) addresses</h3>"
568 "<p>The addresses that you enter here will be added to each "
569 "outgoing mail that is sent with this identity.</p>"
570 "<p>This is commonly used to send a copy of each sent message to "
571 "another account of yours.</p>"
572 "<p>To specify more than one address, use commas to separate "
573 "the list of CC recipients.</p>"
574 "<p>If in doubt, leave this field blank.</p></qt>");
575 label->setWhatsThis(msg);
576 mCcEdit->setWhatsThis(msg);
578 // "BCC addresses" line edit and label:
579 ++row;
580 mBccEdit = new KPIM::AddresseeLineEdit(tab, true);
581 mBccEdit->setClearButtonShown(true);
582 mBccEdit->setObjectName(QStringLiteral("mBccEdit"));
583 glay->addWidget(mBccEdit, row, 1);
584 label = new QLabel(i18n("&BCC addresses:"), tab);
585 label->setBuddy(mBccEdit);
586 glay->addWidget(label, row, 0);
587 msg = i18n("<qt><h3>BCC (Blind Carbon Copy) addresses</h3>"
588 "<p>The addresses that you enter here will be added to each "
589 "outgoing mail that is sent with this identity. They will not "
590 "be visible to other recipients.</p>"
591 "<p>This is commonly used to send a copy of each sent message to "
592 "another account of yours.</p>"
593 "<p>To specify more than one address, use commas to separate "
594 "the list of BCC recipients.</p>"
595 "<p>If in doubt, leave this field blank.</p></qt>");
596 label->setWhatsThis(msg);
597 mBccEdit->setWhatsThis(msg);
599 // "Dictionary" combo box and label:
600 ++row;
601 mDictionaryCombo = new Sonnet::DictionaryComboBox(tab);
602 glay->addWidget(mDictionaryCombo, row, 1);
603 label = new QLabel(i18n("D&ictionary:"), tab);
604 label->setBuddy(mDictionaryCombo);
605 glay->addWidget(label, row, 0);
607 // "Sent-mail Folder" combo box and label:
608 ++row;
609 mFccFolderRequester = new IdentityFolderRequester(tab);
610 mFccFolderRequester->setShowOutbox(false);
611 glay->addWidget(mFccFolderRequester, row, 1);
612 mSentMailFolderCheck = new QCheckBox(i18n("Sent-mail &folder:"), tab);
613 glay->addWidget(mSentMailFolderCheck, row, 0);
614 connect(mSentMailFolderCheck, &QCheckBox::toggled, mFccFolderRequester, &MailCommon::FolderRequester::setEnabled);
616 // "Drafts Folder" combo box and label:
617 ++row;
618 mDraftsFolderRequester = new IdentityFolderRequester(tab);
619 mDraftsFolderRequester->setShowOutbox(false);
620 glay->addWidget(mDraftsFolderRequester, row, 1);
621 label = new QLabel(i18n("&Drafts folder:"), tab);
622 label->setBuddy(mDraftsFolderRequester);
623 glay->addWidget(label, row, 0);
625 // "Templates Folder" combo box and label:
626 ++row;
627 mTemplatesFolderRequester = new IdentityFolderRequester(tab);
628 mTemplatesFolderRequester->setShowOutbox(false);
629 glay->addWidget(mTemplatesFolderRequester, row, 1);
630 label = new QLabel(i18n("&Templates folder:"), tab);
631 label->setBuddy(mTemplatesFolderRequester);
632 glay->addWidget(label, row, 0);
634 // "Special transport" combobox and label:
635 ++row;
636 mTransportCheck = new QCheckBox(i18n("Outgoing Account:"), tab);
637 glay->addWidget(mTransportCheck, row, 0);
638 mTransportCombo = new TransportComboBox(tab);
639 mTransportCombo->setEnabled(false); // since !mTransportCheck->isChecked()
640 glay->addWidget(mTransportCombo, row, 1);
641 connect(mTransportCheck, &QCheckBox::toggled, mTransportCombo, &MailTransport::TransportComboBox::setEnabled);
643 ++row;
644 mAttachMyVCard = new QCheckBox(i18n("Attach my vCard to message"), tab);
645 glay->addWidget(mAttachMyVCard, row, 0);
646 mEditVCard = new QPushButton(i18n("Create..."), tab);
647 connect(mEditVCard, &QPushButton::clicked, this, &IdentityDialog::slotEditVcard);
648 glay->addWidget(mEditVCard, row, 1);
650 ++row;
651 mAutoCorrectionLanguage = new PimCommon::AutoCorrectionLanguage(tab);
652 glay->addWidget(mAutoCorrectionLanguage, row, 1);
653 label = new QLabel(i18n("Autocorrection language:"), tab);
654 label->setBuddy(mAutoCorrectionLanguage);
655 glay->addWidget(label, row, 0);
657 // "default domain" input field:
658 ++row;
659 QHBoxLayout *hbox = new QHBoxLayout;
660 mDefaultDomainEdit = new KLineEdit(tab);
661 mDefaultDomainEdit->setClearButtonShown(true);
662 hbox->addWidget(mDefaultDomainEdit);
663 QToolButton *restoreDefaultDomainName = new QToolButton;
664 restoreDefaultDomainName->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
665 restoreDefaultDomainName->setToolTip(i18n("Restore default domain name"));
666 hbox->addWidget(restoreDefaultDomainName);
667 connect(restoreDefaultDomainName, &QToolButton::clicked, this, &IdentityDialog::slotRefreshDefaultDomainName);
668 glay->addLayout(hbox, row, 1);
669 label = new QLabel(i18n("Defaul&t domain:"), tab);
670 label->setBuddy(mDefaultDomainEdit);
671 glay->addWidget(label, row, 0);
673 // and now: add QWhatsThis:
674 msg = i18n("<qt><p>The default domain is used to complete email "
675 "addresses that only consist of the user's name."
676 "</p></qt>");
677 label->setWhatsThis(msg);
678 mDefaultDomainEdit->setWhatsThis(msg);
680 ++row;
681 glay->setRowStretch(row, 1);
683 // the last row is a spacer
686 // Tab Widget: Templates
688 tab = new QWidget(mTabWidget);
689 vlay = new QVBoxLayout(tab);
691 QHBoxLayout *tlay = new QHBoxLayout();
692 vlay->addLayout(tlay);
694 mCustom = new QCheckBox(i18n("&Use custom message templates for this identity"), tab);
695 tlay->addWidget(mCustom, Qt::AlignLeft);
697 mWidget = new TemplateParser::TemplatesConfiguration(tab, QStringLiteral("identity-templates"));
698 mWidget->setEnabled(false);
700 // Move the help label outside of the templates configuration widget,
701 // so that the help can be read even if the widget is not enabled.
702 tlay->addStretch(9);
703 tlay->addWidget(mWidget->helpLabel(), Qt::AlignRight);
705 vlay->addWidget(mWidget);
707 QHBoxLayout *btns = new QHBoxLayout();
708 mCopyGlobal = new QPushButton(i18n("&Copy Global Templates"), tab);
709 mCopyGlobal->setEnabled(false);
710 btns->addWidget(mCopyGlobal);
711 vlay->addLayout(btns);
712 connect(mCustom, &QCheckBox::toggled, mWidget, &TemplateParser::TemplatesConfiguration::setEnabled);
713 connect(mCustom, &QCheckBox::toggled, mCopyGlobal, &QPushButton::setEnabled);
714 connect(mCopyGlobal, &QPushButton::clicked, this, &IdentityDialog::slotCopyGlobal);
715 mTabWidget->addTab(tab, i18n("Templates"));
718 // Tab Widget: Signature
720 mSignatureConfigurator = new KIdentityManagement::SignatureConfigurator(mTabWidget);
721 mTabWidget->addTab(mSignatureConfigurator, i18n("Signature"));
724 // Tab Widget: Picture
727 mXFaceConfigurator = new XFaceConfigurator(mTabWidget);
728 mTabWidget->addTab(mXFaceConfigurator, i18n("Picture"));
730 #ifndef KCM_KPIMIDENTITIES_STANDALONE
731 resize(KMailSettings::self()->identityDialogSize());
732 #endif
733 mNameEdit->setFocus();
735 connect(mTabWidget, &QTabWidget::currentChanged, this, &IdentityDialog::slotAboutToShow);
738 IdentityDialog::~IdentityDialog()
740 #ifndef KCM_KPIMIDENTITIES_STANDALONE
741 KMailSettings::self()->setIdentityDialogSize(size());
742 #endif
745 void IdentityDialog::slotHelp()
747 PimCommon::Util::invokeHelp(QStringLiteral("kmail/configure-identity.html"));
750 void IdentityDialog::slotAboutToShow(int index)
752 QWidget *w = mTabWidget->widget(index);
753 if (w == mCryptographyTab) {
754 // set the configured email address as initial query of the key
755 // requesters:
756 const QString name = mNameEdit->text().trimmed();
757 const QString email = mEmailEdit->text().trimmed();
759 mPGPEncryptionKeyRequester->setIdentity(name, email);
760 mPGPSigningKeyRequester->setIdentity(name, email);
761 mSMIMEEncryptionKeyRequester->setIdentity(name, email);
762 mSMIMESigningKeyRequester->setIdentity(name, email);
766 void IdentityDialog::slotCopyGlobal()
768 mWidget->loadFromGlobal();
771 void IdentityDialog::slotRefreshDefaultDomainName()
773 mDefaultDomainEdit->setText(QHostInfo::localHostName());
776 void IdentityDialog::slotAccepted()
778 const QStringList aliases = mAliasEdit->items();
779 foreach (const QString &alias, aliases) {
780 if (!KEmailAddress::isValidSimpleAddress(alias)) {
781 const QString errorMsg(KEmailAddress::simpleEmailAddressErrorMsg());
782 KMessageBox::sorry(this, errorMsg, i18n("Invalid Email Alias \"%1\"", alias));
783 return;
787 // Validate email addresses
788 const QString email = mEmailEdit->text().trimmed();
789 if (!KEmailAddress::isValidSimpleAddress(email)) {
790 const QString errorMsg(KEmailAddress::simpleEmailAddressErrorMsg());
791 KMessageBox::sorry(this, errorMsg, i18n("Invalid Email Address"));
792 return;
795 // Check if the 'Reply to' and 'BCC' recipients are valid
796 const QString recipients = mReplyToEdit->text().trimmed() + QLatin1String(", ") + mBccEdit->text().trimmed() + QLatin1String(", ") + mCcEdit->text().trimmed();
797 AddressValidationJob *job = new AddressValidationJob(recipients, this, this);
798 //Use default Value
799 job->setDefaultDomain(mDefaultDomainEdit->text());
800 job->setProperty("email", email);
801 connect(job, &AddressValidationJob::result, this, &IdentityDialog::slotDelayedButtonClicked);
802 job->start();
805 bool IdentityDialog::keyMatchesEmailAddress(const GpgME::Key &key, const QString &email_)
807 if (key.isNull()) {
808 return true;
810 const QString email = email_.trimmed().toLower();
811 const auto uids = key.userIDs();
812 for (const auto &uid : uids) {
813 QString em = QString::fromUtf8(uid.email() ? uid.email() : uid.id());
814 if (em.isEmpty()) {
815 continue;
817 if (em[0] == QLatin1Char('<')) {
818 em = em.mid(1, em.length() - 2);
820 if (em.toLower() == email) {
821 return true;
825 return false;
828 void IdentityDialog::slotDelayedButtonClicked(KJob *job)
830 const AddressValidationJob *validationJob = qobject_cast<AddressValidationJob *>(job);
832 // Abort if one of the recipient addresses is invalid
833 if (!validationJob->isValid()) {
834 return;
837 const QString email = validationJob->property("email").toString();
839 const GpgME::Key &pgpSigningKey = mPGPSigningKeyRequester->currentKey();
840 const GpgME::Key &pgpEncryptionKey = mPGPEncryptionKeyRequester->currentKey();
841 const GpgME::Key &smimeSigningKey = mSMIMESigningKeyRequester->currentKey();
842 const GpgME::Key &smimeEncryptionKey = mSMIMEEncryptionKeyRequester->currentKey();
844 QString msg;
845 bool err = false;
846 if (!keyMatchesEmailAddress(pgpSigningKey, email)) {
847 msg = i18n("One of the configured OpenPGP signing keys does not contain "
848 "any user ID with the configured email address for this "
849 "identity (%1).\n"
850 "This might result in warning messages on the receiving side "
851 "when trying to verify signatures made with this configuration.", email);
852 err = true;
853 } else if (!keyMatchesEmailAddress(pgpEncryptionKey, email)) {
854 msg = i18n("One of the configured OpenPGP encryption keys does not contain "
855 "any user ID with the configured email address for this "
856 "identity (%1).", email);
857 err = true;
858 } else if (!keyMatchesEmailAddress(smimeSigningKey, email)) {
859 msg = i18n("One of the configured S/MIME signing certificates does not contain "
860 "the configured email address for this "
861 "identity (%1).\n"
862 "This might result in warning messages on the receiving side "
863 "when trying to verify signatures made with this configuration.", email);
864 err = true;
865 } else if (!keyMatchesEmailAddress(smimeEncryptionKey, email)) {
866 msg = i18n("One of the configured S/MIME encryption certificates does not contain "
867 "the configured email address for this "
868 "identity (%1).", email);
869 err = true;
872 if (err) {
873 if (KMessageBox::warningContinueCancel(this, msg,
874 i18n("Email Address Not Found in Key/Certificates"),
875 KStandardGuiItem::cont(),
876 KStandardGuiItem::cancel(),
877 QStringLiteral("warn_email_not_in_certificate"))
878 != KMessageBox::Continue) {
879 return;
883 if (mSignatureConfigurator->isSignatureEnabled() &&
884 mSignatureConfigurator->signatureType() == Signature::FromFile) {
885 QFileInfo file(mSignatureConfigurator->filePath());
886 if (!file.isReadable()) {
887 KMessageBox::error(this, i18n("The signature file is not valid"));
888 return;
892 accept();
895 bool IdentityDialog::checkFolderExists(const QString &folderID)
897 const Akonadi::Collection folder = CommonKernel->collectionFromId(folderID.toLongLong());
898 return folder.isValid();
901 void IdentityDialog::setIdentity(KIdentityManagement::Identity &ident)
904 setWindowTitle(i18n("Edit Identity \"%1\"", ident.identityName()));
906 // "General" tab:
907 mNameEdit->setText(ident.fullName());
908 mOrganizationEdit->setText(ident.organization());
909 mEmailEdit->setText(ident.primaryEmailAddress());
910 mAliasEdit->insertStringList(ident.emailAliases());
912 // "Cryptography" tab:
913 mPGPSigningKeyRequester->setDefaultKey(QLatin1String(ident.pgpSigningKey()));
914 mPGPEncryptionKeyRequester->setDefaultKey(QLatin1String(ident.pgpEncryptionKey()));
915 mSMIMESigningKeyRequester->setDefaultKey(QLatin1String(ident.smimeSigningKey()));
916 mSMIMEEncryptionKeyRequester->setDefaultKey(QLatin1String(ident.smimeEncryptionKey()));
918 mPreferredCryptoMessageFormat->setCurrentIndex(format2cb(
919 Kleo::stringToCryptoMessageFormat(ident.preferredCryptoMessageFormat())));
920 mAutoSign->setChecked(ident.pgpAutoSign());
921 mAutoEncrypt->setChecked(ident.pgpAutoEncrypt());
923 // "Advanced" tab:
924 mReplyToEdit->setText(ident.replyToAddr());
925 mBccEdit->setText(ident.bcc());
926 mCcEdit->setText(ident.cc());
927 const int transportId = ident.transport().isEmpty() ? -1 : ident.transport().toInt();
928 const Transport *transport = TransportManager::self()->transportById(transportId, true);
929 mTransportCheck->setChecked(transportId != -1);
930 mTransportCombo->setEnabled(transportId != -1);
931 if (transport) {
932 mTransportCombo->setCurrentTransport(transport->id());
934 mDictionaryCombo->setCurrentByDictionaryName(ident.dictionary());
936 mSentMailFolderCheck->setChecked(!ident.disabledFcc());
937 mFccFolderRequester->setEnabled(mSentMailFolderCheck->isChecked());
938 bool foundNoExistingFolder = false;
939 if (ident.fcc().isEmpty() ||
940 !checkFolderExists(ident.fcc())) {
941 foundNoExistingFolder = true;
942 mFccFolderRequester->setIsInvalidFolder();
943 mFccFolderRequester->setCollection(CommonKernel->sentCollectionFolder());
944 } else {
945 mFccFolderRequester->setCollection(Akonadi::Collection(ident.fcc().toLongLong()));
947 if (ident.drafts().isEmpty() ||
948 !checkFolderExists(ident.drafts())) {
949 foundNoExistingFolder = true;
950 mDraftsFolderRequester->setIsInvalidFolder();
951 mDraftsFolderRequester->setCollection(CommonKernel->draftsCollectionFolder());
952 } else {
953 mDraftsFolderRequester->setCollection(Akonadi::Collection(ident.drafts().toLongLong()));
956 if (ident.templates().isEmpty() ||
957 !checkFolderExists(ident.templates())) {
958 foundNoExistingFolder = true;
959 mTemplatesFolderRequester->setIsInvalidFolder();
960 mTemplatesFolderRequester->setCollection(CommonKernel->templatesCollectionFolder());
962 } else {
963 mTemplatesFolderRequester->setCollection(Akonadi::Collection(ident.templates().toLongLong()));
965 if (foundNoExistingFolder) {
966 mIdentityInvalidFolder->setErrorMessage(i18n("Some custom folder for identity does not exist (anymore); therefore, default folders will be used."));
968 mVcardFilename = ident.vCardFile();
970 mAutoCorrectionLanguage->setLanguage(ident.autocorrectionLanguage());
971 updateVcardButton();
972 if (mVcardFilename.isEmpty()) {
973 mVcardFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + ident.identityName() + QLatin1String(".vcf");
974 QFileInfo fileInfo(mVcardFilename);
975 QDir().mkpath(fileInfo.absolutePath());
976 } else {
977 //Convert path.
978 const QString path = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + QLatin1Char('/') + ident.identityName() + QLatin1String(".vcf");
979 if (QFile(path).exists() && (mVcardFilename != path)) {
980 mVcardFilename = path;
983 mAttachMyVCard->setChecked(ident.attachVcard());
984 QString defaultDomainName = ident.defaultDomainName();
985 if (defaultDomainName.isEmpty()) {
986 defaultDomainName = QHostInfo::localHostName();
988 mDefaultDomainEdit->setText(defaultDomainName);
990 // "Templates" tab:
991 uint identity = ident.uoid();
992 QString iid = TemplateParser::TemplatesConfiguration::configIdString(identity);
993 TemplateParser::Templates t(iid);
994 mCustom->setChecked(t.useCustomTemplates());
995 mWidget->loadFromIdentity(identity);
997 // "Signature" tab:
998 mSignatureConfigurator->setImageLocation(ident);
999 mSignatureConfigurator->setSignature(ident.signature());
1000 mXFaceConfigurator->setXFace(ident.xface());
1001 mXFaceConfigurator->setXFaceEnabled(ident.isXFaceEnabled());
1004 void IdentityDialog::updateIdentity(KIdentityManagement::Identity &ident)
1006 // "General" tab:
1007 ident.setFullName(mNameEdit->text());
1008 ident.setOrganization(mOrganizationEdit->text());
1009 QString email = mEmailEdit->text();
1010 ident.setPrimaryEmailAddress(email);
1011 ident.setEmailAliases(mAliasEdit->items());
1012 // "Cryptography" tab:
1013 ident.setPGPSigningKey(mPGPSigningKeyRequester->currentKey().primaryFingerprint());
1014 ident.setPGPEncryptionKey(mPGPEncryptionKeyRequester->currentKey().primaryFingerprint());
1015 ident.setSMIMESigningKey(mSMIMESigningKeyRequester->currentKey().primaryFingerprint());
1016 ident.setSMIMEEncryptionKey(mSMIMEEncryptionKeyRequester->currentKey().primaryFingerprint());
1017 ident.setPreferredCryptoMessageFormat(
1018 QLatin1String(Kleo::cryptoMessageFormatToString(cb2format(mPreferredCryptoMessageFormat->currentIndex()))));
1019 ident.setPgpAutoSign(mAutoSign->isChecked());
1020 ident.setPgpAutoEncrypt(mAutoEncrypt->isChecked());
1021 // "Advanced" tab:
1022 ident.setReplyToAddr(mReplyToEdit->text());
1023 ident.setBcc(mBccEdit->text());
1024 ident.setCc(mCcEdit->text());
1025 ident.setTransport(mTransportCheck->isChecked() ? QString::number(mTransportCombo->currentTransportId())
1026 : QString());
1027 ident.setDictionary(mDictionaryCombo->currentDictionaryName());
1028 ident.setDisabledFcc(!mSentMailFolderCheck->isChecked());
1029 Akonadi::Collection collection = mFccFolderRequester->collection();
1030 if (collection.isValid()) {
1031 ident.setFcc(QString::number(collection.id()));
1032 Akonadi::EntityDisplayAttribute *attribute = collection.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing);
1033 attribute->setIconName(QStringLiteral("mail-folder-sent"));
1034 new Akonadi::CollectionModifyJob(collection);
1035 } else {
1036 ident.setFcc(QString());
1039 collection = mDraftsFolderRequester->collection();
1040 if (collection.isValid()) {
1041 ident.setDrafts(QString::number(collection.id()));
1042 Akonadi::EntityDisplayAttribute *attribute = collection.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing);
1043 attribute->setIconName(QStringLiteral("document-properties"));
1044 new Akonadi::CollectionModifyJob(collection);
1045 } else {
1046 ident.setDrafts(QString());
1049 collection = mTemplatesFolderRequester->collection();
1050 if (collection.isValid()) {
1051 ident.setTemplates(QString::number(collection.id()));
1052 Akonadi::EntityDisplayAttribute *attribute = collection.attribute<Akonadi::EntityDisplayAttribute>(Akonadi::Collection::AddIfMissing);
1053 attribute->setIconName(QStringLiteral("document-new"));
1054 new Akonadi::CollectionModifyJob(collection);
1055 } else {
1056 ident.setTemplates(QString());
1058 ident.setVCardFile(mVcardFilename);
1059 ident.setAutocorrectionLanguage(mAutoCorrectionLanguage->language());
1060 updateVcardButton();
1061 ident.setAttachVcard(mAttachMyVCard->isChecked());
1062 //Add default ?
1063 ident.setDefaultDomainName(mDefaultDomainEdit->text());
1065 // "Templates" tab:
1066 uint identity = ident.uoid();
1067 QString iid = TemplateParser::TemplatesConfiguration::configIdString(identity);
1068 TemplateParser::Templates t(iid);
1069 qCDebug(KMAIL_LOG) << "use custom templates for identity" << identity << ":" << mCustom->isChecked();
1070 t.setUseCustomTemplates(mCustom->isChecked());
1071 t.save();
1072 mWidget->saveToIdentity(identity);
1074 // "Signature" tab:
1075 ident.setSignature(mSignatureConfigurator->signature());
1076 ident.setXFace(mXFaceConfigurator->xface());
1077 ident.setXFaceEnabled(mXFaceConfigurator->isXFaceEnabled());
1080 void IdentityDialog::slotEditVcard()
1082 if (QFile(mVcardFilename).exists()) {
1083 editVcard(mVcardFilename);
1084 } else {
1085 if (!MailCommon::Kernel::self()->kernelIsRegistered()) {
1086 return;
1088 KIdentityManagement::IdentityManager *manager = KernelIf->identityManager();
1090 QPointer<IdentityAddVcardDialog> dlg = new IdentityAddVcardDialog(manager->shadowIdentities(), this);
1091 if (dlg->exec()) {
1092 IdentityAddVcardDialog::DuplicateMode mode = dlg->duplicateMode();
1093 switch (mode) {
1094 case IdentityAddVcardDialog::Empty: {
1095 editVcard(mVcardFilename);
1096 break;
1098 case IdentityAddVcardDialog::ExistingEntry: {
1099 KIdentityManagement::Identity ident = manager->modifyIdentityForName(dlg->duplicateVcardFromIdentity());
1100 const QString filename = ident.vCardFile();
1101 if (!filename.isEmpty()) {
1102 QFile::copy(filename, mVcardFilename);
1104 editVcard(mVcardFilename);
1105 break;
1107 case IdentityAddVcardDialog::FromExistingVCard: {
1108 const QString filename = dlg->existingVCard().path();
1109 if (!filename.isEmpty()) {
1110 mVcardFilename = filename;
1112 editVcard(mVcardFilename);
1113 break;
1117 delete dlg;
1121 void IdentityDialog::editVcard(const QString &filename)
1123 QPointer<IdentityEditVcardDialog> dlg = new IdentityEditVcardDialog(filename, this);
1124 connect(dlg.data(), &IdentityEditVcardDialog::vcardRemoved, this, &IdentityDialog::slotVCardRemoved);
1125 if (dlg->exec()) {
1126 mVcardFilename = dlg->saveVcard();
1128 updateVcardButton();
1129 delete dlg;
1132 void IdentityDialog::slotVCardRemoved()
1134 mVcardFilename.clear();
1137 void IdentityDialog::updateVcardButton()
1139 if (mVcardFilename.isEmpty() || !QFile(mVcardFilename).exists()) {
1140 mEditVCard->setText(i18n("Create..."));
1141 } else {
1142 mEditVCard->setText(i18n("Edit..."));
1148 #include "identitydialog.moc"