diff --git a/src/core/messagepart.cpp b/src/core/messagepart.cpp
index d006dd4..22b2803 100644
--- a/src/core/messagepart.cpp
+++ b/src/core/messagepart.cpp
@@ -1,1097 +1,1096 @@
 // SPDX-FileCopyrightText: 2015 Sandro Knauß <knauss@kolabsys.com>
 // SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #include "messagepart.h"
 #include "cryptohelper.h"
 #include "mimetreeparser_core_debug.h"
 #include "objecttreeparser.h"
 
 #include "utils.h"
 
 #include <KLocalizedString>
 #include <KMime/Content>
 #include <Libkleo/Compliance>
 #include <Libkleo/KeyCache>
 
 #include <QGpgME/DN>
 #include <QGpgME/DecryptVerifyJob>
 #include <QGpgME/Protocol>
 #include <QGpgME/VerifyDetachedJob>
 #include <QGpgME/VerifyOpaqueJob>
 
 #include <QStringDecoder>
 
 #include <gpgme++/key.h>
 #include <gpgme++/keylistresult.h>
 #include <gpgme.h>
 
 using namespace MimeTreeParser;
 
 //------MessagePart-----------------------
 MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node)
     : mText(text)
     , mOtp(otp)
     , mParentPart(nullptr)
     , mNode(node) // only null for messagepartlist
     , mError(NoError)
     , mRoot(false)
 {
 }
 
 MessagePart::~MessagePart()
 {
     for (auto n : std::as_const(mNodesToDelete)) {
         delete n;
     }
 }
 
 MessagePart::Disposition MessagePart::disposition() const
 {
     if (!mNode) {
         return Invalid;
     }
     const auto cd = mNode->contentDisposition(false);
     if (!cd) {
         return Invalid;
     }
     switch (cd->disposition()) {
     case KMime::Headers::CDinline:
         return Inline;
     case KMime::Headers::CDattachment:
         return Attachment;
     default:
         return Invalid;
     }
 }
 
 QString MessagePart::filename() const
 {
     if (!mNode) {
         return {};
     }
 
     if (const auto cd = mNode->contentDisposition(false)) {
         const auto name = cd->filename();
         // Allow for a fallback for mails that have a ContentDisposition header, but don't set the filename anyways.
         // Not the recommended way, but exists.
         if (!name.isEmpty()) {
             return name;
         }
     }
     if (const auto ct = mNode->contentType(false)) {
         return ct->name();
     }
     return {};
 }
 
 static KMime::Headers::ContentType *contentType(KMime::Content *node)
 {
     if (node) {
         return node->contentType(false);
     }
     return nullptr;
 }
 
 QByteArray MessagePart::charset() const
 {
     if (!mNode) {
         return QByteArrayLiteral("us-ascii");
     }
     if (auto ct = contentType(mNode)) {
         return ct->charset();
     }
     // Per rfc2045 us-ascii is the default
     return QByteArrayLiteral("us-ascii");
 }
 
 QByteArray MessagePart::mimeType() const
 {
     if (auto ct = contentType(mNode)) {
         return ct->mimeType();
     }
     return {};
 }
 
 bool MessagePart::isText() const
 {
     if (auto ct = contentType(mNode)) {
         return ct->isText();
     }
     return false;
 }
 
 MessagePart::Error MessagePart::error() const
 {
     return mError;
 }
 
 QString MessagePart::errorString() const
 {
     return mMetaData.errorText;
 }
 
 PartMetaData *MessagePart::partMetaData()
 {
     return &mMetaData;
 }
 
 bool MessagePart::isAttachment() const
 {
     if (mNode) {
         return KMime::isAttachment(mNode);
     }
     return false;
 }
 
 KMime::Content *MessagePart::node() const
 {
     return mNode;
 }
 
 void MessagePart::setIsRoot(bool root)
 {
     mRoot = root;
 }
 
 bool MessagePart::isRoot() const
 {
     return mRoot;
 }
 
 QString MessagePart::text() const
 {
     return mText;
 }
 
 void MessagePart::setText(const QString &text)
 {
     mText = text;
 }
 
 bool MessagePart::isHtml() const
 {
     return false;
 }
 
 MessagePart *MessagePart::parentPart() const
 {
     return mParentPart;
 }
 
 void MessagePart::setParentPart(MessagePart *parentPart)
 {
     mParentPart = parentPart;
 }
 
 QString MessagePart::htmlContent() const
 {
     return text();
 }
 
 QString MessagePart::plaintextContent() const
 {
     return text();
 }
 
 void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart)
 {
     auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart);
     mRoot = subMessagePart->isRoot();
     for (const auto &part : std::as_const(subMessagePart->subParts())) {
         appendSubPart(part);
     }
 }
 
 void MessagePart::parseInternal(const QByteArray &data)
 {
     auto tempNode = new KMime::Content();
 
     const auto lfData = KMime::CRLFtoLF(data);
     // We have to deal with both bodies and full parts. In inline encrypted/signed parts we can have nested parts,
     // or just plain-text, and both ends up here. setContent defaults to setting only the header, so we have to avoid this.
     if (lfData.contains("\n\n")) {
         tempNode->setContent(lfData);
     } else {
         tempNode->setBody(lfData);
     }
     tempNode->parse();
     tempNode->contentType()->setCharset(charset());
     bindLifetime(tempNode);
 
     if (!tempNode->head().isEmpty()) {
         tempNode->contentDescription()->from7BitString(QByteArrayLiteral("temporary node"));
     }
 
     parseInternal(tempNode);
 }
 
 QString MessagePart::renderInternalText() const
 {
     QString text;
     for (const auto &mp : subParts()) {
         text += mp->text();
     }
     return text;
 }
 
 void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart)
 {
     messagePart->setParentPart(this);
     mBlocks.append(messagePart);
 }
 
 const QList<MessagePart::Ptr> &MessagePart::subParts() const
 {
     return mBlocks;
 }
 
 bool MessagePart::hasSubParts() const
 {
     return !mBlocks.isEmpty();
 }
 
 QList<SignedMessagePart *> MessagePart::signatures() const
 {
     QList<SignedMessagePart *> list;
     if (auto sig = dynamic_cast<SignedMessagePart *>(const_cast<MessagePart *>(this))) {
         list << sig;
     }
     auto parent = parentPart();
     while (parent) {
         if (auto sig = dynamic_cast<SignedMessagePart *>(parent)) {
             list << sig;
         }
         parent = parent->parentPart();
     }
     return list;
 }
 
 QList<EncryptedMessagePart *> MessagePart::encryptions() const
 {
     QList<EncryptedMessagePart *> list;
     if (auto sig = dynamic_cast<EncryptedMessagePart *>(const_cast<MessagePart *>(this))) {
         list << sig;
     }
     auto parent = parentPart();
     while (parent) {
         if (auto sig = dynamic_cast<EncryptedMessagePart *>(parent)) {
             list << sig;
         }
         parent = parent->parentPart();
     }
     return list;
 }
 
 KMMsgEncryptionState MessagePart::encryptionState() const
 {
     if (!encryptions().isEmpty()) {
         return KMMsgFullyEncrypted;
     }
     return KMMsgNotEncrypted;
 }
 
 KMMsgSignatureState MessagePart::signatureState() const
 {
     if (!signatures().isEmpty()) {
         return KMMsgFullySigned;
     }
     return KMMsgNotSigned;
 }
 
 void MessagePart::bindLifetime(KMime::Content *node)
 {
     mNodesToDelete << node;
 }
 
 KMime::Headers::Base *MimeTreeParser::MessagePart::header(const char *header) const
 {
     if (node() && node()->hasHeader(header)) {
         return node()->headerByType(header);
     }
     if (auto parent = parentPart()) {
         return parent->header(header);
     }
 
     return nullptr;
 }
 
 //-----MessagePartList----------------------
 MessagePartList::MessagePartList(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePart(otp, QString(), node)
 {
 }
 
 QString MessagePartList::text() const
 {
     return renderInternalText();
 }
 
 QString MessagePartList::plaintextContent() const
 {
     return QString();
 }
 
 QString MessagePartList::htmlContent() const
 {
     return QString();
 }
 
 //-----TextMessageBlock----------------------
 
 TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePartList(otp, node)
     , mSignatureState(KMMsgSignatureStateUnknown)
     , mEncryptionState(KMMsgEncryptionStateUnknown)
 {
     if (!mNode) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
         return;
     }
 
     parseContent();
 }
 
 void TextMessagePart::parseContent()
 {
     mSignatureState = KMMsgNotSigned;
     mEncryptionState = KMMsgNotEncrypted;
     const auto blocks = prepareMessageForDecryption(mNode->decodedContent());
     // We also get blocks for unencrypted messages
     if (!blocks.isEmpty()) {
         auto aCodec = QStringDecoder(mOtp->codecNameFor(mNode).constData());
         const auto cryptProto = QGpgME::openpgp();
 
         /* The (overall) signature/encrypted status is broken
          * if one unencrypted part is at the beginning or in the middle
          * because mailmain adds an unencrypted part at the end this should not break the overall status
          *
          * That's why we first set the tmp status and if one crypted/signed block comes afterwards, than
          * the status is set to unencryped
          */
         bool fullySignedOrEncrypted = true;
         bool fullySignedOrEncryptedTmp = true;
 
         for (const auto &block : blocks) {
             if (!fullySignedOrEncryptedTmp) {
                 fullySignedOrEncrypted = false;
             }
 
             if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) {
                 fullySignedOrEncryptedTmp = false;
                 appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec.decode(KMime::CRLFtoLF(block.text())))));
             } else if (block.type() == PgpMessageBlock) {
                 auto content = new KMime::Content;
                 content->setBody(block.text());
                 content->parse();
                 content->contentType()->setCharset(charset());
                 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, content, content, false));
                 mp->bindLifetime(content);
                 mp->setIsEncrypted(true);
                 appendSubPart(mp);
             } else if (block.type() == ClearsignedBlock) {
                 auto content = new KMime::Content;
                 content->setBody(block.text());
                 content->parse();
                 content->contentType()->setCharset(charset());
                 SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, cryptProto, nullptr, content, false));
                 mp->bindLifetime(content);
                 mp->setIsSigned(true);
                 appendSubPart(mp);
             } else {
                 continue;
             }
 
             const auto mp = subParts().last().staticCast<MessagePart>();
             const PartMetaData *messagePart(mp->partMetaData());
 
             if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) {
                 mp->setText(aCodec.decode(KMime::CRLFtoLF(block.text())));
             }
 
             if (messagePart->isEncrypted) {
                 mEncryptionState = KMMsgPartiallyEncrypted;
             }
 
             if (messagePart->isSigned) {
                 mSignatureState = KMMsgPartiallySigned;
             }
         }
 
         // Do we have an fully Signed/Encrypted Message?
         if (fullySignedOrEncrypted) {
             if (mSignatureState == KMMsgPartiallySigned) {
                 mSignatureState = KMMsgFullySigned;
             }
             if (mEncryptionState == KMMsgPartiallyEncrypted) {
                 mEncryptionState = KMMsgFullyEncrypted;
             }
         }
     }
 }
 
 KMMsgEncryptionState TextMessagePart::encryptionState() const
 {
     if (mEncryptionState == KMMsgNotEncrypted) {
         return MessagePart::encryptionState();
     }
     return mEncryptionState;
 }
 
 KMMsgSignatureState TextMessagePart::signatureState() const
 {
     if (mSignatureState == KMMsgNotSigned) {
         return MessagePart::signatureState();
     }
     return mSignatureState;
 }
 
 //-----AttachmentMessageBlock----------------------
 
 AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node)
     : TextMessagePart(otp, node)
 {
 }
 
 //-----HtmlMessageBlock----------------------
 
 HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePart(otp, QString(), node)
 {
     if (!mNode) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
         return;
     }
 
     setText(QStringDecoder(mOtp->codecNameFor(mNode).constData()).decode(KMime::CRLFtoLF(mNode->decodedContent())));
 }
 
 //-----MimeMessageBlock----------------------
 
 MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart)
     : MessagePart(otp, QString(), node)
 {
     if (!mNode) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
         return;
     }
 
     parseInternal(mNode, onlyOneMimePart);
 }
 
 MimeMessagePart::~MimeMessagePart()
 {
 }
 
 QString MimeMessagePart::text() const
 {
     return renderInternalText();
 }
 
 QString MimeMessagePart::plaintextContent() const
 {
     return QString();
 }
 
 QString MimeMessagePart::htmlContent() const
 {
     return QString();
 }
 
 //-----AlternativeMessagePart----------------------
 
 AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePart(otp, QString(), node)
 {
     if (auto dataIcal = findTypeInDirectChildren(mNode, "text/calendar")) {
         mChildParts[MultipartIcal] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataIcal, true));
     }
 
     if (auto dataText = findTypeInDirectChildren(mNode, "text/plain")) {
         mChildParts[MultipartPlain] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataText, true));
     }
 
     if (auto dataHtml = findTypeInDirectChildren(mNode, "text/html")) {
         mChildParts[MultipartHtml] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataHtml, true));
     } else {
         // If we didn't find the HTML part as the first child of the multipart/alternative, it might
         // be that this is a HTML message with images, and text/plain and multipart/related are the
         // immediate children of this multipart/alternative node.
         // In this case, the HTML node is a child of multipart/related.
         // In the case of multipart/related we don't expect multiple html parts, it is usually used to group attachments
         // with html content.
         //
         // In any case, this is not a complete implementation of MIME, but an approximation for the kind of mails we actually see in the wild.
         auto data = [&] {
             if (auto d = findTypeInDirectChildren(mNode, "multipart/related")) {
                 return d;
             }
             return findTypeInDirectChildren(mNode, "multipart/mixed");
         }();
         if (data) {
             QString htmlContent;
             const auto parts = data->contents();
             for (auto p : parts) {
                 if ((!p->contentType()->isEmpty()) && (p->contentType()->mimeType() == "text/html")) {
                     htmlContent += MimeMessagePart(mOtp, p, true).text();
                 } else if (KMime::isAttachment(p)) {
                     appendSubPart(MimeMessagePart::Ptr(new MimeMessagePart(otp, p, true)));
                 }
             }
             mChildParts[MultipartHtml] = MessagePart::Ptr(new MessagePart(mOtp, htmlContent, nullptr));
         }
     }
 }
 
 AlternativeMessagePart::~AlternativeMessagePart()
 {
 }
 
 QList<AlternativeMessagePart::HtmlMode> AlternativeMessagePart::availableModes()
 {
     return mChildParts.keys();
 }
 
 QString AlternativeMessagePart::text() const
 {
     if (mChildParts.contains(MultipartPlain)) {
         return mChildParts[MultipartPlain]->text();
     }
     return QString();
 }
 
 bool AlternativeMessagePart::isHtml() const
 {
     return mChildParts.contains(MultipartHtml);
 }
 
 QString AlternativeMessagePart::plaintextContent() const
 {
     return text();
 }
 
 QString AlternativeMessagePart::htmlContent() const
 {
     if (mChildParts.contains(MultipartHtml)) {
         return mChildParts[MultipartHtml]->text();
     } else {
         return plaintextContent();
     }
 }
 
 QString AlternativeMessagePart::icalContent() const
 {
     if (mChildParts.contains(MultipartIcal)) {
         return mChildParts[MultipartIcal]->text();
     }
     return {};
 }
 
 //-----CertMessageBlock----------------------
 
 CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, QGpgME::Protocol *cryptoProto)
     : MessagePart(otp, QString(), node)
     , mCryptoProto(cryptoProto)
 {
     if (!mNode) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
         return;
     }
 }
 
 CertMessagePart::~CertMessagePart()
 {
 }
 
 QString CertMessagePart::text() const
 {
     return QString();
 }
 
 //-----SignedMessageBlock---------------------
 SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp,
                                      const QGpgME::Protocol *cryptoProto,
                                      KMime::Content *node,
                                      KMime::Content *signedData,
                                      bool parseAfterDecryption)
     : MessagePart(otp, {}, node)
     , mParseAfterDecryption(parseAfterDecryption)
     , mCryptoProto(cryptoProto)
     , mSignedData(signedData)
 {
     mMetaData.isSigned = true;
     mMetaData.isGoodSignature = false;
     mMetaData.status = i18ndc("mimetreeparser", "@info:status", "Wrong Crypto Plug-In.");
 }
 
 SignedMessagePart::~SignedMessagePart()
 {
 }
 
 void SignedMessagePart::setIsSigned(bool isSigned)
 {
     mMetaData.isSigned = isSigned;
 }
 
 bool SignedMessagePart::isSigned() const
 {
     return mMetaData.isSigned;
 }
 
 static QString prettifyDN(const char *uid)
 {
     // We used to use QGpgME::DN::prettyDN here. But I'm not sure what we actually need it for.
     return QString::fromUtf8(uid);
 }
 
 const QGpgME::Protocol *SignedMessagePart::cryptoProto() const
 {
     return mCryptoProto;
 }
 
 void SignedMessagePart::startVerification()
 {
     if (!mSignedData) {
         return;
     }
 
     mMetaData.isSigned = false;
     mMetaData.status = i18ndc("mimetreeparser", "@info:status", "Wrong Crypto Plug-In.");
     mMetaData.isEncrypted = false;
     mMetaData.isDecryptable = false;
 
     auto codec = QStringDecoder(mOtp->codecNameFor(mSignedData).constData());
 
     // If we have a mNode, this is a detached signature
     if (mNode) {
         const auto signature = mNode->decodedContent();
 
         // This is necessary in case the original data contained CRLF's. Otherwise the signature will not match the data (since KMIME normalizes to LF)
         const QByteArray signedData = KMime::LFtoCRLF(mSignedData->encodedContent());
 
         const auto job = mCryptoProto->verifyDetachedJob();
         setVerificationResult(job->exec(signature, signedData), signedData);
         job->deleteLater();
         setText(codec.decode(KMime::CRLFtoLF(signedData)));
     } else {
         QByteArray outdata;
         const auto job = mCryptoProto->verifyOpaqueJob();
         setVerificationResult(job->exec(mSignedData->decodedContent(), outdata), outdata);
         job->deleteLater();
         setText(codec.decode(KMime::CRLFtoLF(outdata)));
     }
 
     if (!mMetaData.isSigned) {
         mMetaData.creationTime = QDateTime();
     }
 }
 
 static int signatureToStatus(const GpgME::Signature &sig)
 {
     switch (sig.status().code()) {
     case GPG_ERR_NO_ERROR:
         return GPGME_SIG_STAT_GOOD;
     case GPG_ERR_BAD_SIGNATURE:
         return GPGME_SIG_STAT_BAD;
     case GPG_ERR_NO_PUBKEY:
         return GPGME_SIG_STAT_NOKEY;
     case GPG_ERR_NO_DATA:
         return GPGME_SIG_STAT_NOSIG;
     case GPG_ERR_SIG_EXPIRED:
         return GPGME_SIG_STAT_GOOD_EXP;
     case GPG_ERR_KEY_EXPIRED:
         return GPGME_SIG_STAT_GOOD_EXPKEY;
     default:
         return GPGME_SIG_STAT_ERROR;
     }
 }
 
 void SignedMessagePart::sigStatusToMetaData()
 {
-    GpgME::Key key;
-    if (partMetaData()->isSigned) {
-        GpgME::Signature signature = mSignatures.front();
-        mMetaData.status_code = signatureToStatus(signature);
-        mMetaData.isGoodSignature = partMetaData()->status_code == GPGME_SIG_STAT_GOOD;
-        // save extended signature status flags
-        mMetaData.sigSummary = signature.summary();
-
-        if (partMetaData()->isGoodSignature && !key.keyID()) {
-            // Search for the key by its fingerprint so that we can check for
-            // trust etc.
-            key = Kleo::KeyCache::instance()->findByFingerprint(signature.fingerprint());
-            if (key.isNull() && signature.fingerprint()) {
-                // try to find a subkey that was used for signing;
-                // assumes that the key ID is the last 16 characters of the fingerprint
-                const auto fpr = std::string_view{signature.fingerprint()};
-                const auto keyID = std::string{fpr, fpr.size() - 16, 16};
-                const auto subkeys = Kleo::KeyCache::instance()->findSubkeysByKeyID({keyID});
-                if (subkeys.size() > 0) {
-                    key = subkeys[0].parent();
-                }
-            }
-            if (key.isNull()) {
-                qCDebug(MIMETREEPARSER_CORE_LOG) << "Found no key or subkey for fingerprint" << signature.fingerprint();
-            }
-        }
+    if (!partMetaData()->isSigned) {
+        return;
+    }
 
-        if (key.keyID()) {
-            partMetaData()->keyId = key.keyID();
+    GpgME::Signature signature = mSignatures.front();
+    mMetaData.status_code = signatureToStatus(signature);
+    mMetaData.isGoodSignature = partMetaData()->status_code == GPGME_SIG_STAT_GOOD;
+    // save extended signature status flags
+    mMetaData.sigSummary = signature.summary();
+
+    // Search for the key by its fingerprint so that we can check for
+    // trust etc.
+    GpgME::Key key = Kleo::KeyCache::instance()->findByFingerprint(signature.fingerprint());
+    if (key.isNull() && signature.fingerprint()) {
+        // try to find a subkey that was used for signing;
+        // assumes that the key ID is the last 16 characters of the fingerprint
+        const auto fpr = std::string_view{signature.fingerprint()};
+        const auto keyID = std::string{fpr, fpr.size() - 16, 16};
+        const auto subkeys = Kleo::KeyCache::instance()->findSubkeysByKeyID({keyID});
+        if (subkeys.size() > 0) {
+            key = subkeys[0].parent();
         }
-        if (partMetaData()->keyId.isEmpty()) {
-            partMetaData()->keyId = signature.fingerprint();
-        }
-        partMetaData()->keyTrust = signature.validity();
-        if (key.numUserIDs() > 0 && key.userID(0).id()) {
-            partMetaData()->signer = prettifyDN(key.userID(0).id());
-        }
-        for (const auto &uid : key.userIDs()) {
-            // The following if /should/ always result in TRUE but we
-            // won't trust implicitly the plugin that gave us these data.
-            if (uid.email()) {
-                QString email = QString::fromUtf8(uid.email());
-                if (!email.isEmpty()) {
-                    partMetaData()->signerMailAddresses.append(email);
-                }
+    }
+    if (key.isNull()) {
+        qCDebug(MIMETREEPARSER_CORE_LOG) << "Found no key or subkey for fingerprint" << signature.fingerprint();
+    }
+
+    if (key.keyID()) {
+        partMetaData()->keyId = key.keyID();
+    }
+    if (partMetaData()->keyId.isEmpty()) {
+        partMetaData()->keyId = signature.fingerprint();
+    }
+    partMetaData()->keyTrust = signature.validity();
+    if (key.numUserIDs() > 0 && key.userID(0).id()) {
+        partMetaData()->signer = prettifyDN(key.userID(0).id());
+    }
+    for (const auto &uid : key.userIDs()) {
+        // The following if /should/ always result in TRUE but we
+        // won't trust implicitly the plugin that gave us these data.
+        if (uid.email()) {
+            QString email = QString::fromUtf8(uid.email());
+            if (!email.isEmpty()) {
+                partMetaData()->signerMailAddresses.append(email);
             }
         }
+    }
 
-        if (signature.creationTime()) {
-            partMetaData()->creationTime.setSecsSinceEpoch(signature.creationTime());
-        } else {
-            partMetaData()->creationTime = QDateTime();
+    if (signature.creationTime()) {
+        partMetaData()->creationTime.setSecsSinceEpoch(signature.creationTime());
+    } else {
+        partMetaData()->creationTime = QDateTime();
+    }
+    if (partMetaData()->signer.isEmpty()) {
+        if (key.numUserIDs() > 0 && key.userID(0).name()) {
+            partMetaData()->signer = prettifyDN(key.userID(0).name());
         }
-        if (partMetaData()->signer.isEmpty()) {
-            if (key.numUserIDs() > 0 && key.userID(0).name()) {
-                partMetaData()->signer = prettifyDN(key.userID(0).name());
-            }
-            if (!partMetaData()->signerMailAddresses.empty()) {
-                if (partMetaData()->signer.isEmpty()) {
-                    partMetaData()->signer = partMetaData()->signerMailAddresses.front();
-                } else {
-                    partMetaData()->signer += QLatin1StringView(" <") + partMetaData()->signerMailAddresses.front() + QLatin1Char('>');
-                }
+        if (!partMetaData()->signerMailAddresses.empty()) {
+            if (partMetaData()->signer.isEmpty()) {
+                partMetaData()->signer = partMetaData()->signerMailAddresses.front();
+            } else {
+                partMetaData()->signer += QLatin1StringView(" <") + partMetaData()->signerMailAddresses.front() + QLatin1Char('>');
             }
         }
-        if (Kleo::DeVSCompliance::isCompliant()) {
-            partMetaData()->isCompliant = signature.isDeVs();
-            partMetaData()->compliance = Kleo::DeVSCompliance::name(signature.isDeVs());
-        } else {
-            partMetaData()->isCompliant = true;
-        }
+    }
+    if (Kleo::DeVSCompliance::isCompliant()) {
+        partMetaData()->isCompliant = signature.isDeVs();
+        partMetaData()->compliance = Kleo::DeVSCompliance::name(signature.isDeVs());
+    } else {
+        partMetaData()->isCompliant = true;
     }
 }
 
 void SignedMessagePart::setVerificationResult(const GpgME::VerificationResult &result, const QByteArray &signedData)
 {
     mSignatures = result.signatures();
     // FIXME
     // mMetaData.auditLogError = result.error;
     mMetaData.isSigned = !mSignatures.empty();
     if (mMetaData.isSigned) {
         sigStatusToMetaData();
         if (!signedData.isEmpty() && mParseAfterDecryption) {
             parseInternal(signedData);
         }
     }
 }
 
 QString SignedMessagePart::plaintextContent() const
 {
     if (!mNode) {
         return MessagePart::text();
     } else {
         return QString();
     }
 }
 
 QString SignedMessagePart::htmlContent() const
 {
     if (!mNode) {
         return MessagePart::text();
     } else {
         return QString();
     }
 }
 
 //-----CryptMessageBlock---------------------
 EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp,
                                            const QString &text,
                                            const QGpgME::Protocol *cryptoProto,
                                            KMime::Content *node,
                                            KMime::Content *encryptedNode,
                                            bool parseAfterDecryption)
     : MessagePart(otp, text, node)
     , mParseAfterDecryption(parseAfterDecryption)
     , mPassphraseError(false)
     , mNoSecKey(false)
     , mDecryptMessage(false)
     , mCryptoProto(cryptoProto)
     , mEncryptedNode(encryptedNode)
 {
     mMetaData.isSigned = false;
     mMetaData.isGoodSignature = false;
     mMetaData.isEncrypted = false;
     mMetaData.isDecryptable = false;
     mMetaData.status = i18ndc("mimetreeparser", "@info:status", "Wrong Crypto Plug-In.");
 }
 
 void EncryptedMessagePart::setIsEncrypted(bool encrypted)
 {
     mMetaData.isEncrypted = encrypted;
 }
 
 bool EncryptedMessagePart::isEncrypted() const
 {
     return mMetaData.isEncrypted;
 }
 
 const QGpgME::Protocol *EncryptedMessagePart::cryptoProto() const
 {
     return mCryptoProto;
 }
 
 void EncryptedMessagePart::setDecryptMessage(bool decrypt)
 {
     mDecryptMessage = decrypt;
 }
 
 bool EncryptedMessagePart::decryptMessage() const
 {
     return mDecryptMessage;
 }
 
 bool EncryptedMessagePart::isDecryptable() const
 {
     return mMetaData.isDecryptable;
 }
 
 bool EncryptedMessagePart::isNoSecKey() const
 {
     return mNoSecKey;
 }
 
 bool EncryptedMessagePart::passphraseError() const
 {
     return mPassphraseError;
 }
 
 bool EncryptedMessagePart::decrypt(KMime::Content &data)
 {
     mError = NoError;
     mMetaData.errorText.clear();
     // FIXME
     //  mMetaData.auditLogError = GpgME::Error();
     mMetaData.auditLog.clear();
 
     const QByteArray ciphertext = data.decodedContent();
     QByteArray plainText;
     auto job = mCryptoProto->decryptVerifyJob();
     const std::pair<GpgME::DecryptionResult, GpgME::VerificationResult> p = job->exec(ciphertext, plainText);
     job->deleteLater();
     auto decryptResult = p.first;
     auto verifyResult = p.second;
     mMetaData.isSigned = verifyResult.signatures().size() > 0;
 
     // Normalize CRLF's
     plainText = KMime::CRLFtoLF(plainText);
     auto codec = QStringDecoder(mOtp->codecNameFor(&data).constData());
     const auto decoded = codec.decode(plainText);
 
     partMetaData()->isSigned = verifyResult.signatures().size() > 0;
 
     if (partMetaData()->isSigned) {
         // We simply attach a signed message part to indicate that this content is also signed
         // We're forwarding mNode to not loose the encoding information
         auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, mCryptoProto, mNode, nullptr));
         subPart->setText(decoded);
         subPart->setVerificationResult(verifyResult, plainText);
         appendSubPart(subPart);
     }
 
     mDecryptRecipients.clear();
     bool cannotDecrypt = false;
     bool bDecryptionOk = !decryptResult.error();
 
     for (const auto &recipient : decryptResult.recipients()) {
         if (!recipient.status()) {
             bDecryptionOk = true;
         }
         GpgME::Key key;
         key = Kleo::KeyCache::instance()->findByKeyIDOrFingerprint(recipient.keyID());
         if (key.isNull()) {
             auto ret = Kleo::KeyCache::instance()->findSubkeysByKeyID({recipient.keyID()});
             if (ret.size() == 1) {
                 key = ret.front().parent();
             }
             if (key.isNull()) {
                 qCDebug(MIMETREEPARSER_CORE_LOG) << "Found no Key for KeyID " << recipient.keyID();
             }
         }
         mDecryptRecipients.emplace_back(recipient, key);
     }
 
     if (!bDecryptionOk && partMetaData()->isSigned) {
         // Only a signed part
         partMetaData()->isEncrypted = false;
         bDecryptionOk = true;
         mDecryptedData = plainText;
     } else {
         mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_BAD_PASSPHRASE;
         mMetaData.isEncrypted = bDecryptionOk || decryptResult.error().code() != GPG_ERR_NO_DATA;
 
         if (decryptResult.error().isCanceled()) {
             setDecryptMessage(false);
         }
 
         partMetaData()->errorText = QString::fromLocal8Bit(decryptResult.error().asString());
         if (Kleo::DeVSCompliance::isCompliant()) {
             partMetaData()->isCompliant = decryptResult.isDeVs();
             partMetaData()->compliance = Kleo::DeVSCompliance::name(decryptResult.isDeVs());
         } else {
             partMetaData()->isCompliant = true;
         }
         if (partMetaData()->isEncrypted && decryptResult.numRecipients() > 0) {
             partMetaData()->keyId = decryptResult.recipient(0).keyID();
         }
 
         if (bDecryptionOk) {
             mDecryptedData = plainText;
         } else {
             mNoSecKey = true;
             const auto decryRecipients = decryptResult.recipients();
             for (const GpgME::DecryptionResult::Recipient &recipient : decryRecipients) {
                 mNoSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY);
             }
             if (!mPassphraseError && !mNoSecKey) { // GpgME do not detect passphrase error correctly
                 mPassphraseError = true;
             }
         }
     }
 
     if (!bDecryptionOk) {
         QString cryptPlugLibName;
         mError = UnknownError;
         if (mCryptoProto) {
             cryptPlugLibName = mCryptoProto->name();
         }
 
         if (mNoSecKey) {
             mError = NoKeyError;
         }
 
         if (mPassphraseError) {
             mError = PassphraseError;
         }
 
         if (!mCryptoProto) {
             partMetaData()->errorText = i18n("No appropriate crypto plug-in was found.");
         } else if (cannotDecrypt) {
             partMetaData()->errorText = i18n("Crypto plug-in \"%1\" cannot decrypt messages.", cryptPlugLibName);
         } else if (!passphraseError()) {
             partMetaData()->errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName) + QLatin1StringView("<br />")
                 + i18n("Error: %1", partMetaData()->errorText);
         }
     }
     return bDecryptionOk;
 }
 
 void EncryptedMessagePart::startDecryption(KMime::Content *data)
 {
     mMetaData.isEncrypted = true;
     mMetaData.isDecryptable = decrypt(*data);
 
     if (mParseAfterDecryption && !mMetaData.isSigned) {
         parseInternal(mDecryptedData);
     } else {
         setText(QString::fromUtf8(mDecryptedData.constData()));
     }
 }
 
 void EncryptedMessagePart::startDecryption()
 {
     if (mEncryptedNode) {
         startDecryption(mEncryptedNode);
     } else {
         startDecryption(mNode);
     }
 }
 
 std::vector<std::pair<GpgME::DecryptionResult::Recipient, GpgME::Key>> EncryptedMessagePart::decryptRecipients() const
 {
     return mDecryptRecipients;
 }
 
 QString EncryptedMessagePart::plaintextContent() const
 {
     if (!mNode) {
         return MessagePart::text();
     } else {
         return QString();
     }
 }
 
 QString EncryptedMessagePart::htmlContent() const
 {
     if (!mNode) {
         return MessagePart::text();
     } else {
         return QString();
     }
 }
 
 QString EncryptedMessagePart::text() const
 {
     if (hasSubParts()) {
         auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>();
         if (_mp) {
             return _mp->text();
         }
     }
 
     return MessagePart::text();
 }
 
 EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message)
     : MessagePart(otp, QString(), node)
     , mMessage(message)
 {
     mMetaData.isEncrypted = false;
     mMetaData.isSigned = false;
     mMetaData.isEncapsulatedRfc822Message = true;
 
     if (!mMessage) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "Node is of type message/rfc822 but doesn't have a message!";
         return;
     }
 
     parseInternal(message.data());
 }
 
 QString EncapsulatedRfc822MessagePart::text() const
 {
     return renderInternalText();
 }
 
 QString EncapsulatedRfc822MessagePart::from() const
 {
     if (auto from = mMessage->from(false)) {
         return from->asUnicodeString();
     }
     return {};
 }
 
 QDateTime EncapsulatedRfc822MessagePart::date() const
 {
     if (auto date = mMessage->date(false)) {
         return date->dateTime();
     }
     return {};
 }
 
 HeadersPart::HeadersPart(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePart(otp, QString(), node)
 {
 }
 
 #include "moc_messagepart.cpp"