File indexing completed on 2024-04-21 05:18:09

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(), QLatin1StringView("heinz@test.de"));
0157     QCOMPARE(msg.from()->asUnicodeString(), QString());
0158     QCOMPARE(msg.cc()->asUnicodeString(), QLatin1StringView("moritz@test.de"));
0159     QCOMPARE(msg.subject()->asUnicodeString(), QLatin1StringView("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(),
0179              QLatin1StringView("Hello World"));
0180     QCOMPARE(msg.body().data(), "<Body>");
0181     QCOMPARE(msg.to()->asUnicodeString(), QLatin1StringView("test@test.de"));
0182 }
0183 
0184 void MessageTest::missingHeadersTest()
0185 {
0186     // Test that the message body is OK even though some headers are missing
0187     KMime::Message msg;
0188     const QString body = QStringLiteral("Hi Donald, look at those nice pictures I found!\n");
0189     const QString content =
0190         QLatin1StringView("From: georgebush@whitehouse.org\n"
0191                           "To: donaldrumsfeld@whitehouse.org\n"
0192                           "Subject: Cute Kittens\n"
0193                           "\n") +
0194         body;
0195     msg.setContent(content.toLatin1());
0196     msg.parse();
0197     msg.assemble();
0198 
0199     QCOMPARE(body, QString::fromLatin1(msg.body()));
0200 
0201     // Now create a new message, based on the content of the first one.
0202     // The body of the new message should still be the same.
0203     // (there was a bug that caused missing mandatory headers to be
0204     //  added as a empty newline, which caused parts of the header to
0205     //  leak into the body)
0206     KMime::Message msg2;
0207     msg2.setContent(msg.encodedContent());
0208     msg2.parse();
0209     msg2.assemble();
0210 
0211     QCOMPARE(body, QString::fromLatin1(msg2.body()));
0212 }
0213 
0214 void MessageTest::testBug219749()
0215 {
0216     // Test that the message body is OK even though some headers are missing
0217     KMime::Message msg;
0218     const QString content = QStringLiteral(
0219                                 "Content-Type: MULTIPART/MIXED;\n"
0220                                 " BOUNDARY=\"0-1804289383-1260384639=:52580\"\n"
0221                                 "\n"
0222                                 "--0-1804289383-1260384639=:52580\n"
0223                                 "Content-Type: TEXT/plain; CHARSET=UTF-8\n"
0224                                 "\n"
0225                                 "--0-1804289383-1260384639=:52580\n"
0226                                 "Content-Type: APPLICATION/octet-stream\n"
0227                                 "Content-Transfer-Encoding: BASE64\n"
0228                                 "Content-ID: <jaselka1.docx4AECA1F9@9230725.3CDBB752>\n"
0229                                 "Content-Disposition: ATTACHMENT; FILENAME=\"jaselka 1.docx\"\n"
0230                                 "\n"
0231                                 "UEsDBBQABgAIAAAAIQDd/JU3ZgEAACAFAAATAAgCW0NvbnRlbnRfVHlwZXNd\n"
0232                                 "SUwAAAAA\n"
0233                                 "\n"
0234                                 "--0-1804289383-1260384639=:52580--\n");
0235 
0236     msg.setContent(content.toLatin1());
0237     msg.parse();
0238 
0239     QCOMPARE(msg.contents().size(), 2);
0240     KMime::Content *attachment = msg.contents()[1];
0241     QCOMPARE(attachment->contentType(false)->mediaType().data(), "application");
0242     QCOMPARE(attachment->contentType(false)->subType().data(), "octet-stream");
0243     QCOMPARE(attachment->contentID()->identifier().data(), "jaselka1.docx4AECA1F9@9230725.3CDBB752");
0244     QCOMPARE(attachment->contentID()->as7BitString(false).data(), "<jaselka1.docx4AECA1F9@9230725.3CDBB752>");
0245     Headers::ContentDisposition *cd = attachment->contentDisposition(false);
0246     QVERIFY(cd);
0247     QCOMPARE(cd->filename(), QLatin1StringView("jaselka 1.docx"));
0248 }
0249 
0250 void MessageTest::testBidiSpoofing()
0251 {
0252     const QString RLO(QChar(0x202E));
0253     //const QString PDF( QChar( 0x202C ) );
0254 
0255     const QByteArray senderAndRLO =
0256         encodeRFC2047String(QString(QLatin1StringView("Sender") + RLO +
0257                                     QLatin1StringView(" <sender@test.org>")),
0258                             "utf-8");
0259 
0260     // The display name of the "From" has an RLO, make sure the KMime parser balances it
0261     QByteArray data =
0262         "From: " + senderAndRLO + "\n"
0263         "\n"
0264         "Body";
0265 
0266     KMime::Message msg;
0267     msg.setContent(data);
0268     msg.parse();
0269 
0270     // Test adjusted for taking into account that KMIME now removes bidi control chars
0271     // instead of adding PDF chars, because of broken KHTML.
0272     //const QString expectedDisplayName = "\"Sender" + RLO + PDF + "\"";
0273     const QString expectedDisplayName = QStringLiteral("Sender");
0274     const QString expectedMailbox =
0275         expectedDisplayName + QLatin1StringView(" <sender@test.org>");
0276     QCOMPARE(msg.from()->addresses().count(), 1);
0277     QCOMPARE(msg.from()->asUnicodeString(), expectedMailbox);
0278     QCOMPARE(msg.from()->displayNames().first(), expectedDisplayName);
0279     QCOMPARE(msg.from()->mailboxes().first().name(), expectedDisplayName);
0280     QCOMPARE(msg.from()->mailboxes().first().address().data(), "sender@test.org");
0281 }
0282 
0283 // Test to see if header fields of mails with an UTF-16 body are properly read
0284 // and written.
0285 // See also https://issues.kolab.org/issue3707
0286 void MessageTest::testUtf16()
0287 {
0288     QByteArray data =
0289         "From: foo@bar.com\n"
0290         "Subject: UTF-16 Test\n"
0291         "MIME-Version: 1.0\n"
0292         "Content-Type: Text/Plain;\n"
0293         "  charset=\"utf-16\"\n"
0294         "Content-Transfer-Encoding: base64\n"
0295         "\n"
0296         "//5UAGgAaQBzACAAaQBzACAAVQBUAEYALQAxADYAIABUAGUAeAB0AC4ACgAKAAo";
0297 
0298     KMime::Message msg;
0299     msg.setContent(data);
0300     msg.parse();
0301 
0302     QCOMPARE(msg.from()->asUnicodeString(), QLatin1StringView("foo@bar.com"));
0303     QCOMPARE(msg.subject()->asUnicodeString(),
0304              QLatin1StringView("UTF-16 Test"));
0305     QCOMPARE(msg.decodedText(false, true),
0306              QLatin1StringView("This is UTF-16 Text."));
0307 
0308     // Add a new To header, for testings
0309     auto to = new KMime::Headers::To;
0310     KMime::Types::Mailbox address;
0311     address.setAddress("test@test.de");
0312     address.setName(QStringLiteral("Fränz Töster"));
0313     to->addAddress(address);
0314     to->setRFC2047Charset("ISO-8859-1"); // default changed to UTF-8 in KF6, which is fine, but breaks the test
0315     msg.appendHeader(to);
0316     msg.assemble();
0317 
0318     QByteArray newData =
0319         "From: foo@bar.com\n"
0320         "Subject: UTF-16 Test\n"
0321         "MIME-Version: 1.0\n"
0322         "Content-Type: text/plain; charset=\"utf-16\"\n"
0323         "Content-Transfer-Encoding: base64\n"
0324         "To: =?ISO-8859-1?Q?Fr=E4nz_T=F6ster?= <test@test.de>\n"
0325         "\n"
0326         "//5UAGgAaQBzACAAaQBzACAAVQBUAEYALQAxADYAIABUAGUAeAB0AC4ACgAKAAo=\n"
0327         "\n";
0328 
0329     QCOMPARE(msg.encodedContent().data(), newData.data());
0330 }
0331 
0332 void MessageTest::testDecodedText()
0333 {
0334     QByteArray data =
0335         "Subject: Test\n"
0336         "\n"
0337         "Testing Whitespace   \n  \n \n\n\n";
0338 
0339     KMime::Message msg;
0340     msg.setContent(data);
0341     msg.parse();
0342 
0343     QCOMPARE(msg.decodedText(true, false),
0344              QLatin1StringView("Testing Whitespace"));
0345     QCOMPARE(msg.decodedText(true, true),
0346              QLatin1StringView("Testing Whitespace"));
0347     QCOMPARE(msg.decodedText(false, true),
0348              QLatin1StringView("Testing Whitespace   \n  \n "));
0349 
0350     QByteArray data2 =
0351         "Subject: Test\n"
0352         "\n"
0353         "Testing Whitespace   \n  \n \n\n\n ";
0354 
0355     KMime::Message msg2;
0356     msg2.setContent(data2);
0357     msg2.parse();
0358 
0359     QCOMPARE(msg2.decodedText(true, false),
0360              QLatin1StringView("Testing Whitespace"));
0361     QCOMPARE(msg2.decodedText(true, true),
0362              QLatin1StringView("Testing Whitespace"));
0363     QCOMPARE(msg2.decodedText(false, true),
0364              QLatin1StringView("Testing Whitespace   \n  \n \n\n\n "));
0365 }
0366 
0367 void MessageTest::testInlineImages()
0368 {
0369     const QByteArray data =
0370         "From: <kde@kde.org>\n"
0371         "To: kde@kde.org\n"
0372         "Subject: Inline Image (unsigned)\n"
0373         "Date: Wed, 23 Dec 2009 14:00:59 +0100\n"
0374         "MIME-Version: 1.0\n"
0375         "Content-Type: multipart/related;\n"
0376         "  boundary=\"Boundary-02=_LShMLJyjC7zqmVP\"\n"
0377         "Content-Transfer-Encoding: 7bit\n"
0378         "\n"
0379         "\n"
0380         "--Boundary-02=_LShMLJyjC7zqmVP\n"
0381         "Content-Type: multipart/alternative;\n"
0382         "  boundary=\"Boundary-01=_LShMLzAUPqE38S8\"\n"
0383         "Content-Transfer-Encoding: 7bit\n"
0384         "Content-Disposition: inline\n"
0385         "\n"
0386         "--Boundary-01=_LShMLzAUPqE38S8\n"
0387         "Content-Type: text/plain;\n"
0388         "  charset=\"us-ascii\"\n"
0389         "Content-Transfer-Encoding: 7bit\n"
0390         "\n"
0391         "First line\n"
0392         "\n"
0393         "\n"
0394         "Image above\n"
0395         "\n"
0396         "Last line\n"
0397         "\n"
0398         "--Boundary-01=_LShMLzAUPqE38S8\n"
0399         "Content-Type: text/html;\n"
0400         "  charset=\"us-ascii\"\n"
0401         "Content-Transfer-Encoding: 7bit\n"
0402         "\n"
0403         "Line 1\n"
0404         "--Boundary-01=_LShMLzAUPqE38S8--\n"
0405         "\n"
0406         "--Boundary-02=_LShMLJyjC7zqmVP\n"
0407         "Content-Type: image/png;\n"
0408         "  name=\"inlineimage.png\"\n"
0409         "Content-Transfer-Encoding: base64\n"
0410         "Content-Id: <740439759>\n"
0411         "\n"
0412         "jxrG/ha/VB+rODav6/d5i1US6Za/YEMvtm2SgJC/CXVFiD3UFSH2UFeE2ENdEWIPdUWIPdQVIfZQ\n"
0413         "V4TYQ10RYg91RYg91BUh9lBXhNhDXRFiD3VFiD3UFSH2UFeE2ENdEWIPdUWIPdQVIfZQV4TYQ10R\n"
0414         "Yg91RYg91BUh9lBX5E+Tz6Vty1HSx+NR++UuCOqKEHv+Ax0Y5U59+AHBAAAAAElFTkSuQmCC\n"
0415         "\n"
0416         "--Boundary-02=_LShMLJyjC7zqmVP--";
0417 
0418     KMime::Message msg;
0419     msg.setContent(data);
0420     msg.parse();
0421 
0422     QCOMPARE(msg.contents().size(), 2);
0423     QCOMPARE(msg.contents()[0]->contentType()->isMultipart(), true);
0424     QCOMPARE(msg.contents()[0]->contentType()->subType().data(), "alternative");
0425 
0426     QCOMPARE(msg.contents()[1]->contentType()->isImage(), true);
0427     QCOMPARE(msg.contents()[1]->contentType()->name(),
0428              QLatin1StringView("inlineimage.png"));
0429     QCOMPARE(msg.contents()[1]->contentID()->identifier().data(), "740439759");
0430     QCOMPARE(msg.contents()[1]->contentID()->as7BitString(false).data(), "<740439759>");
0431 }
0432 
0433 void MessageTest::testIssue3908()
0434 {
0435     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("issue3908.mbox"));
0436     QCOMPARE(msg->contents().size(), 2);
0437     KMime::Content *attachment = msg->contents().at(1);
0438     QVERIFY(attachment);
0439     QVERIFY(attachment->contentDescription(false));
0440     QCOMPARE(attachment->contentDescription()->asUnicodeString(), QString::fromUtf8(
0441                  "Kontact oder auch KDE-PIM ist der Groupware-Client aus der KDE Software Compilation 4.Eine der Besonderheiten von Kontact "
0442                  "gegenüber anderen Groupware-Clients ist, dass die Teil-Programme auch weiterhin unabhängig von Kontact gestartet werden "
0443                  "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 "
0444                  "gestartet wird: Die Mails und die persönlichen Einstellungen bleiben stets erhalten.Auch sieht Kontact eine modulare "
0445                  "Anbindung der Programme vor, wodurch sich auch in Zukunft weitere Module entwickeln und anfügen lassen, ohne Kontact "
0446                  "dafür zu ändern. Dies bietet die Möglichkeit, auch privat entwickelte Module einzubinden und so die Groupware grundlegend "
0447                  "eigenen Bedürfnissen anzupassen."));
0448 }
0449 
0450 void MessageTest::testIssue3914()
0451 {
0452     // This loads a mail which has a content-disposition of which the filename parameter is empty.
0453     // Check that the parser doesn't choke on this.
0454     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("broken-content-disposition.mbox"));
0455 
0456     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: Fwd: test broken mail");
0457     QCOMPARE(msg->contents().size(), 2);
0458     KMime::Content *attachedMail =  msg->contents().at(1);
0459     QCOMPARE(attachedMail->contentType()->mimeType().data(), "message/rfc822");
0460     QVERIFY(attachedMail->contentDisposition(false));
0461     QVERIFY(attachedMail->contentDisposition()->hasParameter(
0462         QLatin1StringView("filename")));
0463     QVERIFY(attachedMail->contentDisposition()
0464                 ->parameter(QLatin1StringView("filename"))
0465                 .isEmpty());
0466 }
0467 
0468 void MessageTest::testBug223509()
0469 {
0470     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("encoding-crash.mbox"));
0471 
0472     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: Blub");
0473     QCOMPARE(msg->contents().size(), 0);
0474     QCOMPARE(msg->contentTransferEncoding()->encoding(), KMime::Headers::CEbinary);
0475     QCOMPARE(msg->decodedText().toLatin1().data(), "Bla Bla Bla");
0476     QCOMPARE(msg->encodedBody().data(), "Bla Bla Bla\n");
0477 
0478     // encodedContent() was crashing in this bug because of an invalid assert
0479     QVERIFY(!msg->encodedContent().isEmpty());
0480 
0481     // Make sure that the encodedContent() is sane, by parsing it again.
0482     KMime::Message msg2;
0483     msg2.setContent(msg->encodedContent());
0484     msg2.parse();
0485     QCOMPARE(msg2.subject()->as7BitString().data(), "Subject: Blub");
0486     QCOMPARE(msg2.contents().size(), 0);
0487     QCOMPARE(msg2.contentTransferEncoding()->encoding(), KMime::Headers::CEbinary);
0488 
0489     QCOMPARE(msg2.decodedText().toLatin1().data(), "Bla Bla Bla");
0490     QCOMPARE(msg2.decodedText(true, true /* remove newlines at end */).toLatin1().data(),
0491              "Bla Bla Bla");
0492 }
0493 
0494 void MessageTest::testEncapsulatedMessages()
0495 {
0496     //
0497     // First, test some basic properties to check that the parsing was correct
0498     //
0499     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("simple-encapsulated.mbox"));
0500     QCOMPARE(msg->contentType()->mimeType().data(), "multipart/mixed");
0501     QCOMPARE(msg->contents().size(), 2);
0502     QVERIFY(msg->isTopLevel());
0503 
0504     KMime::Content *const textContent = msg->contents().at(0);
0505     QCOMPARE(textContent->contentType()->mimeType().data(), "text/plain");
0506     QVERIFY(textContent->contents().isEmpty());
0507     QVERIFY(!textContent->bodyIsMessage());
0508     QVERIFY(!textContent->bodyAsMessage());
0509     QVERIFY(!textContent->isTopLevel());
0510     QCOMPARE(
0511         textContent->decodedText(true, true),
0512         QLatin1StringView(
0513             "Hi Hans!\nLook at this interesting mail I forwarded to you!"));
0514     QCOMPARE(textContent->index().toString().toLatin1().data(), "1");
0515 
0516     KMime::Content *messageContent = msg->contents().at(1);
0517     QCOMPARE(messageContent->contentType()->mimeType().data(), "message/rfc822");
0518     QVERIFY(messageContent->body().isEmpty());
0519     QCOMPARE(messageContent->contents().count(), 1);
0520     QVERIFY(messageContent->bodyIsMessage());
0521     QVERIFY(messageContent->bodyAsMessage().data());
0522     QVERIFY(!messageContent->isTopLevel());
0523     QCOMPARE(messageContent->index().toString().toLatin1().data(), "2");
0524 
0525     KMime::Message::Ptr encapsulated = messageContent->bodyAsMessage();
0526     QCOMPARE(encapsulated->contents().size(), 0);
0527     QCOMPARE(encapsulated->contentType()->mimeType().data(), "text/plain");
0528     QVERIFY(!encapsulated->bodyIsMessage());
0529     QVERIFY(!encapsulated->bodyAsMessage());
0530     QCOMPARE(encapsulated->subject()->as7BitString(false).data(), "Foo");
0531     QCOMPARE(encapsulated->decodedText(false, false),
0532              QLatin1StringView("This is the encapsulated message body."));
0533     QCOMPARE(encapsulated.data(), messageContent->bodyAsMessage().data());
0534     QCOMPARE(encapsulated.data(), messageContent->contents().constFirst());
0535     QCOMPARE(encapsulated->parent(), messageContent);
0536     QVERIFY(!encapsulated->isTopLevel());
0537     QCOMPARE(encapsulated->topLevel(), msg.data());
0538     QCOMPARE(encapsulated->index().toString().toLatin1().data(), "2.1");
0539 
0540     // Now test some misc functions
0541     QCOMPARE(msg->storageSize(), msg->head().size() + textContent->storageSize() +
0542              messageContent->storageSize());
0543     QCOMPARE(messageContent->storageSize(), messageContent->head().size() +
0544              encapsulated->storageSize());
0545 
0546     // Now change some properties on the encapsulated message
0547     encapsulated->subject()->fromUnicodeString(QStringLiteral("New subject"), "us-ascii");
0548     encapsulated->fromUnicodeString(QStringLiteral("New body string."));
0549 
0550     // Since we didn't assemble the encapsulated message yet, it should still have the old headers
0551     QVERIFY(encapsulated->encodedContent().contains("Foo"));
0552     QVERIFY(!encapsulated->encodedContent().contains("New subject"));
0553 
0554     // Now assemble the container message
0555     msg->assemble();
0556 
0557     // Assembling the container message should have assembled the encapsulated message as well.
0558     QVERIFY(!encapsulated->encodedContent().contains("Foo"));
0559     QVERIFY(encapsulated->encodedContent().contains("New subject"));
0560     QCOMPARE(encapsulated->body().data(), "New body string.");
0561     QVERIFY(msg->encodedContent().contains(encapsulated->body()));
0562     QCOMPARE(msg->contentType()->mimeType().data(), "multipart/mixed");
0563     QCOMPARE(msg->contents().size(), 2);
0564     messageContent = msg->contents().at(1);
0565     QCOMPARE(messageContent->contentType()->mimeType().data(), "message/rfc822");
0566     QVERIFY(encapsulated.data() == messageContent->bodyAsMessage().data());
0567 
0568     // Setting a new body and then parsing it should discard the encapsulated message
0569     messageContent->contentType()->setMimeType("text/plain");
0570     messageContent->assemble();
0571     messageContent->setBody("Some new body");
0572     messageContent->parse();
0573     QVERIFY(!messageContent->bodyIsMessage());
0574     QVERIFY(!messageContent->bodyAsMessage());
0575     QCOMPARE(messageContent->contents().size(), 0);
0576 }
0577 
0578 void MessageTest::testOutlookAttachmentNaming()
0579 {
0580     // Try and decode
0581     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("outlook-attachment.mbox"));
0582     QVERIFY(msg->attachments().count() == 1);
0583 
0584     KMime::Content *attachment = msg->contents()[1];
0585     QCOMPARE(attachment->contentType(false)->mediaType().data(), "text");
0586     QCOMPARE(attachment->contentType(false)->subType().data(), "x-patch");
0587 
0588     Headers::ContentDisposition *cd = attachment->contentDisposition(false);
0589     QVERIFY(cd);
0590     QCOMPARE(cd->filename(), QString::fromUtf8("å.diff"));
0591 
0592     // Try and encode
0593     attachment->clear();// = new Content();
0594     attachment->contentDisposition()->setDisposition(Headers::CDattachment);
0595     attachment->contentDisposition()->setFilename(QStringLiteral("å.diff"));
0596     attachment->contentDisposition()->setRFC2047Charset("ISO-8859-1"); // default changed to UTF-8 in KF6, which is fine, but breaks the test
0597     attachment->assemble();
0598     qDebug() << "got:" << attachment->contentDisposition()->as7BitString(false);
0599     QCOMPARE(attachment->contentDisposition()->as7BitString(false), QByteArray("attachment; filename*=ISO-8859-1''%E5%2Ediff"));
0600 }
0601 
0602 void MessageTest::testEncryptedMails()
0603 {
0604     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("x-pkcs7.mbox"));
0605     QVERIFY(msg->contents().size() == 0);
0606     QVERIFY(msg->attachments().count() == 0);
0607     QVERIFY(KMime::isEncrypted(msg.data()) == true);
0608     QVERIFY(KMime::isInvitation(msg.data()) == false);
0609     QVERIFY(KMime::isSigned(msg.data()) == false);
0610 }
0611 
0612 void MessageTest::testReturnSameMail()
0613 {
0614     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("dontchangemail.mbox"));
0615     QFile file(QLatin1StringView(TEST_DATA_DIR) +
0616                QLatin1StringView("/mails/dontchangemail.mbox"));
0617     QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
0618     QByteArray fileContent = file.readAll();
0619     QCOMPARE(msg->encodedContent(), fileContent);
0620     QCOMPARE(msg->decodedText(), QLatin1StringView(""));
0621     KMime::Message msg2;
0622     msg2.setContent(msg->encodedContent());
0623     msg2.parse();
0624     QCOMPARE(msg2.encodedContent(), fileContent);
0625 }
0626 
0627 void MessageTest::testEmptySubject()
0628 {
0629     auto msg = readAndParseMail(QStringLiteral("empty-subject.mbox"));
0630     QVERIFY(msg); // was crashing for Andre
0631     QVERIFY(msg->hasHeader("Subject"));
0632     QVERIFY(msg->subject()->asUnicodeString().isEmpty());
0633 }
0634 
0635 void MessageTest::testReplyHeader()
0636 {
0637     auto msg = readAndParseMail(QStringLiteral("reply-header.mbox"));
0638     QVERIFY(msg);
0639     QVERIFY(!msg->replyTo(false));
0640     QCOMPARE(msg->hasHeader("Reply-To"), false);
0641     QCOMPARE(msg->hasHeader("Reply"), true);
0642     QVERIFY(msg->headerByType("Reply"));
0643 }
0644 
0645 KMime::Message::Ptr MessageTest::readAndParseMail(const QString &mailFile) const
0646 {
0647   QFile file(QLatin1StringView(TEST_DATA_DIR) + QLatin1StringView("/mails/") +
0648              mailFile);
0649   const bool ok = file.open(QIODevice::ReadOnly);
0650   if (!ok) {
0651     qWarning() << file.fileName() << "not found";
0652     }
0653     Q_ASSERT(ok);
0654     const QByteArray data = KMime::CRLFtoLF(file.readAll());
0655     Q_ASSERT(!data.isEmpty());
0656     KMime::Message::Ptr msg(new KMime::Message);
0657     msg->setContent(data);
0658     msg->parse();
0659     return msg;
0660 }
0661 
0662 void MessageTest::testBug392239()
0663 {
0664     auto msg = readAndParseMail(QStringLiteral("bug392239.mbox"));
0665 
0666     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: ");
0667     QCOMPARE(msg->contents().size(), 0);
0668     QCOMPARE(msg->contentTransferEncoding()->encoding(), KMime::Headers::CEbase64);
0669     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");
0670 }
0671 
0672 void MessageTest::testBugAttachment387423()
0673 {
0674     auto msg = readAndParseMail(QStringLiteral("kmail-attachmentstatus.mbox"));
0675 
0676     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: XXXXXXXXXXXXXXXXXXXXX");
0677     QEXPECT_FAIL("", "Problem with searching attachment", Continue);
0678     QCOMPARE(msg->attachments().count(), 1);
0679     QCOMPARE(msg->contents().count(), 2);
0680 
0681     KMime::Content *attachment = msg->contents()[1];
0682     QCOMPARE(attachment->contentType(false)->mediaType().data(), "image");
0683     QCOMPARE(attachment->contentType(false)->subType().data(), "gif");
0684     QCOMPARE(attachment->contentType(false)->subType().data(), "gif");
0685     QCOMPARE(attachment->contentDisposition(false)->filename(), QStringLiteral("new.gif"));
0686     QCOMPARE(attachment->contentDisposition(false)->disposition(), Headers::CDattachment);
0687 }
0688 
0689 void MessageTest::testCrashReplyInvalidEmail()
0690 {
0691     KMime::Message::Ptr msg = readAndParseMail(QStringLiteral("crash-invalid-email-reply.mbox"));
0692     QCOMPARE(msg->subject()->as7BitString().data(), "Subject: Re: Authorization required to post to gmane.network.wireguard (b96565298414a43aabcf9fbedf5e7e27)");
0693     QCOMPARE(msg->contentType()->mimeType().data(), "text/plain");
0694     QCOMPARE(msg->contentType()->charset().data(), "us-ascii");
0695     QVERIFY(msg->isTopLevel());
0696 }
0697 
0698 
0699 #include "moc_messagetest.cpp"