File indexing completed on 2024-12-22 05:05:21

0001 // SPDX-FileCopyrightText: 2023 g10 Code GmbH
0002 // SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
0003 // SPDX-License-Identifier: LGPL-2.0-or-later
0004 
0005 #include "../core/utils.h"
0006 #include "messagecontainerwidget_p.h"
0007 #include "urlhandler_p.h"
0008 
0009 #include <KLocalizedString>
0010 #include <KMessageWidget>
0011 #include <Libkleo/Compliance>
0012 #include <Libkleo/Formatting>
0013 #include <QGpgME/Protocol>
0014 
0015 #include <QLabel>
0016 #include <QPaintEvent>
0017 #include <QPainter>
0018 #include <QVBoxLayout>
0019 
0020 namespace
0021 {
0022 
0023 const int borderWidth = 5;
0024 
0025 QColor getColor(PartModel::SecurityLevel securityLevel)
0026 {
0027     const static QHash<PartModel::SecurityLevel, QColor> colors{
0028         {PartModel::Good, QColor(39, 174, 96)}, // Window: ForegroundPositive
0029         {PartModel::Bad, QColor(218, 68, 83)}, // Window: ForegroundNegative
0030         {PartModel::NotSoGood, QColor(246, 116, 0)}, // Window: ForegroundNeutral
0031     };
0032 
0033     return colors.value(securityLevel, QColor());
0034 }
0035 
0036 KMessageWidget::MessageType getType(PartModel::SecurityLevel securityLevel)
0037 {
0038     const static QHash<PartModel::SecurityLevel, KMessageWidget::MessageType> messageTypes{
0039         {PartModel::Good, KMessageWidget::MessageType::Positive},
0040         {PartModel::Bad, KMessageWidget::MessageType::Error},
0041         {PartModel::NotSoGood, KMessageWidget::MessageType::Warning},
0042     };
0043 
0044     return messageTypes.value(securityLevel, KMessageWidget::MessageType::Information);
0045 }
0046 
0047 QString getDetails(const SignatureInfo &signatureDetails)
0048 {
0049     QString href;
0050     if (signatureDetails.cryptoProto) {
0051         href = QStringLiteral("messageviewer:showCertificate#%1 ### %2 ### %3")
0052                    .arg(signatureDetails.cryptoProto->displayName(), signatureDetails.cryptoProto->name(), QString::fromLatin1(signatureDetails.keyId));
0053     }
0054 
0055     QString details;
0056     if (signatureDetails.keyMissing) {
0057         if (Kleo::DeVSCompliance::isCompliant() && signatureDetails.isCompliant) {
0058             details += i18ndc("mimetreeparser",
0059                               "@label",
0060                               "This message has been signed VS-NfD compliant using the certificate <a href=\"%1\">%2</a>.",
0061                               href,
0062                               Kleo::Formatting::prettyID(signatureDetails.keyId.toStdString().data()))
0063                 + QLatin1Char('\n');
0064         } else {
0065             details += i18ndc("mimetreeparser",
0066                               "@label",
0067                               "This message has been signed using the certificate <a href=\"%1\">%2</a>.",
0068                               href,
0069                               Kleo::Formatting::prettyID(signatureDetails.keyId.toStdString().data()))
0070                 + QLatin1Char('\n');
0071         }
0072         details += i18ndc("mimetreeparser", "@label", "The certificate details are not available.");
0073     } else {
0074         QString signerDisplayName = signatureDetails.signer.toHtmlEscaped();
0075         if (signatureDetails.cryptoProto == QGpgME::smime()) {
0076             Kleo::DN dn(signatureDetails.signer);
0077             signerDisplayName = MimeTreeParser::dnToDisplayName(dn).toHtmlEscaped();
0078         }
0079         if (Kleo::DeVSCompliance::isCompliant() && signatureDetails.isCompliant) {
0080             details += i18ndc("mimetreeparser", "@label", "This message has been signed VS-NfD compliant by %1.", signerDisplayName);
0081         } else {
0082             details += i18ndc("mimetreeparser", "@label", "This message has been signed by %1.", signerDisplayName);
0083         }
0084         if (signatureDetails.keyRevoked) {
0085             details += QLatin1Char('\n') + i18ndc("mimetreeparser", "@label", "The <a href=\"%1\">certificate</a> was revoked.", href);
0086         }
0087         if (signatureDetails.keyExpired) {
0088             details += QLatin1Char('\n') + i18ndc("mimetreeparser", "@label", "The <a href=\"%1\">certificate</a> was expired.", href);
0089         }
0090 
0091         if (signatureDetails.keyTrust == GpgME::Signature::Unknown) {
0092             details += QLatin1Char(' ')
0093                 + i18ndc("mimetreeparser", "@label", "The signature is valid, but the <a href=\"%1\">certificate</a>'s validity is unknown.", href);
0094         } else if (signatureDetails.keyTrust == GpgME::Signature::Marginal) {
0095             details += QLatin1Char(' ')
0096                 + i18ndc("mimetreeparser", "@label", "The signature is valid and the <a href=\"%1\">certificate</a> is marginally trusted.", href);
0097         } else if (signatureDetails.keyTrust == GpgME::Signature::Full) {
0098             details +=
0099                 QLatin1Char(' ') + i18ndc("mimetreeparser", "@label", "The signature is valid and the <a href=\"%1\">certificate</a> is fully trusted.", href);
0100         } else if (signatureDetails.keyTrust == GpgME::Signature::Ultimate) {
0101             details += QLatin1Char(' ')
0102                 + i18ndc("mimetreeparser", "@label", "The signature is valid and the <a href=\"%1\">certificate</a> is ultimately trusted.", href);
0103         } else {
0104             details +=
0105                 QLatin1Char(' ') + i18ndc("mimetreeparser", "@label", "The signature is valid, but the <a href=\"%1\">certificate</a> is untrusted.", href);
0106         }
0107         if (!signatureDetails.signatureIsGood && !signatureDetails.keyRevoked && !signatureDetails.keyExpired
0108             && signatureDetails.keyTrust != GpgME::Signature::Unknown) {
0109             details += QLatin1Char(' ') + i18ndc("mimetreeparser", "@label", "The signature is invalid.");
0110         }
0111     }
0112     return details;
0113 }
0114 
0115 }
0116 
0117 MessageWidgetContainer::MessageWidgetContainer(bool isSigned,
0118                                                const SignatureInfo &signatureInfo,
0119                                                PartModel::SecurityLevel signatureSecurityLevel,
0120                                                bool displaySignatureInfo,
0121                                                bool isEncrypted,
0122                                                const SignatureInfo &encryptionInfo,
0123                                                PartModel::SecurityLevel encryptionSecurityLevel,
0124                                                bool displayEncryptionInfo,
0125                                                UrlHandler *urlHandler,
0126                                                QWidget *parent)
0127     : QFrame(parent)
0128     , m_isSigned(isSigned)
0129     , m_signatureInfo(signatureInfo)
0130     , m_signatureSecurityLevel(signatureSecurityLevel)
0131     , m_displaySignatureInfo(displaySignatureInfo)
0132     , m_isEncrypted(isEncrypted)
0133     , m_encryptionInfo(encryptionInfo)
0134     , m_encryptionSecurityLevel(encryptionSecurityLevel)
0135     , m_displayEncryptionInfo(displayEncryptionInfo)
0136     , m_urlHandler(urlHandler)
0137 {
0138     createLayout();
0139 }
0140 
0141 MessageWidgetContainer::~MessageWidgetContainer() = default;
0142 
0143 void MessageWidgetContainer::paintEvent(QPaintEvent *event)
0144 {
0145     Q_UNUSED(event)
0146 
0147     if (!m_isSigned && !m_isEncrypted) {
0148         return;
0149     }
0150 
0151     QPainter painter(this);
0152     if (layoutDirection() == Qt::RightToLeft) {
0153         auto r = rect();
0154         r.setX(width() - borderWidth);
0155         r.setWidth(borderWidth);
0156         const QColor color = getColor(PartModel::SecurityLevel::Good);
0157         painter.setRenderHint(QPainter::Antialiasing);
0158         painter.setBrush(QColor(color));
0159         painter.setPen(QPen(Qt::NoPen));
0160         painter.drawRect(r);
0161     } else {
0162         auto r = rect();
0163         r.setWidth(borderWidth);
0164         const QColor color = getColor(PartModel::SecurityLevel::Good);
0165         painter.setRenderHint(QPainter::Antialiasing);
0166         painter.setBrush(QColor(color));
0167         painter.setPen(QPen(Qt::NoPen));
0168         painter.drawRect(r);
0169     }
0170 }
0171 
0172 bool MessageWidgetContainer::event(QEvent *event)
0173 {
0174     if (event->type() == QEvent::Polish && !layout()) {
0175         createLayout();
0176     }
0177 
0178     return QFrame::event(event);
0179 }
0180 
0181 void MessageWidgetContainer::createLayout()
0182 {
0183     delete layout();
0184 
0185     auto vLayout = new QVBoxLayout(this);
0186 
0187     if (m_isSigned || m_isEncrypted) {
0188         if (layoutDirection() == Qt::RightToLeft) {
0189             layout()->setContentsMargins(0, 0, borderWidth * 2, 0);
0190         } else {
0191             layout()->setContentsMargins(borderWidth * 2, 0, 0, 0);
0192         }
0193     }
0194 
0195     if (m_isEncrypted && m_displayEncryptionInfo) {
0196         auto encryptionMessage = new KMessageWidget(this);
0197         encryptionMessage->setObjectName(QLatin1StringView("EncryptionMessage"));
0198         encryptionMessage->setCloseButtonVisible(false);
0199 
0200         QString text;
0201         if (m_encryptionInfo.keyId.isEmpty()) {
0202             encryptionMessage->setIcon(QIcon::fromTheme(QStringLiteral("data-error")));
0203             encryptionMessage->setMessageType(KMessageWidget::Error);
0204             if (Kleo::DeVSCompliance::isCompliant() && m_encryptionInfo.isCompliant) {
0205                 text = i18n("This message is VS-NfD compliant encrypted but we don't have the certificate for it.", QString::fromUtf8(m_encryptionInfo.keyId));
0206             } else {
0207                 text = i18n("This message is encrypted but we don't have the certificate for it.");
0208             }
0209         } else {
0210             encryptionMessage->setIcon(QIcon::fromTheme(QStringLiteral("mail-encrypted")));
0211             encryptionMessage->setMessageType(KMessageWidget::Positive);
0212             if (Kleo::DeVSCompliance::isCompliant() && m_encryptionInfo.isCompliant) {
0213                 text = i18n("This message is VS-NfD compliant encrypted.");
0214             } else {
0215                 text = i18n("This message is encrypted.");
0216             }
0217         }
0218 
0219         encryptionMessage->setText(text + QLatin1Char(' ') + QStringLiteral("<a href=\"messageviewer:showDetails\">Details</a>"));
0220 
0221         connect(encryptionMessage, &KMessageWidget::linkActivated, this, [this, encryptionMessage, text](const QString &link) {
0222             QUrl url(link);
0223             if (url.path() == QStringLiteral("showDetails")) {
0224                 QString newText = text + QLatin1Char(' ') + i18n("The message is encrypted for the following certificates:");
0225 
0226                 newText += MimeTreeParser::decryptRecipientsToHtml(m_encryptionInfo.decryptRecipients, m_encryptionInfo.cryptoProto);
0227 
0228                 encryptionMessage->setText(newText);
0229                 return;
0230             }
0231 
0232             if (url.path() == QStringLiteral("showCertificate")) {
0233                 m_urlHandler->handleClick(QUrl(link), window()->windowHandle());
0234             }
0235         });
0236 
0237         vLayout->addWidget(encryptionMessage);
0238     }
0239 
0240     if (m_isSigned && m_displaySignatureInfo) {
0241         auto signatureMessage = new KMessageWidget(this);
0242         signatureMessage->setObjectName(QStringLiteral("SignatureMessage"));
0243         signatureMessage->setCloseButtonVisible(false);
0244         signatureMessage->setText(getDetails(m_signatureInfo));
0245         connect(signatureMessage, &KMessageWidget::linkActivated, this, [this](const QString &link) {
0246             m_urlHandler->handleClick(QUrl(link), window()->windowHandle());
0247         });
0248         signatureMessage->setMessageType(getType(m_signatureSecurityLevel));
0249         switch (m_signatureSecurityLevel) {
0250         case PartModel::Good:
0251             signatureMessage->setIcon(QIcon::fromTheme(QStringLiteral("mail-signed")));
0252             break;
0253         case PartModel::Bad:
0254             signatureMessage->setIcon(QIcon::fromTheme(QStringLiteral("data-error")));
0255             break;
0256         case PartModel::NotSoGood:
0257             signatureMessage->setIcon(QIcon::fromTheme(QStringLiteral("data-warning")));
0258             break;
0259         default:
0260             break;
0261         }
0262         signatureMessage->setWordWrap(true);
0263 
0264         vLayout->addWidget(signatureMessage);
0265     }
0266 }
0267 
0268 #include "moc_messagecontainerwidget_p.cpp"