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
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"
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>
68 #include <Libkleo/KeySelectionCombo>
69 #include <Libkleo/CryptoBackendFactory>
70 #include <Libkleo/DefaultKeyFilter>
71 #include <Libkleo/DefaultKeyGenerationJob>
72 #include <Libkleo/ProgressDialog>
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
;
85 #include <KLocalizedString>
86 #include <kmessagebox.h>
87 #include <kfileitem.h>
89 #include "kmail_debug.h"
90 #include <QPushButton>
91 #include <kcombobox.h>
94 #include <sonnet/dictionarycombobox.h>
95 #include <KHelpClient>
100 #include <QVBoxLayout>
101 #include <QGridLayout>
104 #include <QToolButton>
109 #include <gpgme++/key.h>
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
;
126 class KeySelectionCombo
: public Kleo::KeySelectionCombo
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
;
144 void onCustomItemSelected(const QVariant
&type
);
150 GpgME::Protocol mProtocol
;
153 class KeyGenerationJob
: public Kleo::Job
158 KeyGenerationJob(const QString
&name
, const QString
&email
, KeySelectionCombo
*parent
);
161 void slotCancel() Q_DECL_OVERRIDE
;
165 void keyGenerated(const GpgME::KeyGenerationResult
&result
);
173 KeyGenerationJob::KeyGenerationJob(const QString
&name
, const QString
&email
, KeySelectionCombo
*parent
)
181 KeyGenerationJob::~KeyGenerationJob()
185 void KeyGenerationJob::slotCancel()
192 void KeyGenerationJob::start()
194 auto job
= new Kleo::DefaultKeyGenerationJob(this);
195 connect(job
, &Kleo::DefaultKeyGenerationJob::result
,
196 this, &KeyGenerationJob::keyGenerated
);
197 job
->start(mEmail
, mName
);
201 void KeyGenerationJob::keyGenerated(const GpgME::KeyGenerationResult
&result
)
204 if (result
.error()) {
205 KMessageBox::error(qobject_cast
<QWidget
*>(parent()),
206 i18n("Error while generating new key pair: %1", QString::fromUtf8(result
.error().asString())),
207 i18n("Key Generation Error"));
212 KeySelectionCombo
*combo
= qobject_cast
<KeySelectionCombo
*>(parent());
213 combo
->setDefaultKey(QLatin1String(result
.fingerprint()));
214 connect(combo
, &KeySelectionCombo::keyListingFinished
,
215 this, &KeyGenerationJob::done
);
216 combo
->refreshKeys();
219 KeySelectionCombo::KeySelectionCombo(KeyType keyType
, GpgME::Protocol protocol
, QWidget
*parent
)
220 : Kleo::KeySelectionCombo(parent
)
222 , mProtocol(protocol
)
226 KeySelectionCombo::~KeySelectionCombo()
230 void KeySelectionCombo::setIdentity(const QString
&name
, const QString
&email
)
237 void KeySelectionCombo::init()
239 Kleo::KeySelectionCombo::init();
241 boost::shared_ptr
<Kleo::DefaultKeyFilter
> keyFilter(new Kleo::DefaultKeyFilter
);
242 keyFilter
->setIsOpenPGP(mProtocol
== GpgME::OpenPGP
? Kleo::DefaultKeyFilter::Set
: Kleo::DefaultKeyFilter::NotSet
);
243 if (mKeyType
== SigningKey
) {
244 keyFilter
->setCanSign(Kleo::DefaultKeyFilter::Set
);
246 keyFilter
->setCanEncrypt(Kleo::DefaultKeyFilter::Set
);
248 keyFilter
->setHasSecret(Kleo::DefaultKeyFilter::Set
);
249 setKeyFilter(keyFilter
);
250 prependCustomItem(QIcon(), i18n("No key"), QStringLiteral("no-key"));
251 if (mProtocol
== GpgME::OpenPGP
) {
252 appendCustomItem(QIcon::fromTheme(QStringLiteral("password-generate")),
253 i18n("Generate a new key pair"), QStringLiteral("generate-new-key"));
256 connect(this, &KeySelectionCombo::customItemSelected
,
257 this, &KeySelectionCombo::onCustomItemSelected
);
260 void KeySelectionCombo::onCustomItemSelected(const QVariant
&type
)
262 if (type
== QLatin1String("no-key")) {
264 } else if (type
== QLatin1String("generate-new-key")) {
265 auto job
= new KeyGenerationJob(mName
, mEmail
, this);
266 new Kleo::ProgressDialog(job
, i18n("Generating new key pair..."), parentWidget());
268 connect(job
, &KeyGenerationJob::done
,
276 IdentityDialog::IdentityDialog(QWidget
*parent
)
279 setWindowTitle(i18n("Edit Identity"));
280 QVBoxLayout
*mainLayout
= new QVBoxLayout(this);
282 QDialogButtonBox
*buttonBox
= new QDialogButtonBox(QDialogButtonBox::Ok
| QDialogButtonBox::Cancel
| QDialogButtonBox::Help
);
283 connect(buttonBox
->button(QDialogButtonBox::Help
), &QPushButton::clicked
, this, &IdentityDialog::slotHelp
);
284 QPushButton
*okButton
= buttonBox
->button(QDialogButtonBox::Ok
);
285 okButton
->setDefault(true);
286 okButton
->setShortcut(Qt::CTRL
| Qt::Key_Return
);
287 connect(buttonBox
, &QDialogButtonBox::accepted
, this, &IdentityDialog::slotAccepted
);
288 connect(buttonBox
, &QDialogButtonBox::rejected
, this, &IdentityDialog::reject
);
298 // Tab Widget: General
301 QWidget
*page
= new QWidget(this);
302 mainLayout
->addWidget(page
);
303 mainLayout
->addWidget(buttonBox
);
304 QVBoxLayout
*vlay
= new QVBoxLayout(page
);
306 mTabWidget
= new QTabWidget(page
);
307 mTabWidget
->setObjectName(QStringLiteral("config-identity-tab"));
308 vlay
->addWidget(mTabWidget
);
310 tab
= new QWidget(mTabWidget
);
311 mTabWidget
->addTab(tab
, i18nc("@title:tab General identity settings.", "General"));
312 glay
= new QGridLayout(tab
);
313 glay
->setRowStretch(3, 1);
314 glay
->setColumnStretch(1, 1);
316 // "Name" line edit and label:
318 mNameEdit
= new KLineEdit(tab
);
319 glay
->addWidget(mNameEdit
, row
, 1);
320 label
= new QLabel(i18n("&Your name:"), tab
);
321 label
->setBuddy(mNameEdit
);
322 glay
->addWidget(label
, row
, 0);
323 msg
= i18n("<qt><h3>Your name</h3>"
324 "<p>This field should contain your name as you would like "
325 "it to appear in the email header that is sent out;</p>"
326 "<p>if you leave this blank your real name will not "
327 "appear, only the email address.</p></qt>");
328 label
->setWhatsThis(msg
);
329 mNameEdit
->setWhatsThis(msg
);
331 // "Organization" line edit and label:
333 mOrganizationEdit
= new KLineEdit(tab
);
334 glay
->addWidget(mOrganizationEdit
, row
, 1);
335 label
= new QLabel(i18n("Organi&zation:"), tab
);
336 label
->setBuddy(mOrganizationEdit
);
337 glay
->addWidget(label
, row
, 0);
338 msg
= i18n("<qt><h3>Organization</h3>"
339 "<p>This field should have the name of your organization "
340 "if you would like it to be shown in the email header that "
342 "<p>It is safe (and normal) to leave this blank.</p></qt>");
343 label
->setWhatsThis(msg
);
344 mOrganizationEdit
->setWhatsThis(msg
);
346 // "Email Address" line edit and label:
349 mEmailEdit
= new KLineEdit(tab
);
350 glay
->addWidget(mEmailEdit
, row
, 1);
351 label
= new QLabel(i18n("&Email address:"), tab
);
352 label
->setBuddy(mEmailEdit
);
353 glay
->addWidget(label
, row
, 0);
354 msg
= i18n("<qt><h3>Email address</h3>"
355 "<p>This field should have your full email address.</p>"
356 "<p>This address is the primary one, used for all outgoing mail. "
357 "If you have more than one address, either create a new identity, "
358 "or add additional alias addresses in the field below.</p>"
359 "<p>If you leave this blank, or get it wrong, people "
360 "will have trouble replying to you.</p></qt>");
361 label
->setWhatsThis(msg
);
362 mEmailEdit
->setWhatsThis(msg
);
364 KPIM::EmailValidator
*emailValidator
= new KPIM::EmailValidator(this);
365 mEmailEdit
->setValidator(emailValidator
);
367 // "Email Aliases" string text edit and label:
369 mAliasEdit
= new KEditListWidget(tab
);
371 KPIM::EmailValidator
*emailValidator1
= new KPIM::EmailValidator(this);
372 mAliasEdit
->lineEdit()->setValidator(emailValidator1
);
374 glay
->addWidget(mAliasEdit
, row
, 1);
375 label
= new QLabel(i18n("Email a&liases:"), tab
);
376 label
->setBuddy(mAliasEdit
);
377 glay
->addWidget(label
, row
, 0, Qt::AlignTop
);
378 msg
= i18n("<qt><h3>Email aliases</h3>"
379 "<p>This field contains alias addresses that should also "
380 "be considered as belonging to this identity (as opposed "
381 "to representing a different identity).</p>"
384 "<tr><th>Primary address:</th><td>first.last@example.org</td></tr>"
385 "<tr><th>Aliases:</th><td>first@example.org<br>last@example.org</td></tr>"
387 "<p>Type one alias address per line.</p></qt>");
388 label
->setToolTip(msg
);
389 mAliasEdit
->setWhatsThis(msg
);
392 // Tab Widget: Cryptography
395 mCryptographyTab
= tab
= new QWidget(mTabWidget
);
396 mTabWidget
->addTab(tab
, i18n("Cryptography"));
397 glay
= new QGridLayout(tab
);
398 glay
->setColumnStretch(1, 1);
400 // "OpenPGP Signature Key" requester and label:
402 mPGPSigningKeyRequester
= new KeySelectionCombo(KeySelectionCombo::SigningKey
, GpgME::OpenPGP
, tab
);
403 msg
= i18n("<qt><p>The OpenPGP key you choose here will be used "
404 "to digitally sign messages. You can also use GnuPG keys.</p>"
405 "<p>You can leave this blank, but KMail will not be able "
406 "to digitally sign emails using OpenPGP; "
407 "normal mail functions will not be affected.</p>"
408 "<p>You can find out more about keys at <a>http://www.gnupg.org</a></p></qt>");
409 label
= new QLabel(i18n("OpenPGP signing key:"), tab
);
410 label
->setBuddy(mPGPSigningKeyRequester
);
411 mPGPSigningKeyRequester
->setWhatsThis(msg
);
412 label
->setWhatsThis(msg
);
414 glay
->addWidget(label
, row
, 0);
415 glay
->addWidget(mPGPSigningKeyRequester
, row
, 1);
417 // "OpenPGP Encryption Key" requester and label:
419 mPGPEncryptionKeyRequester
= new KeySelectionCombo(KeySelectionCombo::EncryptionKey
, GpgME::OpenPGP
, tab
);
420 msg
= i18n("<qt><p>The OpenPGP key you choose here will be used "
421 "to encrypt messages to yourself and for the \"Attach My Public Key\" "
422 "feature in the composer. You can also use GnuPG keys.</p>"
423 "<p>You can leave this blank, but KMail will not be able "
424 "to encrypt copies of outgoing messages to you 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 encryption key:"), tab
);
428 label
->setBuddy(mPGPEncryptionKeyRequester
);
429 mPGPEncryptionKeyRequester
->setWhatsThis(msg
);
430 label
->setWhatsThis(msg
);
432 glay
->addWidget(label
, row
, 0);
433 glay
->addWidget(mPGPEncryptionKeyRequester
, row
, 1);
435 // "S/MIME Signature Key" requester and label:
437 mSMIMESigningKeyRequester
= new KeySelectionCombo(KeySelectionCombo::SigningKey
, GpgME::CMS
, tab
);
438 msg
= i18n("<qt><p>The S/MIME (X.509) certificate you choose here will be used "
439 "to digitally sign messages.</p>"
440 "<p>You can leave this blank, but KMail will not be able "
441 "to digitally sign emails using S/MIME; "
442 "normal mail functions will not be affected.</p></qt>");
443 label
= new QLabel(i18n("S/MIME signing certificate:"), tab
);
444 label
->setBuddy(mSMIMESigningKeyRequester
);
445 mSMIMESigningKeyRequester
->setWhatsThis(msg
);
446 label
->setWhatsThis(msg
);
447 glay
->addWidget(label
, row
, 0);
448 glay
->addWidget(mSMIMESigningKeyRequester
, row
, 1);
450 const Kleo::CryptoBackend::Protocol
*smimeProtocol
451 = Kleo::CryptoBackendFactory::instance()->smime();
453 label
->setEnabled(smimeProtocol
);
454 mSMIMESigningKeyRequester
->setEnabled(smimeProtocol
);
456 // "S/MIME Encryption Key" requester and label:
458 mSMIMEEncryptionKeyRequester
= new KeySelectionCombo(KeySelectionCombo::EncryptionKey
, GpgME::CMS
, tab
);
459 msg
= i18n("<qt><p>The S/MIME certificate you choose here will be used "
460 "to encrypt messages to yourself and for the \"Attach My Certificate\" "
461 "feature in the composer.</p>"
462 "<p>You can leave this blank, but KMail will not be able "
463 "to encrypt copies of outgoing messages to you using S/MIME; "
464 "normal mail functions will not be affected.</p></qt>");
465 label
= new QLabel(i18n("S/MIME encryption certificate:"), tab
);
466 label
->setBuddy(mSMIMEEncryptionKeyRequester
);
467 mSMIMEEncryptionKeyRequester
->setWhatsThis(msg
);
468 label
->setWhatsThis(msg
);
470 glay
->addWidget(label
, row
, 0);
471 glay
->addWidget(mSMIMEEncryptionKeyRequester
, row
, 1);
473 label
->setEnabled(smimeProtocol
);
474 mSMIMEEncryptionKeyRequester
->setEnabled(smimeProtocol
);
476 // "Preferred Crypto Message Format" combobox and label:
478 mPreferredCryptoMessageFormat
= new KComboBox(tab
);
479 mPreferredCryptoMessageFormat
->setEditable(false);
481 l
<< Kleo::cryptoMessageFormatToLabel(Kleo::AutoFormat
)
482 << Kleo::cryptoMessageFormatToLabel(Kleo::InlineOpenPGPFormat
)
483 << Kleo::cryptoMessageFormatToLabel(Kleo::OpenPGPMIMEFormat
)
484 << Kleo::cryptoMessageFormatToLabel(Kleo::SMIMEFormat
)
485 << Kleo::cryptoMessageFormatToLabel(Kleo::SMIMEOpaqueFormat
);
486 mPreferredCryptoMessageFormat
->addItems(l
);
487 label
= new QLabel(i18nc("preferred format of encrypted messages", "Preferred format:"), tab
);
488 label
->setBuddy(mPreferredCryptoMessageFormat
);
490 glay
->addWidget(label
, row
, 0);
491 glay
->addWidget(mPreferredCryptoMessageFormat
, row
, 1);
494 mAutoSign
= new QCheckBox(i18n("Automatically sign messages"));
495 glay
->addWidget(mAutoSign
, row
, 0);
498 mAutoEncrypt
= new QCheckBox(i18n("Automatically encrypt messages when possible"));
499 glay
->addWidget(mAutoEncrypt
, row
, 0);
502 glay
->setRowStretch(row
, 1);
505 // Tab Widget: Advanced
508 tab
= new QWidget(mTabWidget
);
509 QVBoxLayout
*advancedMainLayout
= new QVBoxLayout(tab
);
510 mIdentityInvalidFolder
= new IdentityInvalidFolder(tab
);
511 advancedMainLayout
->addWidget(mIdentityInvalidFolder
);
512 mTabWidget
->addTab(tab
, i18nc("@title:tab Advanced identity settings.", "Advanced"));
513 glay
= new QGridLayout
;
514 advancedMainLayout
->addLayout(glay
);
515 // the last (empty) row takes all the remaining space
516 glay
->setColumnStretch(1, 1);
518 // "Reply-To Address" line edit and label:
520 mReplyToEdit
= new KPIM::AddresseeLineEdit(tab
, true);
521 mReplyToEdit
->setClearButtonShown(true);
522 mReplyToEdit
->setObjectName(QStringLiteral("mReplyToEdit"));
523 glay
->addWidget(mReplyToEdit
, row
, 1);
524 label
= new QLabel(i18n("&Reply-To address:"), tab
);
525 label
->setBuddy(mReplyToEdit
);
526 glay
->addWidget(label
, row
, 0);
527 msg
= i18n("<qt><h3>Reply-To addresses</h3>"
528 "<p>This sets the <tt>Reply-to:</tt> header to contain a "
529 "different email address to the normal <tt>From:</tt> "
531 "<p>This can be useful when you have a group of people "
532 "working together in similar roles. For example, you "
533 "might want any emails sent to have your email in the "
534 "<tt>From:</tt> field, but any responses to go to "
535 "a group address.</p>"
536 "<p>If in doubt, leave this field blank.</p></qt>");
537 label
->setWhatsThis(msg
);
538 mReplyToEdit
->setWhatsThis(msg
);
540 // "CC addresses" line edit and label:
542 mCcEdit
= new KPIM::AddresseeLineEdit(tab
, true);
543 mCcEdit
->setClearButtonShown(true);
544 mCcEdit
->setObjectName(QStringLiteral("mCcEdit"));
545 glay
->addWidget(mCcEdit
, row
, 1);
546 label
= new QLabel(i18n("&CC addresses:"), tab
);
547 label
->setBuddy(mCcEdit
);
548 glay
->addWidget(label
, row
, 0);
549 msg
= i18n("<qt><h3>CC (Carbon Copy) addresses</h3>"
550 "<p>The addresses that you enter here will be added to each "
551 "outgoing mail that is sent with this identity.</p>"
552 "<p>This is commonly used to send a copy of each sent message to "
553 "another account of yours.</p>"
554 "<p>To specify more than one address, use commas to separate "
555 "the list of CC recipients.</p>"
556 "<p>If in doubt, leave this field blank.</p></qt>");
557 label
->setWhatsThis(msg
);
558 mCcEdit
->setWhatsThis(msg
);
560 // "BCC addresses" line edit and label:
562 mBccEdit
= new KPIM::AddresseeLineEdit(tab
, true);
563 mBccEdit
->setClearButtonShown(true);
564 mBccEdit
->setObjectName(QStringLiteral("mBccEdit"));
565 glay
->addWidget(mBccEdit
, row
, 1);
566 label
= new QLabel(i18n("&BCC addresses:"), tab
);
567 label
->setBuddy(mBccEdit
);
568 glay
->addWidget(label
, row
, 0);
569 msg
= i18n("<qt><h3>BCC (Blind Carbon Copy) addresses</h3>"
570 "<p>The addresses that you enter here will be added to each "
571 "outgoing mail that is sent with this identity. They will not "
572 "be visible to other recipients.</p>"
573 "<p>This is commonly used to send a copy of each sent message to "
574 "another account of yours.</p>"
575 "<p>To specify more than one address, use commas to separate "
576 "the list of BCC recipients.</p>"
577 "<p>If in doubt, leave this field blank.</p></qt>");
578 label
->setWhatsThis(msg
);
579 mBccEdit
->setWhatsThis(msg
);
581 // "Dictionary" combo box and label:
583 mDictionaryCombo
= new Sonnet::DictionaryComboBox(tab
);
584 glay
->addWidget(mDictionaryCombo
, row
, 1);
585 label
= new QLabel(i18n("D&ictionary:"), tab
);
586 label
->setBuddy(mDictionaryCombo
);
587 glay
->addWidget(label
, row
, 0);
589 // "Sent-mail Folder" combo box and label:
591 mFccFolderRequester
= new IdentityFolderRequester(tab
);
592 mFccFolderRequester
->setShowOutbox(false);
593 glay
->addWidget(mFccFolderRequester
, row
, 1);
594 mSentMailFolderCheck
= new QCheckBox(i18n("Sent-mail &folder:"), tab
);
595 glay
->addWidget(mSentMailFolderCheck
, row
, 0);
596 connect(mSentMailFolderCheck
, &QCheckBox::toggled
, mFccFolderRequester
, &MailCommon::FolderRequester::setEnabled
);
598 // "Drafts Folder" combo box and label:
600 mDraftsFolderRequester
= new IdentityFolderRequester(tab
);
601 mDraftsFolderRequester
->setShowOutbox(false);
602 glay
->addWidget(mDraftsFolderRequester
, row
, 1);
603 label
= new QLabel(i18n("&Drafts folder:"), tab
);
604 label
->setBuddy(mDraftsFolderRequester
);
605 glay
->addWidget(label
, row
, 0);
607 // "Templates Folder" combo box and label:
609 mTemplatesFolderRequester
= new IdentityFolderRequester(tab
);
610 mTemplatesFolderRequester
->setShowOutbox(false);
611 glay
->addWidget(mTemplatesFolderRequester
, row
, 1);
612 label
= new QLabel(i18n("&Templates folder:"), tab
);
613 label
->setBuddy(mTemplatesFolderRequester
);
614 glay
->addWidget(label
, row
, 0);
616 // "Special transport" combobox and label:
618 mTransportCheck
= new QCheckBox(i18n("Outgoing Account:"), tab
);
619 glay
->addWidget(mTransportCheck
, row
, 0);
620 mTransportCombo
= new TransportComboBox(tab
);
621 mTransportCombo
->setEnabled(false); // since !mTransportCheck->isChecked()
622 glay
->addWidget(mTransportCombo
, row
, 1);
623 connect(mTransportCheck
, &QCheckBox::toggled
, mTransportCombo
, &MailTransport::TransportComboBox::setEnabled
);
626 mAttachMyVCard
= new QCheckBox(i18n("Attach my vCard to message"), tab
);
627 glay
->addWidget(mAttachMyVCard
, row
, 0);
628 mEditVCard
= new QPushButton(i18n("Create..."), tab
);
629 connect(mEditVCard
, &QPushButton::clicked
, this, &IdentityDialog::slotEditVcard
);
630 glay
->addWidget(mEditVCard
, row
, 1);
633 mAutoCorrectionLanguage
= new PimCommon::AutoCorrectionLanguage(tab
);
634 glay
->addWidget(mAutoCorrectionLanguage
, row
, 1);
635 label
= new QLabel(i18n("Autocorrection language:"), tab
);
636 label
->setBuddy(mAutoCorrectionLanguage
);
637 glay
->addWidget(label
, row
, 0);
639 // "default domain" input field:
641 QHBoxLayout
*hbox
= new QHBoxLayout
;
642 mDefaultDomainEdit
= new KLineEdit(tab
);
643 mDefaultDomainEdit
->setClearButtonShown(true);
644 hbox
->addWidget(mDefaultDomainEdit
);
645 QToolButton
*restoreDefaultDomainName
= new QToolButton
;
646 restoreDefaultDomainName
->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
647 restoreDefaultDomainName
->setToolTip(i18n("Restore default domain name"));
648 hbox
->addWidget(restoreDefaultDomainName
);
649 connect(restoreDefaultDomainName
, &QToolButton::clicked
, this, &IdentityDialog::slotRefreshDefaultDomainName
);
650 glay
->addLayout(hbox
, row
, 1);
651 label
= new QLabel(i18n("Defaul&t domain:"), tab
);
652 label
->setBuddy(mDefaultDomainEdit
);
653 glay
->addWidget(label
, row
, 0);
655 // and now: add QWhatsThis:
656 msg
= i18n("<qt><p>The default domain is used to complete email "
657 "addresses that only consist of the user's name."
659 label
->setWhatsThis(msg
);
660 mDefaultDomainEdit
->setWhatsThis(msg
);
663 glay
->setRowStretch(row
, 1);
665 // the last row is a spacer
668 // Tab Widget: Templates
670 tab
= new QWidget(mTabWidget
);
671 vlay
= new QVBoxLayout(tab
);
673 QHBoxLayout
*tlay
= new QHBoxLayout();
674 vlay
->addLayout(tlay
);
676 mCustom
= new QCheckBox(i18n("&Use custom message templates for this identity"), tab
);
677 tlay
->addWidget(mCustom
, Qt::AlignLeft
);
679 mWidget
= new TemplateParser::TemplatesConfiguration(tab
, QStringLiteral("identity-templates"));
680 mWidget
->setEnabled(false);
682 // Move the help label outside of the templates configuration widget,
683 // so that the help can be read even if the widget is not enabled.
685 tlay
->addWidget(mWidget
->helpLabel(), Qt::AlignRight
);
687 vlay
->addWidget(mWidget
);
689 QHBoxLayout
*btns
= new QHBoxLayout();
690 mCopyGlobal
= new QPushButton(i18n("&Copy Global Templates"), tab
);
691 mCopyGlobal
->setEnabled(false);
692 btns
->addWidget(mCopyGlobal
);
693 vlay
->addLayout(btns
);
694 connect(mCustom
, &QCheckBox::toggled
, mWidget
, &TemplateParser::TemplatesConfiguration::setEnabled
);
695 connect(mCustom
, &QCheckBox::toggled
, mCopyGlobal
, &QPushButton::setEnabled
);
696 connect(mCopyGlobal
, &QPushButton::clicked
, this, &IdentityDialog::slotCopyGlobal
);
697 mTabWidget
->addTab(tab
, i18n("Templates"));
700 // Tab Widget: Signature
702 mSignatureConfigurator
= new KIdentityManagement::SignatureConfigurator(mTabWidget
);
703 mTabWidget
->addTab(mSignatureConfigurator
, i18n("Signature"));
706 // Tab Widget: Picture
709 mXFaceConfigurator
= new XFaceConfigurator(mTabWidget
);
710 mTabWidget
->addTab(mXFaceConfigurator
, i18n("Picture"));
712 #ifndef KCM_KPIMIDENTITIES_STANDALONE
713 resize(KMailSettings::self()->identityDialogSize());
715 mNameEdit
->setFocus();
717 connect(mTabWidget
, &QTabWidget::currentChanged
, this, &IdentityDialog::slotAboutToShow
);
720 IdentityDialog::~IdentityDialog()
722 #ifndef KCM_KPIMIDENTITIES_STANDALONE
723 KMailSettings::self()->setIdentityDialogSize(size());
727 void IdentityDialog::slotHelp()
729 PimCommon::Util::invokeHelp(QStringLiteral("kmail/configure-identity.html"));
732 void IdentityDialog::slotAboutToShow(int index
)
734 QWidget
*w
= mTabWidget
->widget(index
);
735 if (w
== mCryptographyTab
) {
736 // set the configured email address as initial query of the key
738 const QString name
= mNameEdit
->text().trimmed();
739 const QString email
= mEmailEdit
->text().trimmed();
741 mPGPEncryptionKeyRequester
->setIdentity(name
, email
);
742 mPGPSigningKeyRequester
->setIdentity(name
, email
);
743 mSMIMEEncryptionKeyRequester
->setIdentity(name
, email
);
744 mSMIMESigningKeyRequester
->setIdentity(name
, email
);
748 void IdentityDialog::slotCopyGlobal()
750 mWidget
->loadFromGlobal();
753 void IdentityDialog::slotRefreshDefaultDomainName()
755 mDefaultDomainEdit
->setText(QHostInfo::localHostName());
758 void IdentityDialog::slotAccepted()
760 const QStringList aliases
= mAliasEdit
->items();
761 foreach (const QString
&alias
, aliases
) {
762 if (!KEmailAddress::isValidSimpleAddress(alias
)) {
763 const QString
errorMsg(KEmailAddress::simpleEmailAddressErrorMsg());
764 KMessageBox::sorry(this, errorMsg
, i18n("Invalid Email Alias \"%1\"", alias
));
769 // Validate email addresses
770 const QString email
= mEmailEdit
->text().trimmed();
771 if (!KEmailAddress::isValidSimpleAddress(email
)) {
772 const QString
errorMsg(KEmailAddress::simpleEmailAddressErrorMsg());
773 KMessageBox::sorry(this, errorMsg
, i18n("Invalid Email Address"));
777 // Check if the 'Reply to' and 'BCC' recipients are valid
778 const QString recipients
= mReplyToEdit
->text().trimmed() + QLatin1String(", ") + mBccEdit
->text().trimmed() + QLatin1String(", ") + mCcEdit
->text().trimmed();
779 AddressValidationJob
*job
= new AddressValidationJob(recipients
, this, this);
781 job
->setDefaultDomain(mDefaultDomainEdit
->text());
782 job
->setProperty("email", email
);
783 connect(job
, &AddressValidationJob::result
, this, &IdentityDialog::slotDelayedButtonClicked
);
787 bool IdentityDialog::keyMatchesEmailAddress(const GpgME::Key
&key
, const QString
&email_
)
792 const QString email
= email_
.trimmed().toLower();
793 const auto uids
= key
.userIDs();
794 for (const auto &uid
: uids
) {
795 QString em
= QString::fromUtf8(uid
.email() ? uid
.email() : uid
.id());
799 if (em
[0] == QLatin1Char('<')) {
800 em
= em
.mid(1, em
.length() - 2);
802 if (em
.toLower() == email
) {
810 void IdentityDialog::slotDelayedButtonClicked(KJob
*job
)
812 const AddressValidationJob
*validationJob
= qobject_cast
<AddressValidationJob
*>(job
);
814 // Abort if one of the recipient addresses is invalid
815 if (!validationJob
->isValid()) {
819 const QString email
= validationJob
->property("email").toString();
821 const GpgME::Key
&pgpSigningKey
= mPGPSigningKeyRequester
->currentKey();
822 const GpgME::Key
&pgpEncryptionKey
= mPGPEncryptionKeyRequester
->currentKey();
823 const GpgME::Key
&smimeSigningKey
= mSMIMESigningKeyRequester
->currentKey();
824 const GpgME::Key
&smimeEncryptionKey
= mSMIMEEncryptionKeyRequester
->currentKey();
828 if (!keyMatchesEmailAddress(pgpSigningKey
, email
)) {
829 msg
= i18n("One of the configured OpenPGP signing keys does not contain "
830 "any user ID with the configured email address for this "
832 "This might result in warning messages on the receiving side "
833 "when trying to verify signatures made with this configuration.", email
);
835 } else if (!keyMatchesEmailAddress(pgpEncryptionKey
, email
)) {
836 msg
= i18n("One of the configured OpenPGP encryption keys does not contain "
837 "any user ID with the configured email address for this "
838 "identity (%1).", email
);
840 } else if (!keyMatchesEmailAddress(smimeSigningKey
, email
)) {
841 msg
= i18n("One of the configured S/MIME signing certificates does not contain "
842 "the configured email address for this "
844 "This might result in warning messages on the receiving side "
845 "when trying to verify signatures made with this configuration.", email
);
847 } else if (!keyMatchesEmailAddress(smimeEncryptionKey
, email
)) {
848 msg
= i18n("One of the configured S/MIME encryption certificates does not contain "
849 "the configured email address for this "
850 "identity (%1).", email
);
855 if (KMessageBox::warningContinueCancel(this, msg
,
856 i18n("Email Address Not Found in Key/Certificates"),
857 KStandardGuiItem::cont(),
858 KStandardGuiItem::cancel(),
859 QStringLiteral("warn_email_not_in_certificate"))
860 != KMessageBox::Continue
) {
865 if (mSignatureConfigurator
->isSignatureEnabled() &&
866 mSignatureConfigurator
->signatureType() == Signature::FromFile
) {
867 QFileInfo
file(mSignatureConfigurator
->filePath());
868 if (!file
.isReadable()) {
869 KMessageBox::error(this, i18n("The signature file is not valid"));
877 bool IdentityDialog::checkFolderExists(const QString
&folderID
)
879 const Akonadi::Collection folder
= CommonKernel
->collectionFromId(folderID
.toLongLong());
880 return folder
.isValid();
883 void IdentityDialog::setIdentity(KIdentityManagement::Identity
&ident
)
886 setWindowTitle(i18n("Edit Identity \"%1\"", ident
.identityName()));
889 mNameEdit
->setText(ident
.fullName());
890 mOrganizationEdit
->setText(ident
.organization());
891 mEmailEdit
->setText(ident
.primaryEmailAddress());
892 mAliasEdit
->insertStringList(ident
.emailAliases());
894 // "Cryptography" tab:
895 mPGPSigningKeyRequester
->setDefaultKey(QLatin1String(ident
.pgpSigningKey()));
896 mPGPEncryptionKeyRequester
->setDefaultKey(QLatin1String(ident
.pgpEncryptionKey()));
897 mSMIMESigningKeyRequester
->setDefaultKey(QLatin1String(ident
.smimeSigningKey()));
898 mSMIMEEncryptionKeyRequester
->setDefaultKey(QLatin1String(ident
.smimeEncryptionKey()));
900 mPreferredCryptoMessageFormat
->setCurrentIndex(format2cb(
901 Kleo::stringToCryptoMessageFormat(ident
.preferredCryptoMessageFormat())));
902 mAutoSign
->setChecked(ident
.pgpAutoSign());
903 mAutoEncrypt
->setChecked(ident
.pgpAutoEncrypt());
906 mReplyToEdit
->setText(ident
.replyToAddr());
907 mBccEdit
->setText(ident
.bcc());
908 mCcEdit
->setText(ident
.cc());
909 const int transportId
= ident
.transport().isEmpty() ? -1 : ident
.transport().toInt();
910 const Transport
*transport
= TransportManager::self()->transportById(transportId
, true);
911 mTransportCheck
->setChecked(transportId
!= -1);
912 mTransportCombo
->setEnabled(transportId
!= -1);
914 mTransportCombo
->setCurrentTransport(transport
->id());
916 mDictionaryCombo
->setCurrentByDictionaryName(ident
.dictionary());
918 mSentMailFolderCheck
->setChecked(!ident
.disabledFcc());
919 mFccFolderRequester
->setEnabled(mSentMailFolderCheck
->isChecked());
920 bool foundNoExistingFolder
= false;
921 if (ident
.fcc().isEmpty() ||
922 !checkFolderExists(ident
.fcc())) {
923 foundNoExistingFolder
= true;
924 mFccFolderRequester
->setIsInvalidFolder();
925 mFccFolderRequester
->setCollection(CommonKernel
->sentCollectionFolder());
927 mFccFolderRequester
->setCollection(Akonadi::Collection(ident
.fcc().toLongLong()));
929 if (ident
.drafts().isEmpty() ||
930 !checkFolderExists(ident
.drafts())) {
931 foundNoExistingFolder
= true;
932 mDraftsFolderRequester
->setIsInvalidFolder();
933 mDraftsFolderRequester
->setCollection(CommonKernel
->draftsCollectionFolder());
935 mDraftsFolderRequester
->setCollection(Akonadi::Collection(ident
.drafts().toLongLong()));
938 if (ident
.templates().isEmpty() ||
939 !checkFolderExists(ident
.templates())) {
940 foundNoExistingFolder
= true;
941 mTemplatesFolderRequester
->setIsInvalidFolder();
942 mTemplatesFolderRequester
->setCollection(CommonKernel
->templatesCollectionFolder());
945 mTemplatesFolderRequester
->setCollection(Akonadi::Collection(ident
.templates().toLongLong()));
947 if (foundNoExistingFolder
) {
948 mIdentityInvalidFolder
->setErrorMessage(i18n("Some custom folder for identity does not exist (anymore); therefore, default folders will be used."));
950 mVcardFilename
= ident
.vCardFile();
952 mAutoCorrectionLanguage
->setLanguage(ident
.autocorrectionLanguage());
954 if (mVcardFilename
.isEmpty()) {
955 mVcardFilename
= QStandardPaths::writableLocation(QStandardPaths::DataLocation
) + QLatin1Char('/') + ident
.identityName() + QLatin1String(".vcf");
956 QFileInfo
fileInfo(mVcardFilename
);
957 QDir().mkpath(fileInfo
.absolutePath());
960 const QString path
= QStandardPaths::writableLocation(QStandardPaths::DataLocation
) + QLatin1Char('/') + ident
.identityName() + QLatin1String(".vcf");
961 if (QFile(path
).exists() && (mVcardFilename
!= path
)) {
962 mVcardFilename
= path
;
965 mAttachMyVCard
->setChecked(ident
.attachVcard());
966 QString defaultDomainName
= ident
.defaultDomainName();
967 if (defaultDomainName
.isEmpty()) {
968 defaultDomainName
= QHostInfo::localHostName();
970 mDefaultDomainEdit
->setText(defaultDomainName
);
973 uint identity
= ident
.uoid();
974 QString iid
= TemplateParser::TemplatesConfiguration::configIdString(identity
);
975 TemplateParser::Templates
t(iid
);
976 mCustom
->setChecked(t
.useCustomTemplates());
977 mWidget
->loadFromIdentity(identity
);
980 mSignatureConfigurator
->setImageLocation(ident
);
981 mSignatureConfigurator
->setSignature(ident
.signature());
982 mXFaceConfigurator
->setXFace(ident
.xface());
983 mXFaceConfigurator
->setXFaceEnabled(ident
.isXFaceEnabled());
986 void IdentityDialog::updateIdentity(KIdentityManagement::Identity
&ident
)
989 ident
.setFullName(mNameEdit
->text());
990 ident
.setOrganization(mOrganizationEdit
->text());
991 QString email
= mEmailEdit
->text();
992 ident
.setPrimaryEmailAddress(email
);
993 ident
.setEmailAliases(mAliasEdit
->items());
994 // "Cryptography" tab:
995 ident
.setPGPSigningKey(mPGPSigningKeyRequester
->currentKey().primaryFingerprint());
996 ident
.setPGPEncryptionKey(mPGPEncryptionKeyRequester
->currentKey().primaryFingerprint());
997 ident
.setSMIMESigningKey(mSMIMESigningKeyRequester
->currentKey().primaryFingerprint());
998 ident
.setSMIMEEncryptionKey(mSMIMEEncryptionKeyRequester
->currentKey().primaryFingerprint());
999 ident
.setPreferredCryptoMessageFormat(
1000 QLatin1String(Kleo::cryptoMessageFormatToString(cb2format(mPreferredCryptoMessageFormat
->currentIndex()))));
1001 ident
.setPgpAutoSign(mAutoSign
->isChecked());
1002 ident
.setPgpAutoEncrypt(mAutoEncrypt
->isChecked());
1004 ident
.setReplyToAddr(mReplyToEdit
->text());
1005 ident
.setBcc(mBccEdit
->text());
1006 ident
.setCc(mCcEdit
->text());
1007 ident
.setTransport(mTransportCheck
->isChecked() ? QString::number(mTransportCombo
->currentTransportId())
1009 ident
.setDictionary(mDictionaryCombo
->currentDictionaryName());
1010 ident
.setDisabledFcc(!mSentMailFolderCheck
->isChecked());
1011 Akonadi::Collection collection
= mFccFolderRequester
->collection();
1012 if (collection
.isValid()) {
1013 ident
.setFcc(QString::number(collection
.id()));
1014 Akonadi::EntityDisplayAttribute
*attribute
= collection
.attribute
<Akonadi::EntityDisplayAttribute
>(Akonadi::Collection::AddIfMissing
);
1015 attribute
->setIconName(QStringLiteral("mail-folder-sent"));
1016 new Akonadi::CollectionModifyJob(collection
);
1018 ident
.setFcc(QString());
1021 collection
= mDraftsFolderRequester
->collection();
1022 if (collection
.isValid()) {
1023 ident
.setDrafts(QString::number(collection
.id()));
1024 Akonadi::EntityDisplayAttribute
*attribute
= collection
.attribute
<Akonadi::EntityDisplayAttribute
>(Akonadi::Collection::AddIfMissing
);
1025 attribute
->setIconName(QStringLiteral("document-properties"));
1026 new Akonadi::CollectionModifyJob(collection
);
1028 ident
.setDrafts(QString());
1031 collection
= mTemplatesFolderRequester
->collection();
1032 if (collection
.isValid()) {
1033 ident
.setTemplates(QString::number(collection
.id()));
1034 Akonadi::EntityDisplayAttribute
*attribute
= collection
.attribute
<Akonadi::EntityDisplayAttribute
>(Akonadi::Collection::AddIfMissing
);
1035 attribute
->setIconName(QStringLiteral("document-new"));
1036 new Akonadi::CollectionModifyJob(collection
);
1038 ident
.setTemplates(QString());
1040 ident
.setVCardFile(mVcardFilename
);
1041 ident
.setAutocorrectionLanguage(mAutoCorrectionLanguage
->language());
1042 updateVcardButton();
1043 ident
.setAttachVcard(mAttachMyVCard
->isChecked());
1045 ident
.setDefaultDomainName(mDefaultDomainEdit
->text());
1048 uint identity
= ident
.uoid();
1049 QString iid
= TemplateParser::TemplatesConfiguration::configIdString(identity
);
1050 TemplateParser::Templates
t(iid
);
1051 qCDebug(KMAIL_LOG
) << "use custom templates for identity" << identity
<< ":" << mCustom
->isChecked();
1052 t
.setUseCustomTemplates(mCustom
->isChecked());
1054 mWidget
->saveToIdentity(identity
);
1057 ident
.setSignature(mSignatureConfigurator
->signature());
1058 ident
.setXFace(mXFaceConfigurator
->xface());
1059 ident
.setXFaceEnabled(mXFaceConfigurator
->isXFaceEnabled());
1062 void IdentityDialog::slotEditVcard()
1064 if (QFile(mVcardFilename
).exists()) {
1065 editVcard(mVcardFilename
);
1067 if (!MailCommon::Kernel::self()->kernelIsRegistered()) {
1070 KIdentityManagement::IdentityManager
*manager
= KernelIf
->identityManager();
1072 QPointer
<IdentityAddVcardDialog
> dlg
= new IdentityAddVcardDialog(manager
->shadowIdentities(), this);
1074 IdentityAddVcardDialog::DuplicateMode mode
= dlg
->duplicateMode();
1076 case IdentityAddVcardDialog::Empty
: {
1077 editVcard(mVcardFilename
);
1080 case IdentityAddVcardDialog::ExistingEntry
: {
1081 KIdentityManagement::Identity ident
= manager
->modifyIdentityForName(dlg
->duplicateVcardFromIdentity());
1082 const QString filename
= ident
.vCardFile();
1083 if (!filename
.isEmpty()) {
1084 QFile::copy(filename
, mVcardFilename
);
1086 editVcard(mVcardFilename
);
1089 case IdentityAddVcardDialog::FromExistingVCard
: {
1090 const QString filename
= dlg
->existingVCard().path();
1091 if (!filename
.isEmpty()) {
1092 mVcardFilename
= filename
;
1094 editVcard(mVcardFilename
);
1103 void IdentityDialog::editVcard(const QString
&filename
)
1105 QPointer
<IdentityEditVcardDialog
> dlg
= new IdentityEditVcardDialog(filename
, this);
1106 connect(dlg
.data(), &IdentityEditVcardDialog::vcardRemoved
, this, &IdentityDialog::slotVCardRemoved
);
1108 mVcardFilename
= dlg
->saveVcard();
1110 updateVcardButton();
1114 void IdentityDialog::slotVCardRemoved()
1116 mVcardFilename
.clear();
1119 void IdentityDialog::updateVcardButton()
1121 if (mVcardFilename
.isEmpty() || !QFile(mVcardFilename
).exists()) {
1122 mEditVCard
->setText(i18n("Create..."));
1124 mEditVCard
->setText(i18n("Edit..."));
1130 #include "identitydialog.moc"