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"