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

0001 // SPDX-FileCopyrightText: 2016 Sandro Knauß <knauss@kolabsystems.com>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include <MimeTreeParserCore/ObjectTreeParser>
0005 
0006 #include <QDebug>
0007 #include <QTest>
0008 #include <QTimeZone>
0009 
0010 QByteArray readMailFromFile(const QString &mailFile)
0011 {
0012     QFile file(QLatin1StringView(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile);
0013     file.open(QIODevice::ReadOnly);
0014     Q_ASSERT(file.isOpen());
0015     return file.readAll();
0016 }
0017 
0018 class MimeTreeParserTest : public QObject
0019 {
0020     Q_OBJECT
0021 
0022 private Q_SLOTS:
0023     void testTextMail()
0024     {
0025         const auto expectedText = QStringLiteral(
0026             "If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to view the newsletter "
0027             "on our website: http://www.gog.com/newsletter/");
0028         MimeTreeParser::ObjectTreeParser otp;
0029         otp.parseObjectTree(readMailFromFile(QLatin1StringView("plaintext.mbox")));
0030         auto partList = otp.collectContentParts();
0031         QCOMPARE(partList.size(), 1);
0032         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0033         QCOMPARE(part->text(), expectedText);
0034         QCOMPARE(part->charset(), QStringLiteral("utf-8").toLocal8Bit());
0035 
0036         QCOMPARE(part->encryptions().size(), 0);
0037         QCOMPARE(part->signatures().size(), 0);
0038 
0039         QCOMPARE(otp.collectAttachmentParts().size(), 0);
0040 
0041         QCOMPARE(otp.plainTextContent(), expectedText);
0042         QVERIFY(otp.htmlContent().isEmpty());
0043     }
0044 
0045     void testAlternative()
0046     {
0047         MimeTreeParser::ObjectTreeParser otp;
0048         otp.parseObjectTree(readMailFromFile(QLatin1StringView("alternative.mbox")));
0049         auto partList = otp.collectContentParts();
0050         QCOMPARE(partList.size(), 1);
0051         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
0052         QVERIFY(bool(part));
0053         QCOMPARE(part->plaintextContent(),
0054                  QStringLiteral("If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to "
0055                                 "view the newsletter on our website: http://www.gog.com/newsletter/\n"));
0056         QCOMPARE(part->charset(), QStringLiteral("us-ascii").toLocal8Bit());
0057         QCOMPARE(part->htmlContent(), QStringLiteral("<html><body><p><span>HTML</span> text</p></body></html>\n\n"));
0058         QCOMPARE(otp.collectAttachmentParts().size(), 0);
0059         QCOMPARE(part->encryptions().size(), 0);
0060         QCOMPARE(part->signatures().size(), 0);
0061     }
0062 
0063     void testTextHtml()
0064     {
0065         auto expectedText = QStringLiteral("<html><body><p><span>HTML</span> text</p></body></html>");
0066         MimeTreeParser::ObjectTreeParser otp;
0067         otp.parseObjectTree(readMailFromFile(QLatin1StringView("html.mbox")));
0068         otp.print();
0069         auto partList = otp.collectContentParts();
0070         QCOMPARE(partList.size(), 1);
0071         auto part = partList[0].dynamicCast<MimeTreeParser::HtmlMessagePart>();
0072         QVERIFY(bool(part));
0073         QCOMPARE(part->htmlContent(), expectedText);
0074         QCOMPARE(part->charset(), QStringLiteral("windows-1252").toLocal8Bit());
0075         QCOMPARE(part->encryptions().size(), 0);
0076         QCOMPARE(part->signatures().size(), 0);
0077         auto contentAttachmentList = otp.collectAttachmentParts();
0078         QCOMPARE(contentAttachmentList.size(), 0);
0079 
0080         QCOMPARE(otp.htmlContent(), expectedText);
0081         QVERIFY(otp.plainTextContent().isEmpty());
0082     }
0083 
0084     void testSMimeEncrypted()
0085     {
0086         MimeTreeParser::ObjectTreeParser otp;
0087         otp.parseObjectTree(readMailFromFile(QLatin1StringView("smime-encrypted.mbox")));
0088         otp.print();
0089         otp.decryptAndVerify();
0090         otp.print();
0091         auto partList = otp.collectContentParts();
0092         QCOMPARE(partList.size(), 1);
0093         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0094         QVERIFY(bool(part));
0095         QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog."));
0096         QCOMPARE(part->charset(), QStringLiteral("us-ascii").toLocal8Bit());
0097         QCOMPARE(part->encryptions().size(), 1);
0098         QCOMPARE(part->signatures().size(), 0);
0099         auto contentAttachmentList = otp.collectAttachmentParts();
0100         QCOMPARE(contentAttachmentList.size(), 0);
0101     }
0102 
0103     void testOpenPGPEncryptedAttachment()
0104     {
0105         MimeTreeParser::ObjectTreeParser otp;
0106         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")));
0107         otp.print();
0108         otp.decryptAndVerify();
0109         otp.print();
0110         auto partList = otp.collectContentParts();
0111         QCOMPARE(partList.size(), 1);
0112         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0113         QVERIFY(bool(part));
0114         QCOMPARE(part->text(), QStringLiteral("test text"));
0115         QCOMPARE(part->charset(), QStringLiteral("us-ascii").toLocal8Bit());
0116         QCOMPARE(part->encryptions().size(), 1);
0117         QCOMPARE(part->signatures().size(), 1);
0118         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
0119         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
0120         auto contentAttachmentList = otp.collectAttachmentParts();
0121         QCOMPARE(contentAttachmentList.size(), 2);
0122         //     QCOMPARE(contentAttachmentList[0]->availableContents(), QList<QByteArray>() << "text/plain");
0123         // QCOMPARE(contentAttachmentList[0]->content().size(), 1);
0124         QCOMPARE(contentAttachmentList[0]->encryptions().size(), 1);
0125         QCOMPARE(contentAttachmentList[0]->signatures().size(), 1);
0126         QCOMPARE(contentAttachmentList[0]->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
0127         QCOMPARE(contentAttachmentList[0]->signatureState(), MimeTreeParser::KMMsgFullySigned);
0128         //     QCOMPARE(contentAttachmentList[1]->availableContents(), QList<QByteArray>() << "image/png");
0129         //     QCOMPARE(contentAttachmentList[1]->content().size(), 1);
0130         QCOMPARE(contentAttachmentList[1]->encryptions().size(), 0);
0131         QCOMPARE(contentAttachmentList[1]->signatures().size(), 0);
0132         QCOMPARE(contentAttachmentList[1]->encryptionState(), MimeTreeParser::KMMsgNotEncrypted);
0133         QCOMPARE(contentAttachmentList[1]->signatureState(), MimeTreeParser::KMMsgNotSigned);
0134     }
0135 
0136     void testOpenPGPInline()
0137     {
0138         MimeTreeParser::ObjectTreeParser otp;
0139         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-inline-charset-encrypted.mbox")));
0140         otp.print();
0141         otp.decryptAndVerify();
0142         otp.print();
0143         auto partList = otp.collectContentParts();
0144         QCOMPARE(partList.size(), 1);
0145         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0146         QVERIFY(bool(part));
0147         QCOMPARE(part->charset(), QStringLiteral("ISO-8859-15").toLocal8Bit());
0148         QCOMPARE(part->text(), QString::fromUtf8("asdasd asd asd asdf sadf sdaf sadf öäü"));
0149 
0150         QCOMPARE(part->encryptions().size(), 1);
0151         QCOMPARE(part->signatures().size(), 1);
0152         QCOMPARE(otp.collectAttachmentParts().size(), 0);
0153     }
0154 
0155     void testOpenPPGInlineWithNonEncText()
0156     {
0157         MimeTreeParser::ObjectTreeParser otp;
0158         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-inline-encrypted+nonenc.mbox")));
0159         otp.print();
0160         otp.decryptAndVerify();
0161         otp.print();
0162         auto partList = otp.collectContentParts();
0163         QCOMPARE(partList.size(), 1);
0164         auto part1 = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0165         QVERIFY(bool(part1));
0166         QCOMPARE(part1->text(), QStringLiteral("Not encrypted not signed :(\n\nsome random text"));
0167         // TODO test if we get the proper subparts with the appropriate encryptions
0168         QCOMPARE(part1->charset(), QStringLiteral("us-ascii").toLocal8Bit());
0169 
0170         QCOMPARE(part1->encryptionState(), MimeTreeParser::KMMsgPartiallyEncrypted);
0171         QCOMPARE(part1->signatureState(), MimeTreeParser::KMMsgNotSigned);
0172 
0173         // QCOMPARE(part1->text(), QStringLiteral("Not encrypted not signed :(\n\n"));
0174         // QCOMPARE(part1->charset(), QStringLiteral("us-ascii").toLocal8Bit());
0175         // QCOMPARE(contentList[1]->content(), QStringLiteral("some random text").toLocal8Bit());
0176         // QCOMPARE(contentList[1]->charset(), QStringLiteral("us-ascii").toLocal8Bit());
0177         // QCOMPARE(contentList[1]->encryptions().size(), 1);
0178         // QCOMPARE(contentList[1]->signatures().size(), 0);
0179         QCOMPARE(otp.collectAttachmentParts().size(), 0);
0180     }
0181 
0182     void testEncryptionBlock()
0183     {
0184         MimeTreeParser::ObjectTreeParser otp;
0185         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")));
0186         otp.print();
0187         otp.decryptAndVerify();
0188         otp.print();
0189         auto partList = otp.collectContentParts();
0190         QCOMPARE(partList.size(), 1);
0191         auto part1 = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0192         QVERIFY(bool(part1));
0193         QCOMPARE(part1->encryptions().size(), 1);
0194         //     auto enc = contentList[0]->encryptions()[0];
0195         //     QCOMPARE((int) enc->recipients().size(), 2);
0196 
0197         //     auto r = enc->recipients()[0];
0198         //     QCOMPARE(r->keyid(),QStringLiteral("14B79E26050467AA"));
0199         //     QCOMPARE(r->name(),QStringLiteral("kdetest"));
0200         //     QCOMPARE(r->email(),QStringLiteral("you@you.com"));
0201         //     QCOMPARE(r->comment(),QStringLiteral(""));
0202 
0203         //     r = enc->recipients()[1];
0204         //     QCOMPARE(r->keyid(),QStringLiteral("8D9860C58F246DE6"));
0205         //     QCOMPARE(r->name(),QStringLiteral("unittest key"));
0206         //     QCOMPARE(r->email(),QStringLiteral("test@kolab.org"));
0207         //     QCOMPARE(r->comment(),QStringLiteral("no password"));
0208         auto attachmentList = otp.collectAttachmentParts();
0209         QCOMPARE(attachmentList.size(), 2);
0210         auto attachment1 = attachmentList[0];
0211         QVERIFY(attachment1->node());
0212         QCOMPARE(attachment1->filename(), QStringLiteral("file.txt"));
0213         auto attachment2 = attachmentList[1];
0214         QVERIFY(attachment2->node());
0215         QCOMPARE(attachment2->filename(), QStringLiteral("image.png"));
0216     }
0217 
0218     void testSignatureBlock()
0219     {
0220         MimeTreeParser::ObjectTreeParser otp;
0221         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")));
0222         otp.print();
0223         otp.decryptAndVerify();
0224         otp.print();
0225         auto partList = otp.collectContentParts();
0226         QCOMPARE(partList.size(), 1);
0227         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0228         QVERIFY(bool(part));
0229 
0230         // QCOMPARE(contentList[0]->signatures().size(), 1);
0231         // auto sig = contentList[0]->signatures()[0];
0232         // QCOMPARE(sig->creationDateTime(), QDateTime(QDate(2015,05,01),QTime(15,12,47)));
0233         // QCOMPARE(sig->expirationDateTime(), QDateTime());
0234         // QCOMPARE(sig->neverExpires(), true);
0235 
0236         // auto key = sig->key();
0237         // QCOMPARE(key->keyid(),QStringLiteral("8D9860C58F246DE6"));
0238         // QCOMPARE(key->name(),QStringLiteral("unittest key"));
0239         // QCOMPARE(key->email(),QStringLiteral("test@kolab.org"));
0240         // QCOMPARE(key->comment(),QStringLiteral("no password"));
0241     }
0242 
0243     void testRelatedAlternative()
0244     {
0245         MimeTreeParser::ObjectTreeParser otp;
0246         otp.parseObjectTree(readMailFromFile(QLatin1StringView("cid-links.mbox")));
0247         otp.print();
0248         auto partList = otp.collectContentParts();
0249         QCOMPARE(partList.size(), 1);
0250         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0251         QVERIFY(bool(part));
0252         QCOMPARE(part->encryptions().size(), 0);
0253         QCOMPARE(part->signatures().size(), 0);
0254         QCOMPARE(otp.collectAttachmentParts().size(), 1);
0255     }
0256 
0257     void testAttachmentPart()
0258     {
0259         MimeTreeParser::ObjectTreeParser otp;
0260         otp.parseObjectTree(readMailFromFile(QLatin1StringView("attachment.mbox")));
0261         otp.print();
0262         auto partList = otp.collectAttachmentParts();
0263         QCOMPARE(partList.size(), 1);
0264         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0265         QVERIFY(bool(part));
0266         QCOMPARE(part->mimeType(), "image/jpeg");
0267         QCOMPARE(part->filename(), QStringLiteral("aqnaozisxya.jpeg"));
0268     }
0269 
0270     void testAttachment2Part()
0271     {
0272         MimeTreeParser::ObjectTreeParser otp;
0273         otp.parseObjectTree(readMailFromFile(QLatin1StringView("attachment2.mbox")));
0274         otp.print();
0275         auto partList = otp.collectAttachmentParts();
0276         QCOMPARE(partList.size(), 1);
0277         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0278         QVERIFY(bool(part));
0279         QCOMPARE(part->mimeType(), "image/jpeg");
0280         QCOMPARE(part->filename(), QStringLiteral("aqnaozisxya.jpeg"));
0281     }
0282 
0283     void testCidLink()
0284     {
0285         MimeTreeParser::ObjectTreeParser otp;
0286         otp.parseObjectTree(readMailFromFile(QLatin1StringView("cid-links.mbox")));
0287         otp.print();
0288         auto partList = otp.collectContentParts();
0289         QCOMPARE(partList.size(), 1);
0290         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
0291         QVERIFY(bool(part));
0292         auto resolvedContent = otp.resolveCidLinks(part->htmlContent());
0293         QVERIFY(!resolvedContent.contains(QLatin1StringView("cid:")));
0294     }
0295 
0296     void testCidLinkInForwardedInline()
0297     {
0298         MimeTreeParser::ObjectTreeParser otp;
0299         otp.parseObjectTree(readMailFromFile(QLatin1StringView("cid-links-forwarded-inline.mbox")));
0300         otp.print();
0301         auto partList = otp.collectContentParts();
0302         QCOMPARE(partList.size(), 1);
0303         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
0304         QVERIFY(bool(part));
0305         auto resolvedContent = otp.resolveCidLinks(part->htmlContent());
0306         QVERIFY(!resolvedContent.contains(QLatin1StringView("cid:")));
0307     }
0308 
0309     void testOpenPGPInlineError()
0310     {
0311         MimeTreeParser::ObjectTreeParser otp;
0312         otp.parseObjectTree(readMailFromFile(QLatin1StringView("inlinepgpgencrypted-error.mbox")));
0313         otp.print();
0314         otp.decryptAndVerify();
0315         otp.print();
0316         auto partList = otp.collectContentParts();
0317         QCOMPARE(partList.size(), 1);
0318         auto part = partList[0].dynamicCast<MimeTreeParser::EncryptedMessagePart>();
0319         QVERIFY(bool(part));
0320         QVERIFY(part->error());
0321     }
0322 
0323     void testEncapsulated()
0324     {
0325         MimeTreeParser::ObjectTreeParser otp;
0326         otp.parseObjectTree(readMailFromFile(QLatin1StringView("encapsulated-with-attachment.mbox")));
0327         otp.decryptAndVerify();
0328         auto partList = otp.collectContentParts();
0329         QCOMPARE(partList.size(), 2);
0330         auto part = partList[1].dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>();
0331         QVERIFY(bool(part));
0332         QCOMPARE(part->from(), QLatin1StringView("Thomas McGuire <dontspamme@gmx.net>"));
0333         QCOMPARE(part->date().toString(), QLatin1StringView("Wed Aug 5 10:57:58 2009 GMT+0200"));
0334         auto subPartList = otp.collectContentParts(part);
0335         QCOMPARE(subPartList.size(), 1);
0336         qWarning() << subPartList[0]->metaObject()->className();
0337         auto subPart = subPartList[0].dynamicCast<MimeTreeParser::TextMessagePart>();
0338         QVERIFY(bool(subPart));
0339     }
0340 
0341     void test8bitEncodedInPlaintext()
0342     {
0343         MimeTreeParser::ObjectTreeParser otp;
0344         otp.parseObjectTree(readMailFromFile(QLatin1StringView("8bitencoded.mbox")));
0345         QVERIFY(otp.plainTextContent().contains(QString::fromUtf8("Why Pisa’s Tower")));
0346         QVERIFY(otp.htmlContent().contains(QString::fromUtf8("Why Pisa’s Tower")));
0347     }
0348 
0349     void testInlineSigned()
0350     {
0351         MimeTreeParser::ObjectTreeParser otp;
0352         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-inline-signed.mbox")));
0353         otp.decryptAndVerify();
0354         auto partList = otp.collectContentParts();
0355         QCOMPARE(partList.size(), 1);
0356         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0357         QCOMPARE(part->signatures().size(), 1);
0358         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgNotEncrypted);
0359         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
0360         QCOMPARE(part->text(), QString::fromUtf8("ohno öäü\n"));
0361 
0362         QVERIFY(otp.plainTextContent().contains(QString::fromUtf8("ohno öäü")));
0363 
0364         auto signaturePart = part->signatures().first();
0365         QCOMPARE(signaturePart->partMetaData()->isGoodSignature, true);
0366         QCOMPARE(signaturePart->partMetaData()->isTrusted(), true);
0367         QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::KeyMissing, false);
0368         QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::KeyExpired, false);
0369         QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::KeyRevoked, false);
0370         QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::SigExpired, false);
0371         QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::CrlMissing, false);
0372         QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::CrlTooOld, false);
0373         QCOMPARE(signaturePart->partMetaData()->keyId, QByteArray{"8D9860C58F246DE6"});
0374         QCOMPARE(signaturePart->partMetaData()->signer, QLatin1StringView{"unittest key (no password) <test@kolab.org>"});
0375         QCOMPARE(signaturePart->partMetaData()->signerMailAddresses, QStringList{{QStringLiteral("test@kolab.org")}});
0376     }
0377 
0378     void testEncryptedAndSigned()
0379     {
0380         MimeTreeParser::ObjectTreeParser otp;
0381         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-encrypted+signed.mbox")));
0382         otp.decryptAndVerify();
0383         auto partList = otp.collectContentParts();
0384         QCOMPARE(partList.size(), 1);
0385         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0386         QCOMPARE(part->signatures().size(), 1);
0387         QCOMPARE(part->encryptions().size(), 1);
0388         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
0389         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
0390         QVERIFY(otp.plainTextContent().contains(QString::fromUtf8("encrypted message text")));
0391 
0392         auto signaturePart = part->signatures().first();
0393         QCOMPARE(signaturePart->partMetaData()->keyId, QByteArray{"8D9860C58F246DE6"});
0394         QCOMPARE(signaturePart->partMetaData()->isGoodSignature, true);
0395     }
0396 
0397     void testOpenpgpMultipartEmbedded()
0398     {
0399         MimeTreeParser::ObjectTreeParser otp;
0400         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-multipart-embedded.mbox")));
0401         otp.print();
0402         otp.decryptAndVerify();
0403         otp.print();
0404         auto partList = otp.collectContentParts();
0405         QCOMPARE(partList.size(), 1);
0406         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0407         QCOMPARE(part->encryptions().size(), 1);
0408         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
0409         QCOMPARE(otp.plainTextContent(), QString::fromUtf8("sdflskjsdf\n\n-- \nThis is a HTML signature.\n"));
0410     }
0411 
0412     void testOpenpgpMultipartEmbeddedSigned()
0413     {
0414         MimeTreeParser::ObjectTreeParser otp;
0415         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-multipart-embedded-signed.mbox")));
0416         otp.decryptAndVerify();
0417         auto partList = otp.collectContentParts();
0418         QCOMPARE(partList.size(), 1);
0419         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0420         QCOMPARE(part->encryptions().size(), 1);
0421         QCOMPARE(part->signatures().size(), 1);
0422         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
0423         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
0424         QCOMPARE(otp.plainTextContent(), QString::fromUtf8("test\n\n-- \nThis is a HTML signature.\n"));
0425 
0426         auto signaturePart = part->signatures().first();
0427         QVERIFY(signaturePart->partMetaData()->keyId.endsWith(QByteArray{"2E3B7787B1B75920"}));
0428         // We lack the public key for this message
0429         QCOMPARE(false, signaturePart->partMetaData()->isGoodSignature);
0430         QCOMPARE(GpgME::Signature::KeyMissing, signaturePart->partMetaData()->sigSummary);
0431     }
0432 
0433     void testAppleHtmlWithAttachments()
0434     {
0435         MimeTreeParser::ObjectTreeParser otp;
0436         otp.parseObjectTree(readMailFromFile(QLatin1StringView("applehtmlwithattachments.mbox")));
0437         otp.decryptAndVerify();
0438         auto partList = otp.collectContentParts();
0439         QCOMPARE(partList.size(), 1);
0440         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
0441         QVERIFY(part);
0442         QCOMPARE(part->encryptions().size(), 0);
0443         QCOMPARE(part->signatures().size(), 0);
0444         QVERIFY(part->isHtml());
0445         QCOMPARE(otp.plainTextContent(), QString::fromUtf8("Hi,\n\nThis is an HTML message with attachments.\n\nCheers,\nChristian"));
0446         QCOMPARE(otp.htmlContent(),
0447                  QString::fromUtf8(
0448                      "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=us-ascii\"></head><body style=\"word-wrap: break-word; "
0449                      "-webkit-nbsp-mode: space; line-break: after-white-space;\" class=\"\"><meta http-equiv=\"Content-Type\" content=\"text/html; "
0450                      "charset=us-ascii\" class=\"\"><div style=\"word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;\" "
0451                      "class=\"\">Hi,<div class=\"\"><br class=\"\"></div><blockquote style=\"margin: 0 0 0 40px; border: none; padding: 0px;\" class=\"\"><div "
0452                      "class=\"\">This is an <b class=\"\">HTML</b> message with attachments.</div></blockquote><div class=\"\"><br class=\"\"></div><div "
0453                      "class=\"\">Cheers,</div><div class=\"\">Christian<img apple-inline=\"yes\" id=\"B9EE68A9-83CA-41CD-A3E4-E5BA301F797A\" class=\"\" "
0454                      "src=\"cid:F5B62D1D-E4EC-4C59-AA5A-708525C2AC3C\"></div></div></body></html>"));
0455 
0456         auto attachments = otp.collectAttachmentParts();
0457         QCOMPARE(attachments.size(), 1);
0458     }
0459 
0460     void testAppleHtmlWithAttachmentsMixed()
0461     {
0462         MimeTreeParser::ObjectTreeParser otp;
0463         otp.parseObjectTree(readMailFromFile(QLatin1StringView("applehtmlwithattachmentsmixed.mbox")));
0464         otp.decryptAndVerify();
0465         otp.print();
0466         auto partList = otp.collectContentParts();
0467         QCOMPARE(partList.size(), 1);
0468         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
0469         QVERIFY(part);
0470         QCOMPARE(part->encryptions().size(), 0);
0471         QCOMPARE(part->signatures().size(), 0);
0472         QVERIFY(part->isHtml());
0473         QCOMPARE(otp.plainTextContent(), QString::fromUtf8("Hello\n\n\n\nRegards\n\nFsdfsdf"));
0474         QCOMPARE(otp.htmlContent(),
0475                  QString::fromUtf8(
0476                      "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=us-ascii\"></head><body style=\"word-wrap: break-word; "
0477                      "-webkit-nbsp-mode: space; line-break: after-white-space;\" class=\"\"><strike class=\"\">Hello</strike><div class=\"\"><br "
0478                      "class=\"\"></div><div class=\"\"></div></body></html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; "
0479                      "charset=us-ascii\"></head><body style=\"word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;\" "
0480                      "class=\"\"><div class=\"\"></div><div class=\"\"><br class=\"\"></div><div class=\"\"><b class=\"\">Regards</b></div><div class=\"\"><b "
0481                      "class=\"\"><br class=\"\"></b></div><div class=\"\">Fsdfsdf</div></body></html>"));
0482 
0483         auto attachments = otp.collectAttachmentParts();
0484         QCOMPARE(attachments.size(), 1);
0485     }
0486 
0487     void testInvitation()
0488     {
0489         MimeTreeParser::ObjectTreeParser otp;
0490         otp.parseObjectTree(readMailFromFile(QLatin1StringView("invitation.mbox")));
0491         otp.decryptAndVerify();
0492         otp.print();
0493         auto partList = otp.collectContentParts();
0494         QCOMPARE(partList.size(), 1);
0495         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
0496         QVERIFY(part);
0497         QCOMPARE(part->encryptions().size(), 0);
0498         QCOMPARE(part->signatures().size(), 0);
0499         QVERIFY(!part->isHtml());
0500         QVERIFY(part->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal));
0501 
0502         auto attachments = otp.collectAttachmentParts();
0503         QCOMPARE(attachments.size(), 0);
0504     }
0505 
0506     void testGmailInvitation()
0507     {
0508         MimeTreeParser::ObjectTreeParser otp;
0509         otp.parseObjectTree(readMailFromFile(QLatin1StringView("gmail-invitation.mbox")));
0510         otp.decryptAndVerify();
0511         otp.print();
0512         auto partList = otp.collectContentParts();
0513         QCOMPARE(partList.size(), 1);
0514         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
0515         QVERIFY(part);
0516         QCOMPARE(part->encryptions().size(), 0);
0517         qWarning() << part;
0518         QCOMPARE(part->signatures().size(), 0);
0519         QVERIFY(part->isHtml());
0520         QVERIFY(part->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal));
0521 
0522         auto attachments = otp.collectAttachmentParts();
0523         QCOMPARE(attachments.size(), 1);
0524     }
0525 
0526     void testMemoryHole()
0527     {
0528         MimeTreeParser::ObjectTreeParser otp;
0529         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-encrypted-memoryhole.mbox")));
0530         otp.decryptAndVerify();
0531         otp.print();
0532 
0533         auto partList = otp.collectContentParts();
0534         QCOMPARE(partList.size(), 1);
0535         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0536         QVERIFY(bool(part));
0537 
0538         QCOMPARE(part->text(), QStringLiteral("very secret mesage\n"));
0539 
0540         QCOMPARE(part->header("subject")->asUnicodeString(), QStringLiteral("hidden subject"));
0541         QCOMPARE(part->header("from")->asUnicodeString(), QStringLiteral("you@example.com"));
0542         QCOMPARE(part->header("to")->asUnicodeString(), QStringLiteral("me@example.com"));
0543         QCOMPARE(part->header("cc")->asUnicodeString(), QStringLiteral("cc@example.com"));
0544         QCOMPARE(part->header("message-id")->asUnicodeString(), QStringLiteral("<myhiddenreference@me>"));
0545         QCOMPARE(part->header("references")->asUnicodeString(), QStringLiteral("<hiddenreference@hidden>"));
0546         QCOMPARE(part->header("in-reply-to")->asUnicodeString(), QStringLiteral("<hiddenreference@hidden>"));
0547         QCOMPARE(static_cast<const KMime::Headers::Date *>(part->header("date"))->dateTime(), QDateTime(QDate(2018, 1, 2), QTime(3, 4, 5), QTimeZone::utc()));
0548     }
0549 
0550     /**
0551      * Required special handling because the list replaces the toplevel part.
0552      */
0553     void testMemoryHoleWithList()
0554     {
0555         MimeTreeParser::ObjectTreeParser otp;
0556         otp.parseObjectTree(readMailFromFile(QLatin1StringView("cid-links-forwarded-inline.mbox")));
0557         const auto parts = otp.collectContentParts();
0558         auto part = parts[0];
0559         QVERIFY(part->header("references"));
0560         QCOMPARE(part->header("references")->asUnicodeString(), QStringLiteral("<a1777ec781546ccc5dcd4918a5e4e03d@info>"));
0561     }
0562 
0563     void testMemoryHoleMultipartMixed()
0564     {
0565         MimeTreeParser::ObjectTreeParser otp;
0566         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-encrypted-memoryhole2.mbox")));
0567         otp.decryptAndVerify();
0568         otp.print();
0569 
0570         auto partList = otp.collectContentParts();
0571         QCOMPARE(partList.size(), 1);
0572         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0573         QVERIFY(bool(part));
0574 
0575         QCOMPARE(part->text(),
0576                  QStringLiteral("\n\n  Fsdflkjdslfj\n\n\nHappy Monday!\n\nBelow you will find a quick overview of the current on-goings. Remember\n"));
0577 
0578         QCOMPARE(part->header("subject")->asUnicodeString(), QStringLiteral("This is the subject"));
0579     }
0580 
0581     void testMIMESignature()
0582     {
0583         MimeTreeParser::ObjectTreeParser otp;
0584         otp.parseObjectTree(readMailFromFile(QLatin1StringView("text+html-maillinglist.mbox")));
0585         otp.decryptAndVerify();
0586         otp.print();
0587 
0588         auto partList = otp.collectContentParts();
0589         for (const auto &part : partList) {
0590             qWarning() << "found part " << part->metaObject()->className();
0591         }
0592         QCOMPARE(partList.size(), 2);
0593         // The actual content
0594         {
0595             auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
0596             QVERIFY(bool(part));
0597         }
0598 
0599         // The signature
0600         {
0601             auto part = partList[1].dynamicCast<MimeTreeParser::TextMessagePart>();
0602             QVERIFY(bool(part));
0603             QVERIFY(part->text().contains(QStringLiteral("bugzilla mailing list")));
0604         }
0605     }
0606 
0607     void testCRLFEncryptedWithSignature()
0608     {
0609         MimeTreeParser::ObjectTreeParser otp;
0610         otp.parseObjectTree(readMailFromFile(QLatin1StringView("crlf-encrypted-with-signature.mbox")));
0611         otp.decryptAndVerify();
0612         otp.print();
0613 
0614         QCOMPARE(otp.plainTextContent(), QStringLiteral("CRLF file\n\n-- \nThis is a signature\nWith two lines\n\nAand another line\n"));
0615     }
0616 
0617     void testCRLFEncryptedWithSignatureMultipart()
0618     {
0619         MimeTreeParser::ObjectTreeParser otp;
0620         otp.parseObjectTree(readMailFromFile(QLatin1StringView("crlf-encrypted-with-signature-multipart.mbox")));
0621         otp.decryptAndVerify();
0622         otp.print();
0623 
0624         // QEXPECT_FAIL("", "because MessagePart::parseInternal uses \n\n to detect encapsulated messages (so 'CRLF file' ends up as header)", Continue);
0625         // QCOMPARE(otp.plainTextContent(), QStringLiteral("CRLF file\n\n-- \nThis is a signature\nWith two lines\n\nAand another line\n"));
0626         // QVERIFY(!otp.htmlContent().contains(QStringLiteral("\r\n")));
0627     }
0628 
0629     void testCRLFOutlook()
0630     {
0631         MimeTreeParser::ObjectTreeParser otp;
0632         otp.parseObjectTree(readMailFromFile(QLatin1StringView("outlook.mbox")));
0633         otp.decryptAndVerify();
0634         otp.print();
0635 
0636         qWarning() << otp.plainTextContent();
0637         QVERIFY(otp.plainTextContent().startsWith(QStringLiteral("Hi Christian,\n\nhabs gerade getestet:\n\n«This is a test")));
0638         QVERIFY(!otp.htmlContent().contains(QLatin1StringView("\r\n")));
0639     }
0640 
0641     void testOpenPGPEncryptedSignedThunderbird()
0642     {
0643         MimeTreeParser::ObjectTreeParser otp;
0644         otp.parseObjectTree(readMailFromFile(QLatin1StringView("openpgp-encrypted-signed-thunderbird.mbox")));
0645         otp.print();
0646         otp.decryptAndVerify();
0647         otp.print();
0648         auto partList = otp.collectContentParts();
0649         QCOMPARE(partList.size(), 1);
0650         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0651         QVERIFY(bool(part));
0652         QCOMPARE(part->text(), QStringLiteral("sdfsdf\n"));
0653         QCOMPARE(part->charset(), QStringLiteral("utf-8").toLocal8Bit());
0654         QCOMPARE(part->encryptions().size(), 1);
0655         QCOMPARE(part->signatures().size(), 1);
0656         QCOMPARE(part->signatures()[0]->partMetaData()->isGoodSignature, true);
0657         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
0658         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
0659         auto contentAttachmentList = otp.collectAttachmentParts();
0660         QCOMPARE(contentAttachmentList.size(), 1);
0661         // QCOMPARE(contentAttachmentList[0]->content().size(), 1);
0662         QCOMPARE(contentAttachmentList[0]->encryptions().size(), 1);
0663         QCOMPARE(contentAttachmentList[0]->signatures().size(), 1);
0664         QCOMPARE(contentAttachmentList[0]->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
0665         QCOMPARE(contentAttachmentList[0]->signatureState(), MimeTreeParser::KMMsgFullySigned);
0666     }
0667 
0668     void testSignedForwardOpenpgpSignedEncrypted()
0669     {
0670         MimeTreeParser::ObjectTreeParser otp;
0671         otp.parseObjectTree(readMailFromFile(QLatin1StringView("signed-forward-openpgp-signed-encrypted.mbox")));
0672         otp.print();
0673         otp.decryptAndVerify();
0674         otp.print();
0675         auto partList = otp.collectContentParts();
0676         QCOMPARE(partList.size(), 2);
0677         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0678         QVERIFY(bool(part));
0679         QCOMPARE(part->text(), QStringLiteral("bla bla bla"));
0680 
0681         part = partList[1].dynamicCast<MimeTreeParser::MessagePart>();
0682         QVERIFY(bool(part));
0683         QCOMPARE(part->text(), QString());
0684         QCOMPARE(part->charset(), QStringLiteral("ISO-8859-1").toLocal8Bit());
0685         QCOMPARE(part->signatures().size(), 1);
0686         QCOMPARE(part->signatures()[0]->partMetaData()->isGoodSignature, true);
0687         auto contentAttachmentList = otp.collectAttachmentParts();
0688         QCOMPARE(contentAttachmentList.size(), 1);
0689     }
0690 
0691     void testSmimeOpaqueSign()
0692     {
0693         MimeTreeParser::ObjectTreeParser otp;
0694         otp.parseObjectTree(readMailFromFile(QLatin1StringView("smime-opaque-sign.mbox")));
0695         otp.print();
0696         otp.decryptAndVerify();
0697         otp.print();
0698         auto partList = otp.collectContentParts();
0699         QCOMPARE(partList.size(), 1);
0700         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0701         QVERIFY(bool(part));
0702         QCOMPARE(part->text(), QStringLiteral("A simple signed only test."));
0703     }
0704 
0705     void testSmimeEncrypted()
0706     {
0707         MimeTreeParser::ObjectTreeParser otp;
0708         otp.parseObjectTree(readMailFromFile(QLatin1StringView("smime-encrypted.mbox")));
0709         otp.print();
0710         otp.decryptAndVerify();
0711         otp.print();
0712         auto partList = otp.collectContentParts();
0713         QCOMPARE(partList.size(), 1);
0714         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0715         QVERIFY(bool(part));
0716         QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog."));
0717     }
0718 
0719     void testSmimeSignedApple()
0720     {
0721         MimeTreeParser::ObjectTreeParser otp;
0722         otp.parseObjectTree(readMailFromFile(QLatin1StringView("smime-signed-apple.mbox")));
0723         otp.print();
0724         otp.decryptAndVerify();
0725         otp.print();
0726         auto partList = otp.collectContentParts();
0727         QCOMPARE(partList.size(), 1);
0728         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0729         QVERIFY(bool(part));
0730         // QCOMPARE(part->text(), QStringLiteral("A simple signed only test."));
0731     }
0732 
0733     void testSmimeEncryptedOctetStream()
0734     {
0735         MimeTreeParser::ObjectTreeParser otp;
0736         otp.parseObjectTree(readMailFromFile(QLatin1StringView("smime-encrypted-octet-stream.mbox")));
0737         otp.print();
0738         otp.decryptAndVerify();
0739         otp.print();
0740         auto partList = otp.collectContentParts();
0741         QCOMPARE(partList.size(), 1);
0742         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0743         QVERIFY(bool(part));
0744         QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog."));
0745     }
0746 
0747     void testSmimeOpaqueSignedEncryptedAttachment()
0748     {
0749         MimeTreeParser::ObjectTreeParser otp;
0750         otp.parseObjectTree(readMailFromFile(QLatin1StringView("smime-opaque-signed-encrypted-attachment.mbox")));
0751         otp.print();
0752         QVERIFY(otp.hasEncryptedParts());
0753         otp.decryptAndVerify();
0754         otp.print();
0755         auto partList = otp.collectContentParts();
0756         QCOMPARE(partList.size(), 1);
0757         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0758         QVERIFY(bool(part));
0759         QCOMPARE(part->text(), QStringLiteral("This is an Opaque S/MIME encrypted and signed message with attachment"));
0760     }
0761 
0762     void testSmimeOpaqueEncSign()
0763     {
0764         MimeTreeParser::ObjectTreeParser otp;
0765         otp.parseObjectTree(readMailFromFile(QLatin1StringView("smime-opaque-enc+sign.mbox")));
0766         otp.print();
0767         QVERIFY(otp.hasEncryptedParts());
0768         QVERIFY(!otp.hasSignedParts());
0769         otp.decryptAndVerify();
0770         QVERIFY(otp.hasSignedParts());
0771         otp.print();
0772         auto partList = otp.collectContentParts();
0773         QCOMPARE(partList.size(), 1);
0774         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0775         QVERIFY(bool(part));
0776         QCOMPARE(part->text(), QStringLiteral("Encrypted and signed mail."));
0777     }
0778 };
0779 
0780 QTEST_GUILESS_MAIN(MimeTreeParserTest)
0781 #include "mimetreeparsertest.moc"