diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,7 +138,7 @@ set(MAILCOMMON_LIB_VERSION_LIB "5.3.41") set(KDEPIM_APPS_LIB_VERSION_LIB "5.3.40") set(MESSAGELIB_LIB_VERSION_LIB "5.3.44") - set(LIBKLEO_LIB_VERSION_LIB "5.3.40") + set(LIBKLEO_LIB_VERSION_LIB "5.3.42") set(LIBGRANTLEETHEME_LIB_VERSION_LIB "5.3.40") set(PIMCOMMON_LIB_VERSION_LIB "5.3.41") set(LIBKDEPIM_LIB_VERSION_LIB "5.3.40") diff --git a/akonadiconsole/CMakeLists.txt b/akonadiconsole/CMakeLists.txt --- a/akonadiconsole/CMakeLists.txt +++ b/akonadiconsole/CMakeLists.txt @@ -52,7 +52,7 @@ find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets DBus Sql) set(LIBMAILIMPORTER_VERSION_LIB "5.3.40") set(MESSAGELIB_LIB_VERSION_LIB "5.3.44") - set(LIBKLEO_LIB_VERSION_LIB "5.3.40") + set(LIBKLEO_LIB_VERSION_LIB "5.3.42") set(LIBKDEPIM_LIB_VERSION_LIB "5.3.40") set(KCALENDARCORE_LIB_VERSION "5.3.40") set(KCONTACTS_LIB_VERSION "5.3.42") diff --git a/akregator/CMakeLists.txt b/akregator/CMakeLists.txt --- a/akregator/CMakeLists.txt +++ b/akregator/CMakeLists.txt @@ -44,7 +44,7 @@ set(KPIMTEXTEDIT_LIB_VERSION "5.3.41") set(LIBGRANTLEETHEME_LIB_VERSION_LIB "5.3.40") set(LIBKDEPIM_LIB_VERSION_LIB "5.3.40") - set(LIBKLEO_LIB_VERSION_LIB "5.3.40") + set(LIBKLEO_LIB_VERSION_LIB "5.3.42") set(MESSAGELIB_LIB_VERSION_LIB "5.3.44") set(PIMCOMMON_LIB_VERSION_LIB "5.3.41") set(SYNDICATION_LIB_VERSION "5.3.40") diff --git a/grantleeeditor/CMakeLists.txt b/grantleeeditor/CMakeLists.txt --- a/grantleeeditor/CMakeLists.txt +++ b/grantleeeditor/CMakeLists.txt @@ -43,7 +43,7 @@ set(KDEPIM_LIB_SOVERSION "5") set(KDEPIM_APPS_LIB_VERSION_LIB "5.3.40") set(LIBGRANTLEETHEME_LIB_VERSION_LIB "5.3.40") - set(LIBKLEO_LIB_VERSION_LIB "5.3.40") + set(LIBKLEO_LIB_VERSION_LIB "5.3.42") set(QT_REQUIRED_VERSION "5.6.0") find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets WebEngine WebEngineWidgets) diff --git a/kmail/CMakeLists.txt b/kmail/CMakeLists.txt --- a/kmail/CMakeLists.txt +++ b/kmail/CMakeLists.txt @@ -65,7 +65,7 @@ set(MAILCOMMON_LIB_VERSION_LIB "5.3.41") set(KDEPIM_APPS_LIB_VERSION_LIB "5.3.40") set(MESSAGELIB_LIB_VERSION_LIB "5.3.44") - set(LIBKLEO_LIB_VERSION_LIB "5.3.40") + set(LIBKLEO_LIB_VERSION_LIB "5.3.42") set(PIMCOMMON_LIB_VERSION_LIB "5.3.41") set(LIBKDEPIM_LIB_VERSION_LIB "5.3.40") set(LIBKSIEVE_LIB_VERSION_LIB "5.3.42") diff --git a/kmail/src/identity/identitydialog.h b/kmail/src/identity/identitydialog.h --- a/kmail/src/identity/identitydialog.h +++ b/kmail/src/identity/identitydialog.h @@ -42,10 +42,9 @@ class QPushButton; class QTabWidget; -namespace Kleo +namespace GpgME { -class EncryptionKeyRequester; -class SigningKeyRequester; +class Key; } namespace KIdentityManagement { @@ -85,6 +84,8 @@ { class IdentityFolderRequester; class IdentityInvalidFolder; +class KeySelectionCombo; + class IdentityDialog : public QDialog { Q_OBJECT @@ -110,6 +111,7 @@ void slotVCardRemoved(); void slotHelp(); private: + bool keyMatchesEmailAddress(const GpgME::Key &key, const QString &email); bool checkFolderExists(const QString &folder); bool validateAddresses(const QString &addresses); void updateVcardButton(); @@ -123,10 +125,10 @@ KEditListWidget *mAliasEdit; // "cryptography" tab: QWidget *mCryptographyTab; - Kleo::SigningKeyRequester *mPGPSigningKeyRequester; - Kleo::EncryptionKeyRequester *mPGPEncryptionKeyRequester; - Kleo::SigningKeyRequester *mSMIMESigningKeyRequester; - Kleo::EncryptionKeyRequester *mSMIMEEncryptionKeyRequester; + KeySelectionCombo *mPGPSigningKeyRequester; + KeySelectionCombo *mPGPEncryptionKeyRequester; + KeySelectionCombo *mSMIMESigningKeyRequester; + KeySelectionCombo *mSMIMEEncryptionKeyRequester; KComboBox *mPreferredCryptoMessageFormat; QCheckBox *mAutoSign; QCheckBox *mAutoEncrypt; diff --git a/kmail/src/identity/identitydialog.cpp b/kmail/src/identity/identitydialog.cpp --- a/kmail/src/identity/identitydialog.cpp +++ b/kmail/src/identity/identitydialog.cpp @@ -65,8 +65,14 @@ #include // libkleopatra: -#include "Libkleo/KeyRequester" -#include "Libkleo/CryptoBackendFactory" +#include +#include +#include +#include +#include + +// gpgme++ +#include #include #include @@ -117,6 +123,173 @@ namespace KMail { +class KeySelectionCombo : public Kleo::KeySelectionCombo +{ + Q_OBJECT + +public: + enum KeyType { + SigningKey, + EncryptionKey + }; + + KeySelectionCombo(KeyType keyType, GpgME::Protocol protocol, QWidget *parent); + ~KeySelectionCombo(); + + void setIdentity(const QString &name, const QString &email); + + void init() Q_DECL_OVERRIDE; + +private Q_SLOTS: + void onCustomItemSelected(const QVariant &type); + +private: + QString mEmail; + QString mName; + KeyType mKeyType; + GpgME::Protocol mProtocol; +}; + + +class KeyGenerationJob : public Kleo::Job +{ + Q_OBJECT + +public: + KeyGenerationJob(const QString &name, const QString &email, KeySelectionCombo *parent); + ~KeyGenerationJob(); + + void slotCancel() Q_DECL_OVERRIDE; + void start(); + +private Q_SLOTS: + void keyGenerated(const GpgME::KeyGenerationResult &result); + +private: + QString mName; + QString mEmail; + Kleo::Job *mJob; +}; + + + +KeyGenerationJob::KeyGenerationJob(const QString &name, const QString &email, KeySelectionCombo *parent) + : Kleo::Job(parent) + , mName(name) + , mEmail(email) + , mJob(Q_NULLPTR) +{ +} + +KeyGenerationJob::~KeyGenerationJob() +{ +} + +void KeyGenerationJob::slotCancel() +{ + if (mJob) { + mJob->slotCancel(); + } +} + +void KeyGenerationJob::start() +{ + const QString args = QStringLiteral("\n" + "%ask-passphrase\n" + "key-type: RSA\n" + "key-length: 2048\n" + "key-usage: sign\n" + "subkey-type: RSA\n" + "subkey-length: 2048\n" + "subkey-usage: encrypt\n" + "name-email: %1\n" + "name-real: %2\n" + "").arg(mEmail, mName); + + auto job = Kleo::CryptoBackendFactory::instance()->openpgp()->keyGenerationJob(); + connect(job, &Kleo::KeyGenerationJob::result, + this, &KeyGenerationJob::keyGenerated); + job->start(args); + mJob = job; +} + +void KeyGenerationJob::keyGenerated(const GpgME::KeyGenerationResult &result) +{ + mJob = Q_NULLPTR; + if (result.error()) { + KMessageBox::error(qobject_cast(parent()), + i18n("Error while generating new key pair: %1", QString::fromUtf8(result.error().asString())), + i18n("Key Generation Error")); + Q_EMIT done(); + return; + } + + KeySelectionCombo *combo = qobject_cast(parent()); + combo->setDefaultKey(QLatin1String(result.fingerprint())); + connect(combo, &KeySelectionCombo::keyListingFinished, + this, &KeyGenerationJob::done); + combo->refreshKeys(); +} + +KeySelectionCombo::KeySelectionCombo(KeyType keyType, GpgME::Protocol protocol, QWidget *parent) + : Kleo::KeySelectionCombo(parent) + , mKeyType(keyType) + , mProtocol(protocol) +{ +} + +KeySelectionCombo::~KeySelectionCombo() +{ +} + +void KeySelectionCombo::setIdentity(const QString &name, const QString &email) +{ + mName = name; + mEmail = email; + setIdFilter(email); +} + +void KeySelectionCombo::init() +{ + Kleo::KeySelectionCombo::init(); + + boost::shared_ptr keyFilter(new Kleo::DefaultKeyFilter); + keyFilter->setIsOpenPGP(mProtocol == GpgME::OpenPGP ? Kleo::DefaultKeyFilter::Set : Kleo::DefaultKeyFilter::NotSet); + if (mKeyType == SigningKey) { + keyFilter->setCanSign(Kleo::DefaultKeyFilter::Set); + } else { + keyFilter->setCanEncrypt(Kleo::DefaultKeyFilter::Set); + } + keyFilter->setHasSecret(Kleo::DefaultKeyFilter::Set); + setKeyFilter(keyFilter); + + prependCustomItem(QIcon(), i18n("No key"), QStringLiteral("no-key")); + if (mProtocol == GpgME::OpenPGP) { + appendCustomItem(QIcon::fromTheme(QStringLiteral("password-generate")), + i18n("Generate a new key pair"), QStringLiteral("generate-new-key")); + } + + connect(this, &KeySelectionCombo::customItemSelected, + this, &KeySelectionCombo::onCustomItemSelected); +} + +void KeySelectionCombo::onCustomItemSelected(const QVariant &type) +{ + const QString typeStr = type.toString(); + if (type == QLatin1String("no-key")) { + return; + } else if (type == QLatin1String("generate-new-key")) { + auto job = new KeyGenerationJob(mName, mEmail, this); + new Kleo::ProgressDialog(job, i18n("Generating new key pair..."), parentWidget()); + setEnabled(false); + connect(job, &KeyGenerationJob::done, + this, [this]() { setEnabled(true); }); + job->start(); + } +} + + + IdentityDialog::IdentityDialog(QWidget *parent) : QDialog(parent) { @@ -247,20 +420,13 @@ // "OpenPGP Signature Key" requester and label: ++row; - mPGPSigningKeyRequester = new Kleo::SigningKeyRequester(false, Kleo::SigningKeyRequester::OpenPGP, tab); - mPGPSigningKeyRequester->dialogButton()->setText(i18n("Chang&e...")); - mPGPSigningKeyRequester->setDialogCaption(i18n("Your OpenPGP Signature Key")); - msg = i18n("Select the OpenPGP key which should be used to " - "digitally sign your messages."); - mPGPSigningKeyRequester->setDialogMessage(msg); - + mPGPSigningKeyRequester = new KeySelectionCombo(KeySelectionCombo::SigningKey, GpgME::OpenPGP, tab); msg = i18n("

The OpenPGP key you choose here will be used " "to digitally sign messages. You can also use GnuPG keys.

" "

You can leave this blank, but KMail will not be able " "to digitally sign emails using OpenPGP; " "normal mail functions will not be affected.

" "

You can find out more about keys at http://www.gnupg.org

"); - label = new QLabel(i18n("OpenPGP signing key:"), tab); label->setBuddy(mPGPSigningKeyRequester); mPGPSigningKeyRequester->setWhatsThis(msg); @@ -271,14 +437,7 @@ // "OpenPGP Encryption Key" requester and label: ++row; - mPGPEncryptionKeyRequester = new Kleo::EncryptionKeyRequester(false, Kleo::EncryptionKeyRequester::OpenPGP, tab); - mPGPEncryptionKeyRequester->dialogButton()->setText(i18n("Chang&e...")); - mPGPEncryptionKeyRequester->setDialogCaption(i18n("Your OpenPGP Encryption Key")); - msg = i18n("Select the OpenPGP key which should be used when encrypting " - "to yourself and for the \"Attach My Public Key\" " - "feature in the composer."); - mPGPEncryptionKeyRequester->setDialogMessage(msg); - + mPGPEncryptionKeyRequester = new KeySelectionCombo(KeySelectionCombo::EncryptionKey, GpgME::OpenPGP, tab); msg = i18n("

The OpenPGP key you choose here will be used " "to encrypt messages to yourself and for the \"Attach My Public Key\" " "feature in the composer. You can also use GnuPG keys.

" @@ -296,13 +455,7 @@ // "S/MIME Signature Key" requester and label: ++row; - mSMIMESigningKeyRequester = new Kleo::SigningKeyRequester(false, Kleo::SigningKeyRequester::SMIME, tab); - mSMIMESigningKeyRequester->dialogButton()->setText(i18n("Chang&e...")); - mSMIMESigningKeyRequester->setDialogCaption(i18n("Your S/MIME Signature Certificate")); - msg = i18n("Select the S/MIME certificate which should be used to " - "digitally sign your messages."); - mSMIMESigningKeyRequester->setDialogMessage(msg); - + mSMIMESigningKeyRequester = new KeySelectionCombo(KeySelectionCombo::SigningKey, GpgME::CMS, tab); msg = i18n("

The S/MIME (X.509) certificate you choose here will be used " "to digitally sign messages.

" "

You can leave this blank, but KMail will not be able " @@ -323,14 +476,7 @@ // "S/MIME Encryption Key" requester and label: ++row; - mSMIMEEncryptionKeyRequester = new Kleo::EncryptionKeyRequester(false, Kleo::EncryptionKeyRequester::SMIME, tab); - mSMIMEEncryptionKeyRequester->dialogButton()->setText(i18n("Chang&e...")); - mSMIMEEncryptionKeyRequester->setDialogCaption(i18n("Your S/MIME Encryption Certificate")); - msg = i18n("Select the S/MIME certificate which should be used when encrypting " - "to yourself and for the \"Attach My Certificate\" " - "feature in the composer."); - mSMIMEEncryptionKeyRequester->setDialogMessage(msg); - + mSMIMEEncryptionKeyRequester = new KeySelectionCombo(KeySelectionCombo::EncryptionKey, GpgME::CMS, tab); msg = i18n("

The S/MIME certificate you choose here will be used " "to encrypt messages to yourself and for the \"Attach My Certificate\" " "feature in the composer.

" @@ -610,62 +756,21 @@ if (w == mCryptographyTab) { // set the configured email address as initial query of the key // requesters: + const QString name = mNameEdit->text().trimmed(); const QString email = mEmailEdit->text().trimmed(); - mPGPEncryptionKeyRequester->setInitialQuery(email); - mPGPSigningKeyRequester->setInitialQuery(email); - mSMIMEEncryptionKeyRequester->setInitialQuery(email); - mSMIMESigningKeyRequester->setInitialQuery(email); + + mPGPEncryptionKeyRequester->setIdentity(name, email); + mPGPSigningKeyRequester->setIdentity(name, email); + mSMIMEEncryptionKeyRequester->setIdentity(name, email); + mSMIMESigningKeyRequester->setIdentity(name, email); } } void IdentityDialog::slotCopyGlobal() { mWidget->loadFromGlobal(); } -namespace -{ -struct DoesntMatchEMailAddress { - explicit DoesntMatchEMailAddress(const QString &s) - : email(s.trimmed().toLower()) {} - bool operator()(const GpgME::Key &key) const; -private: - bool checkForEmail(const char *email) const; - static QString extractEmail(const char *email); - const QString email; -}; - -bool DoesntMatchEMailAddress::operator()(const GpgME::Key &key) const -{ - const std::vector uids = key.userIDs(); - std::vector::const_iterator end = uids.end(); - for (std::vector::const_iterator it = uids.begin(); it != end; ++it) - if (checkForEmail(it->email() ? it->email() : it->id())) { - return false; - } - return true; // note the negation! -} - -bool DoesntMatchEMailAddress::checkForEmail(const char *e) const -{ - const QString em = extractEmail(e); - return !em.isEmpty() && email.toLower() == em.toLower(); -} - -QString DoesntMatchEMailAddress::extractEmail(const char *e) -{ - if (!e || !*e) { - return QString(); - } - const QString em = QString::fromUtf8(e); - if (e[0] == '<') { - return em.mid(1, em.length() - 2); - } else { - return em; - } -} -} - void IdentityDialog::slotRefreshDefaultDomainName() { mDefaultDomainEdit->setText(QHostInfo::localHostName()); @@ -700,6 +805,29 @@ job->start(); } +bool IdentityDialog::keyMatchesEmailAddress(const GpgME::Key &key, const QString &email_) +{ + if (key.isNull()) { + return true; + } + const QString email = email_.trimmed().toLower(); + const auto uids = key.userIDs(); + for (const auto &uid : uids) { + QString em = QString::fromUtf8(uid.email() ? uid.email() : uid.id()); + if (em.isEmpty()) { + continue; + } + if (em[0] == QLatin1Char('<')) { + em = em.mid(1, em.length() - 2); + } + if (em.toLower() == email) { + return true; + } + } + + return false; +} + void IdentityDialog::slotDelayedButtonClicked(KJob *job) { const AddressValidationJob *validationJob = qobject_cast(job); @@ -711,41 +839,33 @@ const QString email = validationJob->property("email").toString(); - const std::vector &pgpSigningKeys = - mPGPSigningKeyRequester->keys(); - const std::vector &pgpEncryptionKeys = - mPGPEncryptionKeyRequester->keys(); - const std::vector &smimeSigningKeys = - mSMIMESigningKeyRequester->keys(); - const std::vector &smimeEncryptionKeys = - mSMIMEEncryptionKeyRequester->keys(); + const GpgME::Key &pgpSigningKey = mPGPSigningKeyRequester->currentKey(); + const GpgME::Key &pgpEncryptionKey = mPGPEncryptionKeyRequester->currentKey(); + const GpgME::Key &smimeSigningKey = mSMIMESigningKeyRequester->currentKey(); + const GpgME::Key &smimeEncryptionKey = mSMIMEEncryptionKeyRequester->currentKey(); QString msg; bool err = false; - if (std::find_if(pgpSigningKeys.begin(), pgpSigningKeys.end(), - DoesntMatchEMailAddress(email)) != pgpSigningKeys.end()) { + if (!keyMatchesEmailAddress(pgpSigningKey, email)) { msg = i18n("One of the configured OpenPGP signing keys does not contain " "any user ID with the configured email address for this " "identity (%1).\n" "This might result in warning messages on the receiving side " "when trying to verify signatures made with this configuration.", email); err = true; - } else if (std::find_if(pgpEncryptionKeys.begin(), pgpEncryptionKeys.end(), - DoesntMatchEMailAddress(email)) != pgpEncryptionKeys.end()) { + } else if (!keyMatchesEmailAddress(pgpEncryptionKey, email)) { msg = i18n("One of the configured OpenPGP encryption keys does not contain " "any user ID with the configured email address for this " "identity (%1).", email); err = true; - } else if (std::find_if(smimeSigningKeys.begin(), smimeSigningKeys.end(), - DoesntMatchEMailAddress(email)) != smimeSigningKeys.end()) { + } else if (!keyMatchesEmailAddress(smimeSigningKey, email)) { msg = i18n("One of the configured S/MIME signing certificates does not contain " "the configured email address for this " "identity (%1).\n" "This might result in warning messages on the receiving side " "when trying to verify signatures made with this configuration.", email); err = true; - } else if (std::find_if(smimeEncryptionKeys.begin(), smimeEncryptionKeys.end(), - DoesntMatchEMailAddress(email)) != smimeEncryptionKeys.end()) { + } else if (!keyMatchesEmailAddress(smimeEncryptionKey, email)) { msg = i18n("One of the configured S/MIME encryption certificates does not contain " "the configured email address for this " "identity (%1).", email); @@ -793,10 +913,10 @@ mAliasEdit->insertStringList(ident.emailAliases()); // "Cryptography" tab: - mPGPSigningKeyRequester->setFingerprint(QLatin1String(ident.pgpSigningKey())); - mPGPEncryptionKeyRequester->setFingerprint(QLatin1String(ident.pgpEncryptionKey())); - mSMIMESigningKeyRequester->setFingerprint(QLatin1String(ident.smimeSigningKey())); - mSMIMEEncryptionKeyRequester->setFingerprint(QLatin1String(ident.smimeEncryptionKey())); + mPGPSigningKeyRequester->setDefaultKey(QLatin1String(ident.pgpSigningKey())); + mPGPEncryptionKeyRequester->setDefaultKey(QLatin1String(ident.pgpEncryptionKey())); + mSMIMESigningKeyRequester->setDefaultKey(QLatin1String(ident.smimeSigningKey())); + mSMIMEEncryptionKeyRequester->setDefaultKey(QLatin1String(ident.smimeEncryptionKey())); mPreferredCryptoMessageFormat->setCurrentIndex(format2cb( Kleo::stringToCryptoMessageFormat(ident.preferredCryptoMessageFormat()))); @@ -893,10 +1013,10 @@ ident.setPrimaryEmailAddress(email); ident.setEmailAliases(mAliasEdit->items()); // "Cryptography" tab: - ident.setPGPSigningKey(mPGPSigningKeyRequester->fingerprint().toLatin1()); - ident.setPGPEncryptionKey(mPGPEncryptionKeyRequester->fingerprint().toLatin1()); - ident.setSMIMESigningKey(mSMIMESigningKeyRequester->fingerprint().toLatin1()); - ident.setSMIMEEncryptionKey(mSMIMEEncryptionKeyRequester->fingerprint().toLatin1()); + ident.setPGPSigningKey(mPGPSigningKeyRequester->currentKey().primaryFingerprint()); + ident.setPGPEncryptionKey(mPGPEncryptionKeyRequester->currentKey().primaryFingerprint()); + ident.setSMIMESigningKey(mSMIMESigningKeyRequester->currentKey().primaryFingerprint()); + ident.setSMIMEEncryptionKey(mSMIMEEncryptionKeyRequester->currentKey().primaryFingerprint()); ident.setPreferredCryptoMessageFormat( QLatin1String(Kleo::cryptoMessageFormatToString(cb2format(mPreferredCryptoMessageFormat->currentIndex())))); ident.setPgpAutoSign(mAutoSign->isChecked()); @@ -1028,3 +1148,4 @@ } +#include "identitydialog.moc"