File indexing completed on 2024-06-23 05:20:28

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