File indexing completed on 2024-12-22 05:07:45

0001 // SPDX-FileCopyrightText: 2016 Sandro Knauß <knauss@kolabsystems.com>
0002 // SPDX-License-Identifier: LGPL-2.0-or-later
0003 
0004 #include <objecttreeparser.h>
0005 
0006 #include <QDebug>
0007 #include <QTest>
0008 #include <QTimeZone>
0009 
0010 QByteArray readMailFromFile(const QString &mailFile)
0011 {
0012     QFile file(QLatin1String(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(QLatin1String("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(QLatin1String("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(QLatin1String("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(QLatin1String("smime-encrypted.mbox")));
0088         otp.print();
0089         otp.decryptParts();
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(QLatin1String("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")));
0107         otp.print();
0108         otp.decryptParts();
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(), QVector<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(), QVector<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(QLatin1String("openpgp-inline-charset-encrypted.mbox")));
0140         otp.print();
0141         otp.decryptParts();
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(QLatin1String("openpgp-inline-encrypted+nonenc.mbox")));
0159         otp.print();
0160         otp.decryptParts();
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(QLatin1String("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")));
0186         otp.print();
0187         otp.decryptParts();
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(QLatin1String("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")));
0222         otp.print();
0223         otp.decryptParts();
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(QLatin1String("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(QLatin1String("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(QLatin1String("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(QLatin1String("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(QLatin1String("cid:")));
0294     }
0295 
0296     void testCidLinkInForwardedInline()
0297     {
0298         MimeTreeParser::ObjectTreeParser otp;
0299         otp.parseObjectTree(readMailFromFile(QLatin1String("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(QLatin1String("cid:")));
0307     }
0308 
0309     void testOpenPGPInlineError()
0310     {
0311         MimeTreeParser::ObjectTreeParser otp;
0312         otp.parseObjectTree(readMailFromFile(QLatin1String("inlinepgpgencrypted-error.mbox")));
0313         otp.print();
0314         otp.decryptParts();
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(QLatin1String("encapsulated-with-attachment.mbox")));
0327         otp.decryptParts();
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(), QLatin1String("Thomas McGuire <dontspamme@gmx.net>"));
0333         QCOMPARE(part->date().toString(), QLatin1String("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(QLatin1String("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(QLatin1String("openpgp-inline-signed.mbox")));
0353         otp.decryptParts();
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()->keyIsTrusted, true);
0367         QCOMPARE(signaturePart->partMetaData()->keyMissing, false);
0368         QCOMPARE(signaturePart->partMetaData()->keyExpired, false);
0369         QCOMPARE(signaturePart->partMetaData()->keyRevoked, false);
0370         QCOMPARE(signaturePart->partMetaData()->sigExpired, false);
0371         QCOMPARE(signaturePart->partMetaData()->crlMissing, false);
0372         QCOMPARE(signaturePart->partMetaData()->crlTooOld, false);
0373         QCOMPARE(signaturePart->partMetaData()->keyId, QByteArray{"8D9860C58F246DE6"});
0374         QCOMPARE(signaturePart->partMetaData()->signer, QLatin1String{"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(QLatin1String("openpgp-encrypted+signed.mbox")));
0382         otp.decryptParts();
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(QLatin1String("openpgp-multipart-embedded.mbox")));
0401         otp.print();
0402         otp.decryptParts();
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(QLatin1String("openpgp-multipart-embedded-signed.mbox")));
0416         otp.decryptParts();
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(signaturePart->partMetaData()->isGoodSignature, false);
0430         QCOMPARE(signaturePart->partMetaData()->keyMissing, true);
0431     }
0432 
0433     void testAppleHtmlWithAttachments()
0434     {
0435         MimeTreeParser::ObjectTreeParser otp;
0436         otp.parseObjectTree(readMailFromFile(QLatin1String("applehtmlwithattachments.mbox")));
0437         otp.decryptParts();
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(QLatin1String("applehtmlwithattachmentsmixed.mbox")));
0464         otp.decryptParts();
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(QLatin1String("invitation.mbox")));
0491         otp.decryptParts();
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(QLatin1String("gmail-invitation.mbox")));
0510         otp.decryptParts();
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         QCOMPARE(part->signatures().size(), 0);
0518         QVERIFY(part->isHtml());
0519         QVERIFY(part->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal));
0520 
0521         auto attachments = otp.collectAttachmentParts();
0522         QCOMPARE(attachments.size(), 1);
0523     }
0524 
0525     void testMemoryHole()
0526     {
0527         MimeTreeParser::ObjectTreeParser otp;
0528         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-memoryhole.mbox")));
0529         otp.decryptParts();
0530         otp.print();
0531 
0532         auto partList = otp.collectContentParts();
0533         QCOMPARE(partList.size(), 1);
0534         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0535         QVERIFY(bool(part));
0536 
0537         QCOMPARE(part->text(), QStringLiteral("very secret mesage\n"));
0538 
0539         QCOMPARE(part->header("subject")->asUnicodeString(), QStringLiteral("hidden subject"));
0540         QCOMPARE(part->header("from")->asUnicodeString(), QStringLiteral("you@example.com"));
0541         QCOMPARE(part->header("to")->asUnicodeString(), QStringLiteral("me@example.com"));
0542         QCOMPARE(part->header("cc")->asUnicodeString(), QStringLiteral("cc@example.com"));
0543         QCOMPARE(part->header("message-id")->asUnicodeString(), QStringLiteral("<myhiddenreference@me>"));
0544         QCOMPARE(part->header("references")->asUnicodeString(), QStringLiteral("<hiddenreference@hidden>"));
0545         QCOMPARE(part->header("in-reply-to")->asUnicodeString(), QStringLiteral("<hiddenreference@hidden>"));
0546         QCOMPARE(static_cast<const KMime::Headers::Date *>(part->header("date"))->dateTime(), QDateTime(QDate(2018, 1, 2), QTime(3, 4, 5), QTimeZone::utc()));
0547     }
0548 
0549     /**
0550      * Required special handling because the list replaces the toplevel part.
0551      */
0552     void testMemoryHoleWithList()
0553     {
0554         MimeTreeParser::ObjectTreeParser otp;
0555         otp.parseObjectTree(readMailFromFile(QLatin1String("cid-links-forwarded-inline.mbox")));
0556         auto part = otp.collectContentParts()[0];
0557         QVERIFY(part->header("references"));
0558         QCOMPARE(part->header("references")->asUnicodeString(), QStringLiteral("<a1777ec781546ccc5dcd4918a5e4e03d@info>"));
0559     }
0560 
0561     void testMemoryHoleMultipartMixed()
0562     {
0563         MimeTreeParser::ObjectTreeParser otp;
0564         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-memoryhole2.mbox")));
0565         otp.decryptParts();
0566         otp.print();
0567 
0568         auto partList = otp.collectContentParts();
0569         QCOMPARE(partList.size(), 1);
0570         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0571         QVERIFY(bool(part));
0572 
0573         QCOMPARE(part->text(),
0574                  QStringLiteral("\n\n  Fsdflkjdslfj\n\n\nHappy Monday!\n\nBelow you will find a quick overview of the current on-goings. Remember\n"));
0575 
0576         QCOMPARE(part->header("subject")->asUnicodeString(), QStringLiteral("This is the subject"));
0577     }
0578 
0579     void testMIMESignature()
0580     {
0581         MimeTreeParser::ObjectTreeParser otp;
0582         otp.parseObjectTree(readMailFromFile(QLatin1String("text+html-maillinglist.mbox")));
0583         otp.decryptParts();
0584         otp.print();
0585 
0586         auto partList = otp.collectContentParts();
0587         for (const auto &part : partList) {
0588             qWarning() << "found part " << part->metaObject()->className();
0589         }
0590         QCOMPARE(partList.size(), 2);
0591         // The actual content
0592         {
0593             auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
0594             QVERIFY(bool(part));
0595         }
0596 
0597         // The signature
0598         {
0599             auto part = partList[1].dynamicCast<MimeTreeParser::TextMessagePart>();
0600             QVERIFY(bool(part));
0601             QVERIFY(part->text().contains(QStringLiteral("bugzilla mailing list")));
0602         }
0603     }
0604 
0605     void testCRLFEncryptedWithSignature()
0606     {
0607         MimeTreeParser::ObjectTreeParser otp;
0608         otp.parseObjectTree(readMailFromFile(QLatin1String("crlf-encrypted-with-signature.mbox")));
0609         otp.decryptParts();
0610         otp.print();
0611 
0612         QCOMPARE(otp.plainTextContent(), QStringLiteral("CRLF file\n\n-- \nThis is a signature\nWith two lines\n\nAand another line\n"));
0613     }
0614 
0615     void testCRLFEncryptedWithSignatureMultipart()
0616     {
0617         MimeTreeParser::ObjectTreeParser otp;
0618         otp.parseObjectTree(readMailFromFile(QLatin1String("crlf-encrypted-with-signature-multipart.mbox")));
0619         otp.decryptParts();
0620         otp.print();
0621 
0622         // QEXPECT_FAIL("", "because MessagePart::parseInternal uses \n\n to detect encapsulated messages (so 'CRLF file' ends up as header)", Continue);
0623         // QCOMPARE(otp.plainTextContent(), QStringLiteral("CRLF file\n\n-- \nThis is a signature\nWith two lines\n\nAand another line\n"));
0624         // QVERIFY(!otp.htmlContent().contains(QStringLiteral("\r\n")));
0625     }
0626 
0627     void testCRLFOutlook()
0628     {
0629         MimeTreeParser::ObjectTreeParser otp;
0630         otp.parseObjectTree(readMailFromFile(QLatin1String("outlook.mbox")));
0631         otp.decryptParts();
0632         otp.print();
0633 
0634         qWarning() << otp.plainTextContent();
0635         QVERIFY(otp.plainTextContent().startsWith(QStringLiteral("Hi Christian,\n\nhabs gerade getestet:\n\n«This is a test")));
0636         QVERIFY(!otp.htmlContent().contains(QLatin1String("\r\n")));
0637     }
0638 
0639     void testOpenPGPEncryptedSignedThunderbird()
0640     {
0641         MimeTreeParser::ObjectTreeParser otp;
0642         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-signed-thunderbird.mbox")));
0643         otp.print();
0644         otp.decryptParts();
0645         otp.print();
0646         auto partList = otp.collectContentParts();
0647         QCOMPARE(partList.size(), 1);
0648         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0649         QVERIFY(bool(part));
0650         QCOMPARE(part->text(), QStringLiteral("sdfsdf\n"));
0651         QCOMPARE(part->charset(), QStringLiteral("utf-8").toLocal8Bit());
0652         QCOMPARE(part->encryptions().size(), 1);
0653         QCOMPARE(part->signatures().size(), 1);
0654         QCOMPARE(part->signatures()[0]->partMetaData()->isGoodSignature, true);
0655         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
0656         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
0657         auto contentAttachmentList = otp.collectAttachmentParts();
0658         QCOMPARE(contentAttachmentList.size(), 1);
0659         // QCOMPARE(contentAttachmentList[0]->content().size(), 1);
0660         QCOMPARE(contentAttachmentList[0]->encryptions().size(), 1);
0661         QCOMPARE(contentAttachmentList[0]->signatures().size(), 1);
0662         QCOMPARE(contentAttachmentList[0]->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
0663         QCOMPARE(contentAttachmentList[0]->signatureState(), MimeTreeParser::KMMsgFullySigned);
0664     }
0665 
0666     void testSignedForwardOpenpgpSignedEncrypted()
0667     {
0668         MimeTreeParser::ObjectTreeParser otp;
0669         otp.parseObjectTree(readMailFromFile(QLatin1String("signed-forward-openpgp-signed-encrypted.mbox")));
0670         otp.print();
0671         otp.decryptParts();
0672         otp.print();
0673         auto partList = otp.collectContentParts();
0674         QCOMPARE(partList.size(), 2);
0675         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0676         QVERIFY(bool(part));
0677         QCOMPARE(part->text(), QStringLiteral("bla bla bla"));
0678 
0679         part = partList[1].dynamicCast<MimeTreeParser::MessagePart>();
0680         QVERIFY(bool(part));
0681         QCOMPARE(part->text(), QStringLiteral(""));
0682         QCOMPARE(part->charset(), QStringLiteral("ISO-8859-1").toLocal8Bit());
0683         QCOMPARE(part->signatures().size(), 1);
0684         QCOMPARE(part->signatures()[0]->partMetaData()->isGoodSignature, true);
0685         auto contentAttachmentList = otp.collectAttachmentParts();
0686         QCOMPARE(contentAttachmentList.size(), 1);
0687     }
0688 
0689     void testSmimeOpaqueSign()
0690     {
0691         MimeTreeParser::ObjectTreeParser otp;
0692         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-opaque-sign.mbox")));
0693         otp.print();
0694         otp.decryptParts();
0695         otp.print();
0696         auto partList = otp.collectContentParts();
0697         QCOMPARE(partList.size(), 1);
0698         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0699         QVERIFY(bool(part));
0700         QCOMPARE(part->text(), QStringLiteral("A simple signed only test."));
0701     }
0702 
0703     void testSmimeEncrypted()
0704     {
0705         MimeTreeParser::ObjectTreeParser otp;
0706         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-encrypted.mbox")));
0707         otp.print();
0708         otp.decryptParts();
0709         otp.print();
0710         auto partList = otp.collectContentParts();
0711         QCOMPARE(partList.size(), 1);
0712         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0713         QVERIFY(bool(part));
0714         QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog."));
0715     }
0716 
0717     void testSmimeSignedApple()
0718     {
0719         MimeTreeParser::ObjectTreeParser otp;
0720         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-signed-apple.mbox")));
0721         otp.print();
0722         otp.decryptParts();
0723         otp.print();
0724         auto partList = otp.collectContentParts();
0725         QCOMPARE(partList.size(), 1);
0726         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0727         QVERIFY(bool(part));
0728         // QCOMPARE(part->text(), QStringLiteral("A simple signed only test."));
0729     }
0730 
0731     void testSmimeEncryptedOctetStream()
0732     {
0733         MimeTreeParser::ObjectTreeParser otp;
0734         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-encrypted-octet-stream.mbox")));
0735         otp.print();
0736         otp.decryptParts();
0737         otp.print();
0738         auto partList = otp.collectContentParts();
0739         QCOMPARE(partList.size(), 1);
0740         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0741         QVERIFY(bool(part));
0742         QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog."));
0743     }
0744 
0745     void testSmimeOpaqueSignedEncryptedAttachment()
0746     {
0747         MimeTreeParser::ObjectTreeParser otp;
0748         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-opaque-signed-encrypted-attachment.mbox")));
0749         otp.print();
0750         otp.decryptParts();
0751         otp.print();
0752         auto partList = otp.collectContentParts();
0753         QCOMPARE(partList.size(), 1);
0754         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0755         QVERIFY(bool(part));
0756         QCOMPARE(part->text(), QStringLiteral("This is an Opaque S/MIME encrypted and signed message with attachment"));
0757     }
0758 
0759     void testSmimeOpaqueEncSign()
0760     {
0761         MimeTreeParser::ObjectTreeParser otp;
0762         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-opaque-enc+sign.mbox")));
0763         otp.print();
0764         otp.decryptParts();
0765         otp.print();
0766         auto partList = otp.collectContentParts();
0767         QCOMPARE(partList.size(), 1);
0768         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
0769         QVERIFY(bool(part));
0770         QCOMPARE(part->text(), QStringLiteral("Encrypted and signed mail."));
0771     }
0772 };
0773 
0774 QTEST_GUILESS_MAIN(MimeTreeParserTest)
0775 #include "mimetreeparsertest.moc"