File indexing completed on 2024-04-28 09:12:07

0001 /*
0002     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "messagetest.h"
0008 #include <QTest>
0009 #include <QDebug>
0010 #include <QFile>
0011 #include <kmime_codecs.cpp>
0012 
0013 using namespace KMime;
0014 
0015 QTEST_MAIN(MessageTest)
0016 
0017 void MessageTest::testMainBodyPart()
0018 {
0019     auto msg = new Message();
0020     auto msg2 = new Message();
0021     auto text = new Content();
0022     text->contentType()->setMimeType("text/plain");
0023     auto html = new Content();
0024     html->contentType()->setMimeType("text/html");
0025 
0026     // empty message
0027     QCOMPARE(msg->mainBodyPart(), msg);
0028     QCOMPARE(msg->mainBodyPart("text/plain"), (Content *)nullptr);
0029 
0030     // non-multipart
0031     msg->contentType()->setMimeType("text/html");
0032 
0033     QCOMPARE(msg->mainBodyPart(), msg);
0034     QCOMPARE(msg->mainBodyPart("text/plain"), (Content *)nullptr);
0035     QCOMPARE(msg->mainBodyPart("text/html"), msg);
0036 
0037     // multipart/mixed
0038     msg2->contentType()->setMimeType("multipart/mixed");
0039     msg2->appendContent(text);
0040     msg2->appendContent(html);
0041 
0042     QCOMPARE(msg2->mainBodyPart(), text);
0043     QCOMPARE(msg2->mainBodyPart("text/plain"), text);
0044     QCOMPARE(msg2->mainBodyPart("text/html"), (Content *)nullptr);
0045 
0046     // Careful with removing content here.  If we remove one of the two contents
0047     // (by adding it to another message), the multipart will automatically be
0048     // converted to a single-part, deleting the other content!
0049     msg2->clearContents(false);
0050 
0051     // multipart/alternative
0052     msg->contentType()->setMimeType("multipart/alternative");
0053     msg->appendContent(html);
0054     msg->appendContent(text);
0055 
0056     QCOMPARE(msg->mainBodyPart(), html);
0057     QCOMPARE(msg->mainBodyPart("text/plain"), text);
0058     QCOMPARE(msg->mainBodyPart("text/html"), html);
0059 
0060     // multipart/alternative inside multipart/mixed
0061     auto msg3 = new Message();
0062     msg3->contentType()->setMimeType("multipart/mixed");
0063     msg3->appendContent(msg);
0064     auto attach = new Content();
0065     attach->contentType()->setMimeType("text/plain");
0066 
0067     QCOMPARE(msg3->mainBodyPart(), html);
0068     QCOMPARE(msg3->mainBodyPart("text/plain"), text);
0069     QCOMPARE(msg3->mainBodyPart("text/html"), html);
0070 
0071     delete msg2;
0072     delete msg3;
0073     delete attach;
0074 }
0075 
0076 void MessageTest::testBrunosMultiAssembleBug()
0077 {
0078     QByteArray data =
0079         "From: Sender <sender@test.org>\n"
0080         "Subject: Sample message\n"
0081         "To: Receiver <receiver@test.org>\n"
0082         "Date: Sat, 04 Aug 2007 12:44:00 +0200\n"
0083         "MIME-Version: 1.0\n"
0084         "Content-Type: text/plain\n"
0085         "X-Foo: bla\n"
0086         "X-Bla: foo\n"
0087         "\n"
0088         "body";
0089 
0090     auto msg = new Message;
0091     msg->setContent(data);
0092     msg->parse();
0093     msg->assemble();
0094     QCOMPARE(msg->encodedContent(), data);
0095 
0096     msg->inReplyTo();
0097     msg->assemble();
0098     QCOMPARE(msg->encodedContent(), data);
0099 
0100     delete msg;
0101 }
0102 
0103 void MessageTest::testWillsAndTillsCrash()
0104 {
0105     QByteArray deadlyMail = "From: censored@yahoogroups.com\n"
0106                             "To: censored@yahoogroups.com\n"
0107                             "Sender: censored@yahoogroups.com\n"
0108                             "MIME-Version: 1.0\n"
0109                             "Date: 29 Jan 2006 23:58:21 -0000\n"
0110                             "Subject: [censored] Birthday Reminder\n"
0111                             "Reply-To: censored@yahoogroups.com\n"
0112                             "Content-Type: multipart/alternative;\n boundary=\"YCalReminder=cNM4SNTGA4Cg1MVLaPpqNF1138579098\"\n"
0113                             "X-Length: 9594\n"
0114                             "X-UID: 6161\n"
0115                             "Status: RO\n"
0116                             "X-Status: OC\n"
0117                             "X-KMail-EncryptionState:\n"
0118                             "X-KMail-SignatureState:\n"
0119                             "X-KMail-MDN-Sent:\n\n";
0120 
0121     auto msg = new KMime::Message;
0122     msg->setContent(deadlyMail);
0123     msg->parse();
0124     QVERIFY(!msg->date()->isEmpty());
0125     QCOMPARE(msg->subject()->as7BitString(false), QByteArray("[censored] Birthday Reminder"));
0126     QCOMPARE(msg->from()->mailboxes().count(), 1);
0127     QCOMPARE(msg->sender()->mailboxes().count(), 1);
0128     QCOMPARE(msg->replyTo()->mailboxes().count(), 1);
0129     QCOMPARE(msg->to()->mailboxes().count(), 1);
0130     QCOMPARE(msg->cc()->mailboxes().count(), 0);
0131     QCOMPARE(msg->bcc()->mailboxes().count(), 0);
0132     QCOMPARE(msg->inReplyTo()->identifiers().count(), 0);
0133     QCOMPARE(msg->messageID()->identifiers().count(), 0);
0134     delete msg;
0135 }
0136 
0137 void MessageTest::testDavidsParseCrash()
0138 {
0139     KMime::Message::Ptr mail = readAndParseMail(QStringLiteral("dfaure-crash.mbox"));
0140     QCOMPARE(mail->to()->asUnicodeString().toLatin1().data(), "frank@domain.com");
0141 }
0142 
0143 void MessageTest::testHeaderFieldWithoutSpace()
0144 {
0145     // Headers without a space, like the CC header here, are allowed according to
0146     // the examples in RFC2822, Appendix A5
0147     const QString mail = QStringLiteral("From:\n"
0148                                  "To: heinz@test.de\n"
0149                                  "Cc:moritz@test.de\n"
0150                                  "Subject: Test\n"
0151                                  "X-Mailer:");
0152     KMime::Message msg;
0153     msg.setContent(mail.toLatin1());
0154     msg.parse();
0155 
0156     QCOMPARE(msg.to()->asUnicodeString(), QLatin1String("heinz@test.de"));
0157     QCOMPARE(msg.from()->asUnicodeString(), QString());
0158     QCOMPARE(msg.cc()->asUnicodeString(), QLatin1String("moritz@test.de"));
0159     QCOMPARE(msg.subject()->asUnicodeString(), QLatin1String("Test"));
0160     QVERIFY(msg.hasHeader("X-Mailer"));
0161     QVERIFY(msg.headerByType("X-Mailer")->asUnicodeString().isEmpty());
0162 }
0163 
0164 void MessageTest::testWronglyFoldedHeaders()
0165 {
0166     // The first subject line here doesn't contain anything. This is invalid,
0167     // however there are some mailers out there that produce those messages.
0168     const QString mail = QStringLiteral("Subject:\n"
0169                                  " Hello\n"
0170                                  " World\n"
0171                                  "To: \n"
0172                                  " test@test.de\n\n"
0173                                  "<Body>");
0174     KMime::Message msg;
0175     msg.setContent(mail.toLatin1());
0176     msg.parse();
0177 
0178     QCOMPARE(msg.subject()->asUnicodeString(), QLatin1String("Hello World"));
0179     QCOMPARE(msg.body().data(), "<Body>");
0180     QCOMPARE(msg.to()->asUnicodeString(), QLatin1String("test@test.de"));
0181 }
0182 
0183 void MessageTest::missingHeadersTest()
0184 {
0185     // Test that the message body is OK even though some headers are missing
0186     KMime::Message msg;
0187     const QString body = QStringLiteral("Hi Donald, look at those nice pictures I found!\n");
0188     const QString content = QLatin1String("From: georgebush@whitehouse.org\n"
0189                                     "To: donaldrumsfeld@whitehouse.org\n"
0190                                     "Subject: Cute Kittens\n"
0191                                     "\n") + body;
0192     msg.setContent(content.toLatin1());
0193     msg.parse();
0194     msg.assemble();
0195 
0196     QCOMPARE(body, QString::fromLatin1(msg.body()));
0197 
0198     // Now create a new message, based on the content of the first one.
0199     // The body of the new message should still be the same.
0200     // (there was a bug that caused missing mandatory headers to be
0201     //  added as a empty newline, which caused parts of the header to
0202     //  leak into the body)
0203     KMime::Message msg2;
0204     msg2.setContent(msg.encodedContent());
0205     msg2.parse();
0206     msg2.assemble();
0207 
0208     QCOMPARE(body, QString::fromLatin1(msg2.body()));
0209 }
0210 
0211 void MessageTest::testBug219749()
0212 {
0213     // Test that the message body is OK even though some headers are missing
0214     KMime::Message msg;
0215     const QString content = QStringLiteral(
0216                                 "Content-Type: MULTIPART/MIXED;\n"
0217                                 " BOUNDARY=\"0-1804289383-1260384639=:52580\"\n"
0218                                 "\n"
0219                                 "--0-1804289383-1260384639=:52580\n"
0220                                 "Content-Type: TEXT/plain; CHARSET=UTF-8\n"
0221                                 "\n"
0222                                 "--0-1804289383-1260384639=:52580\n"
0223                                 "Content-Type: APPLICATION/octet-stream\n"
0224                                 "Content-Transfer-Encoding: BASE64\n"
0225                                 "Content-ID: <jaselka1.docx4AECA1F9@9230725.3CDBB752>\n"
0226                                 "Content-Disposition: ATTACHMENT; FILENAME=\"jaselka 1.docx\"\n"
0227                                 "\n"
0228                                 "UEsDBBQABgAIAAAAIQDd/JU3ZgEAACAFAAATAAgCW0NvbnRlbnRfVHlwZXNd\n"
0229                                 "SUwAAAAA\n"
0230                                 "\n"
0231                                 "--0-1804289383-1260384639=:52580--\n");
0232 
0233     msg.setContent(content.toLatin1());
0234     msg.parse();
0235 
0236     QCOMPARE(msg.contents().size(), 2);
0237     KMime::Content *attachment = msg.contents()[1];
0238     QCOMPARE(attachment->contentType(false)->mediaType().data(), "application");
0239     QCOMPARE(attachment->contentType(false)->subType().data(), "octet-stream");
0240     QCOMPARE(attachment->contentID()->identifier().data(), "jaselka1.docx4AECA1F9@9230725.3CDBB752");
0241     QCOMPARE(attachment->contentID()->as7BitString(false).data(), "<jaselka1.docx4AECA1F9@9230725.3CDBB752>");
0242     Headers::ContentDisposition *cd = attachment->contentDisposition(false);
0243     QVERIFY(cd);
0244     QCOMPARE(cd->filename(), QLatin1String("jaselka 1.docx"));
0245 }
0246 
0247 void MessageTest::testBidiSpoofing()
0248 {
0249     const QString RLO(QChar(0x202E));
0250     //const QString PDF( QChar( 0x202C ) );
0251 
0252     const QByteArray senderAndRLO =
0253         encodeRFC2047String(QString(QLatin1String("Sender") + RLO + QLatin1String(" <sender@test.org>")), "utf-8");
0254 
0255     // The display name of the "From" has an RLO, make sure the KMime parser balances it
0256     QByteArray data =
0257         "From: " + senderAndRLO + "\n"
0258         "\n"
0259         "Body";
0260 
0261     KMime::Message msg;
0262     msg.setContent(data);
0263     msg.parse();
0264 
0265     // Test adjusted for taking into account that KMIME now removes bidi control chars
0266     // instead of adding PDF chars, because of broken KHTML.
0267     //const QString expectedDisplayName = "\"Sender" + RLO + PDF + "\"";
0268     const QString expectedDisplayName = QStringLiteral("Sender");
0269     const QString expectedMailbox = expectedDisplayName + QLatin1String(" <sender@test.org>");
0270     QCOMPARE(msg.from()->addresses().count(), 1);
0271     QCOMPARE(msg.from()->asUnicodeString(), expectedMailbox);
0272     QCOMPARE(msg.from()->displayNames().first(), expectedDisplayName);
0273     QCOMPARE(msg.from()->mailboxes().first().name(), expectedDisplayName);
0274     QCOMPARE(msg.from()->mailboxes().first().address().data(), "sender@test.org");
0275 }
0276 
0277 // Test to see if header fields of mails with an UTF-16 body are properly read
0278 // and written.
0279 // See also https://issues.kolab.org/issue3707
0280 void MessageTest::testUtf16()
0281 {
0282     QByteArray data =
0283         "From: foo@bar.com\n"
0284         "Subject: UTF-16 Test\n"
0285         "MIME-Version: 1.0\n"
0286         "Content-Type: Text/Plain;\n"
0287         "  charset=\"utf-16\"\n"
0288         "Content-Transfer-Encoding: base64\n"
0289         "\n"
0290         "//5UAGgAaQBzACAAaQBzACAAVQBUAEYALQAxADYAIABUAGUAeAB0AC4ACgAKAAo";
0291 
0292     KMime::Message msg;
0293     msg.setContent(data);
0294     msg.parse();
0295 
0296     QCOMPARE(msg.from()->asUnicodeString(), QLatin1String("foo@bar.com"));
0297     QCOMPARE(msg.subject()->asUnicodeString(), QLatin1String("UTF-16 Test"));
0298     QCOMPARE(msg.decodedText(false, true), QLatin1String("This is UTF-16 Text."));
0299 
0300     // Add a new To header, for testings
0301     auto to = new KMime::Headers::To;
0302     KMime::Types::Mailbox address;
0303     address.setAddress("test@test.de");
0304     address.setName(QStringLiteral("Fränz Töster"));
0305     to->addAddress(address);
0306     to->setRFC2047Charset("ISO-8859-1"); // default changed to UTF-8 in KF6, which is fine, but breaks the test
0307     msg.appendHeader(to);
0308     msg.assemble();
0309 
0310     QByteArray newData =
0311         "From: foo@bar.com\n"
0312         "Subject: UTF-16 Test\n"
0313         "MIME-Version: 1.0\n"
0314         "Content-Type: text/plain; charset=\"utf-16\"\n"
0315         "Content-Transfer-Encoding: base64\n"
0316         "To: =?ISO-8859-1?Q?Fr=E4nz_T=F6ster?= <test@test.de>\n"
0317         "\n"
0318         "//5UAGgAaQBzACAAaQBzACAAVQBUAEYALQAxADYAIABUAGUAeAB0AC4ACgAKAAo=\n"
0319         "\n";
0320 
0321     QCOMPARE(msg.encodedContent().data(), newData.data());
0322 }
0323 
0324 void MessageTest::testDecodedText()
0325 {
0326     QByteArray data =
0327         "Subject: Test\n"
0328         "\n"
0329         "Testing Whitespace   \n  \n \n\n\n";
0330 
0331     KMime::Message msg;
0332     msg.setContent(data);
0333     msg.parse();
0334 
0335     QCOMPARE(msg.decodedText(true, false), QLatin1String("Testing Whitespace"));
0336     QCOMPARE(msg.decodedText(true, true), QLatin1String("Testing Whitespace"));
0337     QCOMPARE(msg.decodedText(false, true), QLatin1String("Testing Whitespace   \n  \n "));
0338 
0339     QByteArray data2 =
0340         "Subject: Test\n"
0341         "\n"
0342         "Testing Whitespace   \n  \n \n\n\n ";
0343 
0344     KMime::Message msg2;
0345     msg2.setContent(data2);
0346     msg2.parse();
0347 
0348     QCOMPARE(msg2.decodedText(true, false), QLatin1String("Testing Whitespace"));
0349     QCOMPARE(msg2.decodedText(true, true), QLatin1String("Testing Whitespace"));
0350     QCOMPARE(msg2.decodedText(false, true), QLatin1String("Testing Whitespace   \n  \n \n\n\n "));
0351 }
0352 
0353 void MessageTest::testInlineImages()
0354 {
0355     const QByteArray data =
0356         "From: <kde@kde.org>\n"
0357         "To: kde@kde.org\n"
0358         "Subject: Inline Image (unsigned)\n"
0359         "Date: Wed, 23 Dec 2009 14:00:59 +0100\n"
0360         "MIME-Version: 1.0\n"
0361         "Content-Type: multipart/related;\n"
0362         "  boundary=\"Boundary-02=_LShMLJyjC7zqmVP\"\n"
0363         "Content-Transfer-Encoding: 7bit\n"
0364         "\n"
0365         "\n"
0366         "--Boundary-02=_LShMLJyjC7zqmVP\n"
0367         "Content-Type: multipart/alternative;\n"
0368         "  boundary=\"Boundary-01=_LShMLzAUPqE38S8\"\n"
0369         "Content-Transfer-Encoding: 7bit\n"
0370         "Content-Disposition: inline\n"
0371         "\n"
0372         "--Boundary-01=_LShMLzAUPqE38S8\n"
0373         "Content-Type: text/plain;\n"
0374         "  charset=\"us-ascii\"\n"
0375         "Content-Transfer-Encoding: 7bit\n"
0376         "\n"
0377         "First line\n"
0378         "\n"
0379         "\n"
0380         "Image above\n"
0381         "\n"
0382         "Last line\n"
0383         "\n"
0384         "--Boundary-01=_LShMLzAUPqE38S8\n"
0385         "Content-Type: text/html;\n"
0386         "  charset=\"us-ascii\"\n"
0387         "Content-Transfer-Encoding: 7bit\n"
0388         "\n"
0389         "Line 1\n"
0390         "--Boundary-01=_LShMLzAUPqE38S8--\n"
0391         "\n"
0392         "--Boundary-02=_LShMLJyjC7zqmVP\n"
0393         "Content-Type: image/png;\n"
0394         "  name=\"inlineimage.png\"\n"
0395         "Content-Transfer-Encoding: base64\n"
0396         "Content-Id: <740439759>\n"
0397         "\n"
0398         "jxrG/ha/VB+rODav6/d5i1US6Za/YEMvtm2SgJC/CXVFiD3UFSH2UFeE2ENdEWIPdUWIPdQVIfZQ\n"
0399         "V4TYQ10RYg91RYg91BUh9lBXhNhDXRFiD3VFiD3UFSH2UFeE2ENdEWIPdUWIPdQVIfZQV4TYQ10R\n"
0400         "Yg91RYg91BUh9lBX5E+Tz6Vty1HSx+NR++UuCOqKEHv+Ax0Y5U59+AHBAAAAAElFTkSuQmCC\n"
0401         "\n"
0402         "--Boundary-02=_LShMLJyjC7zqmVP--";
0403 
0404     KMime::Message msg;
0405     msg.setContent(data);
0406     msg.parse();
0407 
0408     QCOMPARE(msg.contents().size(), 2);
0409     QCOMPARE(msg.contents()[0]->contentType()->isMultipart(), true);
0410     QCOMPARE(msg.contents()[0]->contentType()->subType().data(), "alternative");
0411 
0412     QCOMPARE(msg.contents()[1]->contentType()->isImage(), true);
0413     QCOMPARE(msg.contents()[1]->contentType()->name(), QLatin1String("inlineimage.png"));
0414     QCOMPARE(msg.contents()[1]->contentID()->identifier().data(), "740439759");
0415     QCOMPARE(msg.contents()[1]->contentID()->as7BitString(false).data(), "<740439759>");
0416 }
0417 
0418 void MessageTest::testIssue3908()
0419 {
0420     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("issue3908.mbox"));
0421     QCOMPARE(msg->contents().size(), 2);
0422     KMime::Content *attachment = msg->contents().at(1);
0423     QVERIFY(attachment);
0424     QVERIFY(attachment->contentDescription(false));
0425     QCOMPARE(attachment->contentDescription()->asUnicodeString(), QString::fromUtf8(
0426                  "Kontact oder auch KDE-PIM ist der Groupware-Client aus der KDE Software Compilation 4.Eine der Besonderheiten von Kontact "
0427                  "gegenüber anderen Groupware-Clients ist, dass die Teil-Programme auch weiterhin unabhängig von Kontact gestartet werden "
0428                  "können. So spielt es zum Beispiel keine Rolle für das Arbeiten mit KMail, ob es mal allein oder mal im Rahmen von Kontact "
0429                  "gestartet wird: Die Mails und die persönlichen Einstellungen bleiben stets erhalten.Auch sieht Kontact eine modulare "
0430                  "Anbindung der Programme vor, wodurch sich auch in Zukunft weitere Module entwickeln und anfügen lassen, ohne Kontact "
0431                  "dafür zu ändern. Dies bietet die Möglichkeit, auch privat entwickelte Module einzubinden und so die Groupware grundlegend "
0432                  "eigenen Bedürfnissen anzupassen."));
0433 }
0434 
0435 void MessageTest::testIssue3914()
0436 {
0437     // This loads a mail which has a content-disposition of which the filename parameter is empty.
0438     // Check that the parser doesn't choke on this.
0439     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("broken-content-disposition.mbox"));
0440 
0441     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: Fwd: test broken mail");
0442     QCOMPARE(msg->contents().size(), 2);
0443     KMime::Content *attachedMail =  msg->contents().at(1);
0444     QCOMPARE(attachedMail->contentType()->mimeType().data(), "message/rfc822");
0445     QVERIFY(attachedMail->contentDisposition(false));
0446     QVERIFY(attachedMail->contentDisposition()->hasParameter(QLatin1String("filename")));
0447     QVERIFY(attachedMail->contentDisposition()->parameter(QLatin1String("filename")).isEmpty());
0448 }
0449 
0450 void MessageTest::testBug223509()
0451 {
0452     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("encoding-crash.mbox"));
0453 
0454     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: Blub");
0455     QCOMPARE(msg->contents().size(), 0);
0456     QCOMPARE(msg->contentTransferEncoding()->encoding(), KMime::Headers::CEbinary);
0457     QCOMPARE(msg->decodedText().toLatin1().data(), "Bla Bla Bla");
0458     QCOMPARE(msg->encodedBody().data(), "Bla Bla Bla\n");
0459 
0460     // encodedContent() was crashing in this bug because of an invalid assert
0461     QVERIFY(!msg->encodedContent().isEmpty());
0462 
0463     // Make sure that the encodedContent() is sane, by parsing it again.
0464     KMime::Message msg2;
0465     msg2.setContent(msg->encodedContent());
0466     msg2.parse();
0467     QCOMPARE(msg2.subject()->as7BitString().data(), "Subject: Blub");
0468     QCOMPARE(msg2.contents().size(), 0);
0469     QCOMPARE(msg2.contentTransferEncoding()->encoding(), KMime::Headers::CEbinary);
0470 
0471     QCOMPARE(msg2.decodedText().toLatin1().data(), "Bla Bla Bla");
0472     QCOMPARE(msg2.decodedText(true, true /* remove newlines at end */).toLatin1().data(),
0473              "Bla Bla Bla");
0474 }
0475 
0476 void MessageTest::testEncapsulatedMessages()
0477 {
0478     //
0479     // First, test some basic properties to check that the parsing was correct
0480     //
0481     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("simple-encapsulated.mbox"));
0482     QCOMPARE(msg->contentType()->mimeType().data(), "multipart/mixed");
0483     QCOMPARE(msg->contents().size(), 2);
0484     QVERIFY(msg->isTopLevel());
0485 
0486     KMime::Content *const textContent = msg->contents().at(0);
0487     QCOMPARE(textContent->contentType()->mimeType().data(), "text/plain");
0488     QVERIFY(textContent->contents().isEmpty());
0489     QVERIFY(!textContent->bodyIsMessage());
0490     QVERIFY(!textContent->bodyAsMessage());
0491     QVERIFY(!textContent->isTopLevel());
0492     QCOMPARE(textContent->decodedText(true, true),
0493              QLatin1String("Hi Hans!\nLook at this interesting mail I forwarded to you!"));
0494     QCOMPARE(textContent->index().toString().toLatin1().data(), "1");
0495 
0496     KMime::Content *messageContent = msg->contents().at(1);
0497     QCOMPARE(messageContent->contentType()->mimeType().data(), "message/rfc822");
0498     QVERIFY(messageContent->body().isEmpty());
0499     QCOMPARE(messageContent->contents().count(), 1);
0500     QVERIFY(messageContent->bodyIsMessage());
0501     QVERIFY(messageContent->bodyAsMessage().data());
0502     QVERIFY(!messageContent->isTopLevel());
0503     QCOMPARE(messageContent->index().toString().toLatin1().data(), "2");
0504 
0505     KMime::Message::Ptr encapsulated = messageContent->bodyAsMessage();
0506     QCOMPARE(encapsulated->contents().size(), 0);
0507     QCOMPARE(encapsulated->contentType()->mimeType().data(), "text/plain");
0508     QVERIFY(!encapsulated->bodyIsMessage());
0509     QVERIFY(!encapsulated->bodyAsMessage());
0510     QCOMPARE(encapsulated->subject()->as7BitString(false).data(), "Foo");
0511     QCOMPARE(encapsulated->decodedText(false, false),
0512              QLatin1String("This is the encapsulated message body."));
0513     QCOMPARE(encapsulated.data(), messageContent->bodyAsMessage().data());
0514     QCOMPARE(encapsulated.data(), messageContent->contents().constFirst());
0515     QCOMPARE(encapsulated->parent(), messageContent);
0516     QVERIFY(!encapsulated->isTopLevel());
0517     QCOMPARE(encapsulated->topLevel(), msg.data());
0518     QCOMPARE(encapsulated->index().toString().toLatin1().data(), "2.1");
0519 
0520     // Now test some misc functions
0521     QCOMPARE(msg->storageSize(), msg->head().size() + textContent->storageSize() +
0522              messageContent->storageSize());
0523     QCOMPARE(messageContent->storageSize(), messageContent->head().size() +
0524              encapsulated->storageSize());
0525 
0526     // Now change some properties on the encapsulated message
0527     encapsulated->subject()->fromUnicodeString(QStringLiteral("New subject"), "us-ascii");
0528     encapsulated->fromUnicodeString(QStringLiteral("New body string."));
0529 
0530     // Since we didn't assemble the encapsulated message yet, it should still have the old headers
0531     QVERIFY(encapsulated->encodedContent().contains("Foo"));
0532     QVERIFY(!encapsulated->encodedContent().contains("New subject"));
0533 
0534     // Now assemble the container message
0535     msg->assemble();
0536 
0537     // Assembling the container message should have assembled the encapsulated message as well.
0538     QVERIFY(!encapsulated->encodedContent().contains("Foo"));
0539     QVERIFY(encapsulated->encodedContent().contains("New subject"));
0540     QCOMPARE(encapsulated->body().data(), "New body string.");
0541     QVERIFY(msg->encodedContent().contains(encapsulated->body()));
0542     QCOMPARE(msg->contentType()->mimeType().data(), "multipart/mixed");
0543     QCOMPARE(msg->contents().size(), 2);
0544     messageContent = msg->contents().at(1);
0545     QCOMPARE(messageContent->contentType()->mimeType().data(), "message/rfc822");
0546     QVERIFY(encapsulated.data() == messageContent->bodyAsMessage().data());
0547 
0548     // Setting a new body and then parsing it should discard the encapsulated message
0549     messageContent->contentType()->setMimeType("text/plain");
0550     messageContent->assemble();
0551     messageContent->setBody("Some new body");
0552     messageContent->parse();
0553     QVERIFY(!messageContent->bodyIsMessage());
0554     QVERIFY(!messageContent->bodyAsMessage());
0555     QCOMPARE(messageContent->contents().size(), 0);
0556 }
0557 
0558 void MessageTest::testOutlookAttachmentNaming()
0559 {
0560     // Try and decode
0561     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("outlook-attachment.mbox"));
0562     QVERIFY(msg->attachments().count() == 1);
0563 
0564     KMime::Content *attachment = msg->contents()[1];
0565     QCOMPARE(attachment->contentType(false)->mediaType().data(), "text");
0566     QCOMPARE(attachment->contentType(false)->subType().data(), "x-patch");
0567 
0568     Headers::ContentDisposition *cd = attachment->contentDisposition(false);
0569     QVERIFY(cd);
0570     QCOMPARE(cd->filename(), QString::fromUtf8("å.diff"));
0571 
0572     // Try and encode
0573     attachment->clear();// = new Content();
0574     attachment->contentDisposition()->setDisposition(Headers::CDattachment);
0575     attachment->contentDisposition()->setFilename(QStringLiteral("å.diff"));
0576     attachment->contentDisposition()->setRFC2047Charset("ISO-8859-1"); // default changed to UTF-8 in KF6, which is fine, but breaks the test
0577     attachment->assemble();
0578     qDebug() << "got:" << attachment->contentDisposition()->as7BitString(false);
0579     QCOMPARE(attachment->contentDisposition()->as7BitString(false), QByteArray("attachment; filename*=ISO-8859-1''%E5%2Ediff"));
0580 }
0581 
0582 void MessageTest::testEncryptedMails()
0583 {
0584     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("x-pkcs7.mbox"));
0585     QVERIFY(msg->contents().size() == 0);
0586     QVERIFY(msg->attachments().count() == 0);
0587     QVERIFY(KMime::isEncrypted(msg.data()) == true);
0588     QVERIFY(KMime::isInvitation(msg.data()) == false);
0589     QVERIFY(KMime::isSigned(msg.data()) == false);
0590 }
0591 
0592 void MessageTest::testReturnSameMail()
0593 {
0594     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("dontchangemail.mbox"));
0595     QFile file(QLatin1String(TEST_DATA_DIR) + QLatin1String("/mails/dontchangemail.mbox"));
0596     QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
0597     QByteArray fileContent = file.readAll();
0598     QCOMPARE(msg->encodedContent(), fileContent);
0599     QCOMPARE(msg->decodedText(), QLatin1String(""));
0600     KMime::Message msg2;
0601     msg2.setContent(msg->encodedContent());
0602     msg2.parse();
0603     QCOMPARE(msg2.encodedContent(), fileContent);
0604 }
0605 
0606 void MessageTest::testEmptySubject()
0607 {
0608     auto msg = readAndParseMail(QStringLiteral("empty-subject.mbox"));
0609     QVERIFY(msg); // was crashing for Andre
0610     QVERIFY(msg->hasHeader("Subject"));
0611     QVERIFY(msg->subject()->asUnicodeString().isEmpty());
0612 }
0613 
0614 void MessageTest::testReplyHeader()
0615 {
0616     auto msg = readAndParseMail(QStringLiteral("reply-header.mbox"));
0617     QVERIFY(msg);
0618     QVERIFY(!msg->replyTo(false));
0619     QCOMPARE(msg->hasHeader("Reply-To"), false);
0620     QCOMPARE(msg->hasHeader("Reply"), true);
0621     QVERIFY(msg->headerByType("Reply"));
0622 }
0623 
0624 KMime::Message::Ptr MessageTest::readAndParseMail(const QString &mailFile) const
0625 {
0626     QFile file(QLatin1String(TEST_DATA_DIR) + QLatin1String("/mails/") + mailFile);
0627     const bool ok = file.open(QIODevice::ReadOnly);
0628     if (!ok) {
0629         qWarning() << file.fileName() << "not found";
0630     }
0631     Q_ASSERT(ok);
0632     const QByteArray data = KMime::CRLFtoLF(file.readAll());
0633     Q_ASSERT(!data.isEmpty());
0634     KMime::Message::Ptr msg(new KMime::Message);
0635     msg->setContent(data);
0636     msg->parse();
0637     return msg;
0638 }
0639 
0640 void MessageTest::testBug392239()
0641 {
0642     auto msg = readAndParseMail(QStringLiteral("bug392239.mbox"));
0643 
0644     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: ");
0645     QCOMPARE(msg->contents().size(), 0);
0646     QCOMPARE(msg->contentTransferEncoding()->encoding(), KMime::Headers::CEbase64);
0647     QCOMPARE(msg->decodedText().toUtf8().data(), "Following this paragraph there is a double line break which should result in vertical spacing.\r\rPreceding this paragraph there is a double line break which should result in vertical spacing.\r");
0648 }
0649 
0650 void MessageTest::testBugAttachment387423()
0651 {
0652     auto msg = readAndParseMail(QStringLiteral("kmail-attachmentstatus.mbox"));
0653 
0654     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: XXXXXXXXXXXXXXXXXXXXX");
0655     QEXPECT_FAIL("", "Problem with searching attachment", Continue);
0656     QCOMPARE(msg->attachments().count(), 1);
0657     QCOMPARE(msg->contents().count(), 2);
0658 
0659     KMime::Content *attachment = msg->contents()[1];
0660     QCOMPARE(attachment->contentType(false)->mediaType().data(), "image");
0661     QCOMPARE(attachment->contentType(false)->subType().data(), "gif");
0662     QCOMPARE(attachment->contentType(false)->subType().data(), "gif");
0663     QCOMPARE(attachment->contentDisposition(false)->filename(), QStringLiteral("new.gif"));
0664     QCOMPARE(attachment->contentDisposition(false)->disposition(), Headers::CDattachment);
0665 }
0666 
0667 void MessageTest::testCrashReplyInvalidEmail()
0668 {
0669     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("crash-invalid-email-reply.mbox"));
0670     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: Re: Authorization required to post to gmane.network.wireguard (b96565298414a43aabcf9fbedf5e7e27)");
0671     QCOMPARE(msg->contentType()->mimeType().data(), "text/plain");
0672     QCOMPARE(msg->contentType()->charset().data(), "us-ascii");
0673     QVERIFY(msg->isTopLevel());
0674 }
0675 
0676 
0677 #include "moc_messagetest.cpp"