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

0001 /*
0002     SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "contenttest.h"
0008 
0009 #include <QDebug>
0010 #include <QTest>
0011 
0012 #include <kmime_content.h>
0013 #include <kmime_headers.h>
0014 #include <kmime_message.h>
0015 using namespace KMime;
0016 
0017 QTEST_MAIN(ContentTest)
0018 
0019 void ContentTest::testGetHeaderInstance()
0020 {
0021     // stuff that looks trivial but breaks if you mess with virtual method signatures (see r534381)
0022     auto myfrom = new Headers::From();
0023     QCOMPARE(myfrom->type(), "From");
0024     Headers::Base *mybase = myfrom;
0025     QCOMPARE(mybase->type(), "From");
0026     delete myfrom;
0027 
0028     // getHeaderInstance() is protected, so we need to test it via KMime::Message
0029     auto c = new Message();
0030     Headers::From *f1 = c->from(true);
0031     Headers::From *f2 = c->from(true);
0032     QCOMPARE(f1, f2);
0033     delete c;
0034 }
0035 
0036 void ContentTest::testHeaderAddRemove()
0037 {
0038     // Add a Content-Description header to a content.
0039     auto c = new Content;
0040     QVERIFY(!c->contentDescription(false));
0041     c->contentDescription()->from7BitString("description");
0042 
0043     // The content must now have the header.
0044     QVERIFY(c->contentDescription(false));
0045     QCOMPARE(c->contentDescription()->as7BitString(false), QByteArray("description"));
0046 
0047     // The content's head must also have the header.  Save the head.
0048     c->assemble();
0049     QByteArray head = c->head();
0050 
0051     // Clear the content.  It must now forget the cached header.
0052     c->clear();
0053     QVERIFY(c->head().isEmpty());
0054     QVERIFY(!c->contentDescription(false));
0055 
0056     // Put the head back.  It must now remember the header.
0057     c->setHead(head);
0058     QVERIFY(!c->contentDescription(false));
0059     c->parse();
0060     QVERIFY(c->contentDescription(false));
0061     c->contentDescription()->from7BitString("description");
0062 
0063     // Now remove the header explicitly.
0064     bool ret = c->removeHeader("Content-Description");
0065     QVERIFY(ret);
0066 
0067     // The content must have forgotten the header now.
0068     QVERIFY(!c->contentDescription(false));
0069 
0070     // And after assembly the header should stay gone.
0071     c->assemble();
0072     //QVERIFY(c->head().isEmpty());
0073     QVERIFY(!c->contentDescription(false));
0074 
0075     delete c;
0076 
0077     // test template versions
0078     Content c2;
0079     QVERIFY(!c2.hasHeader(KMime::Headers::Lines::staticType()));
0080     QVERIFY(!c2.header<KMime::Headers::Lines>(false));
0081     QVERIFY(c2.header<KMime::Headers::Lines>(true));
0082     QVERIFY(c2.hasHeader(KMime::Headers::Lines::staticType()));
0083     c2.removeHeader<KMime::Headers::Lines>();
0084     QVERIFY(!c2.hasHeader(KMime::Headers::Lines::staticType()));
0085 }
0086 
0087 void ContentTest::testHeaderAppend()
0088 {
0089     auto c = new Content;
0090     QByteArray d1("Resent-From: test1@example.com");
0091     QByteArray d2("Resent-From: test2@example.com");
0092     auto h1 = new Headers::Generic("Resent-From");
0093     h1->from7BitString("test1@example.com");
0094     auto h2 = new Headers::Generic("Resent-From");
0095     h2->from7BitString("test2@example.com");
0096     c->appendHeader(h1);
0097     c->appendHeader(h2);
0098     c->assemble();
0099     QByteArray head = d1 + '\n' + d2 + '\n';
0100     QCOMPARE(c->head(), head);
0101 
0102     QByteArray d3("Resent-From: test3@example.com");
0103     auto h3 = new Headers::Generic("Resent-From");
0104     h3->from7BitString("test3@example.com");
0105     c->appendHeader(h3);
0106     c->assemble();
0107     head.append(d3 + '\n');
0108     QCOMPARE(c->head(), head);
0109     delete c;
0110 }
0111 
0112 void ContentTest::testExplicitMultipartGeneration()
0113 {
0114     auto c1 = new Content();
0115     c1->contentType()->from7BitString("multipart/mixed");
0116 
0117     auto c2 = new Content();
0118     c2->contentType()->from7BitString("text/plain");
0119     c2->setBody("textpart");
0120 
0121     auto c3 = new Content();
0122     c3->contentType()->from7BitString("text/html");
0123     c3->setBody("htmlpart");
0124 
0125     c1->appendContent(c2);
0126     c1->appendContent(c3);
0127 
0128     // c1 should not have been changed.
0129     QCOMPARE(c1->contentType()->mimeType(), QByteArray("multipart/mixed"));
0130     QVERIFY(c1->body().isEmpty());
0131 
0132     QCOMPARE(c1->contents().count(), 2);
0133     QCOMPARE(c1->contents().at(0), c2);
0134     QCOMPARE(c1->contents().at(1), c3);
0135 
0136     c1->takeContent(c3);
0137     QCOMPARE(c1->contents().count(), 1);
0138     QCOMPARE(c1->contentType()->mimeType(), QByteArray("multipart/mixed"));
0139     QVERIFY(c1->body().isEmpty());
0140     QCOMPARE(c1->contents().at(0), c2);
0141     QCOMPARE(c2->body(), QByteArray("textpart"));
0142     QCOMPARE(c3->parent(), nullptr);
0143 
0144     // Clean up.
0145     delete c1;
0146     // c2 was deleted when c1 turned itself single-part.
0147     delete c3;
0148 }
0149 
0150 void ContentTest::testSetContent()
0151 {
0152     auto c = new Content();
0153     QVERIFY(!c->hasContent());
0154 
0155     // head and body present
0156     c->setContent("head1\nhead2\n\nbody1\n\nbody2\n");
0157     QVERIFY(c->hasContent());
0158     QCOMPARE(c->head(), QByteArray("head1\nhead2\n"));
0159     QCOMPARE(c->body(), QByteArray("body1\n\nbody2\n"));
0160 
0161     // empty content
0162     c->setContent(QByteArray());
0163     QVERIFY(!c->hasContent());
0164     QVERIFY(c->head().isEmpty());
0165     QVERIFY(c->body().isEmpty());
0166 
0167     // empty head
0168     c->setContent("\nbody1\n\nbody2\n");
0169     QVERIFY(c->hasContent());
0170     QVERIFY(c->head().isEmpty());
0171     QCOMPARE(c->body(), QByteArray("body1\n\nbody2\n"));
0172 
0173     // empty body
0174     c->setContent("head1\nhead2\n\n");
0175     QVERIFY(c->hasContent());
0176     QCOMPARE(c->head(), QByteArray("head1\nhead2\n"));
0177     QVERIFY(c->body().isEmpty());
0178 
0179     delete c;
0180 }
0181 
0182 void ContentTest::testEncodedContent()
0183 {
0184     // Example taken from RFC 2046, section 5.1.1.
0185     // Removed "preamble" and "epilogue", which KMime loses.
0186     QByteArray data =
0187         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0188         "To: Ned Freed <ned@innosoft.com>\n"
0189         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0190         "Subject: Sample message\n"
0191         "MIME-Version: 1.0\n"
0192         "Content-type: multipart/mixed; boundary=\"simple boundary\"\n"
0193         "\n"
0194         "\n"
0195         "--simple boundary\n"
0196         "\n"
0197         "This is implicitly typed plain US-ASCII text.\n"
0198         "It does NOT end with a linebreak.\n"
0199         "--simple boundary\n"
0200         "Content-type: text/plain; charset=us-ascii\n"
0201         "\n"
0202         "This is explicitly typed plain US-ASCII text.\n"
0203         "It DOES end with a linebreak.\n"
0204         "\n"
0205         "--simple boundary--\n";
0206 
0207     auto msg = new Message;
0208     msg->setContent(data);
0209     msg->parse();
0210 
0211     // Test that multiple calls do not corrupt anything.
0212     //QByteArray encc = msg->encodedContent();
0213     //qDebug() << "original data" << data;
0214     //qDebug() << "encodedContent" << encc;
0215     QCOMPARE(msg->encodedContent(), data);
0216     QCOMPARE(msg->encodedContent(), data);
0217     QCOMPARE(msg->encodedContent(), data);
0218     delete msg;
0219 
0220     // RFC 2822 3.5: lines are limited to 1000 characters (998 + CRLF)
0221     // (bug #187345)
0222     msg = new Message();
0223     data =
0224         "Subject:"
0225         "test test test test test test test test test test test test test test test test test test test test "
0226         "test test test test test test test test test test test test test test test test test test test test "
0227         "test test test test test test test test test test test test test test test test test test test test "
0228         "test test test test test test test test test test test test test test test test test test test test "
0229         "test test test test test test test test test test test test test test test test test test test test "
0230         "test test test test test test test test test test test test test test test test test test test test "
0231         "test test test test test test test test test test test test test test test test test test test test "
0232         "test test test test test test test test test test test test test test test test test test test test "
0233         "test test test test test test test test test test test test test test test test test test test test "
0234         "test test test test test test test test test test test test test test test test test test test test"
0235         "\n"
0236         "References: "
0237         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> "
0238         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> "
0239         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> "
0240         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> "
0241         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> "
0242         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> "
0243         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> "
0244         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> "
0245         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> "
0246         "<test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com> <test1@example.com>"
0247         "\n\n"
0248         "body\n";
0249     msg->setContent(data);
0250     QByteArray content = msg->encodedContent(true /* use CRLF */);
0251     const QStringList lines = QString::fromLatin1(content).split(QStringLiteral("\r\n"));
0252     for (const QString &line : lines) {
0253         QEXPECT_FAIL("", "KMime does not fold lines longer than 998 characters", Continue);
0254         QVERIFY(line.length() < 998 && !line.isEmpty() && line != QLatin1String("body"));
0255         // The test should be (after the expected failure disappears):
0256         //QVERIFY( line.length() < 998 );
0257     }
0258     delete msg;
0259 
0260 }
0261 
0262 void ContentTest::testDecodedContent()
0263 {
0264     auto c = new Content();
0265     c->setBody("\0");
0266     QVERIFY(c->decodedContent() == QByteArray());
0267     c->setBody(QByteArray());
0268     QVERIFY(c->decodedContent() == QByteArray());
0269     c->setBody(" ");
0270     QVERIFY(c->decodedContent() == QByteArray(" "));
0271     delete c;
0272 }
0273 
0274 void ContentTest::testMultipleHeaderExtraction()
0275 {
0276     QByteArray data =
0277         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0278         "To: Ned Freed <ned@innosoft.com>\n"
0279         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0280         "Subject: Sample message\n"
0281         "Received: from ktown.kde.org ([192.168.100.1])\n"
0282         "Received: from dev1.kde.org ([192.168.100.2])\n"
0283         "\t by ktown.kde.org ([192.168.100.1])\n"
0284         "Received: from dev2.kde.org ([192.168.100.3])\n"
0285         "           by ktown.kde.org ([192.168.100.1])\n";
0286 
0287     auto msg = new Message();
0288     msg->setContent(data);
0289     // FAILS identically to ContentTest::testMultipartMixed
0290     //  QCOMPARE( msg->encodedContent(), data );
0291     msg->parse();
0292 
0293     auto result = msg->headersByType("Received");
0294     QCOMPARE(result.count(), 3);
0295     QCOMPARE(result[0]->asUnicodeString(),  QString::fromLatin1("from ktown.kde.org ([192.168.100.1])"));
0296     QCOMPARE(result[1]->asUnicodeString(),  QString::fromLatin1("from dev1.kde.org ([192.168.100.2]) by ktown.kde.org ([192.168.100.1])"));
0297     QCOMPARE(result[2]->asUnicodeString(),  QString::fromLatin1("from dev2.kde.org ([192.168.100.3]) by ktown.kde.org ([192.168.100.1])"));
0298     delete msg;
0299 }
0300 
0301 void ContentTest::testMultipartMixed()
0302 {
0303     // example taken from RFC 2046, section 5.1.1.
0304     QByteArray data =
0305         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0306         "To: Ned Freed <ned@innosoft.com>\n"
0307         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0308         "Subject: Sample message\n"
0309         "MIME-Version: 1.0\n"
0310         "Content-type: multipart/mixed; boundary=\"simple boundary\"\n"
0311         "\n"
0312         "This is the preamble.  It is to be ignored, though it\n"
0313         "is a handy place for composition agents to include an\n"
0314         "explanatory note to non-MIME conformant readers.\n"
0315         "\n"
0316         "--simple boundary\n"
0317         "\n"
0318         "This is implicitly typed plain US-ASCII text.\n"
0319         "It does NOT end with a linebreak.\n"
0320         "--simple boundary\n"
0321         "Content-type: text/plain; charset=us-ascii\n"
0322         "\n"
0323         "This is explicitly typed plain US-ASCII text.\n"
0324         "It DOES end with a linebreak.\n"
0325         "\n"
0326         "--simple boundary--\n"
0327         "\n"
0328         "This is the epilogue.  It is also to be ignored.\n";
0329 
0330     QByteArray part1 =
0331         "This is implicitly typed plain US-ASCII text.\n"
0332         "It does NOT end with a linebreak.";
0333 
0334     QByteArray part2 =
0335         "This is explicitly typed plain US-ASCII text.\n"
0336         "It DOES end with a linebreak.\n";
0337 
0338     // What we expect KMime to parse the above data into.
0339     QByteArray parsedWithPreambleAndEpilogue =
0340         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0341         "To: Ned Freed <ned@innosoft.com>\n"
0342         "Date: Sun, 21 Mar 1993 23:56:48 -0800\n"
0343         "Subject: Sample message\n"
0344         "MIME-Version: 1.0\n"
0345         "Content-Type: multipart/mixed; boundary=\"simple boundary\"\n"
0346         "\n"
0347         "This is the preamble.  It is to be ignored, though it\n"
0348         "is a handy place for composition agents to include an\n"
0349         "explanatory note to non-MIME conformant readers.\n"
0350         "\n"
0351         "--simple boundary\n"
0352         "Content-Type: text/plain; charset=\"us-ascii\"\n\n"
0353         "This is implicitly typed plain US-ASCII text.\n"
0354         "It does NOT end with a linebreak.\n"
0355         "--simple boundary\n"
0356         "Content-Type: text/plain; charset=\"us-ascii\"\n"
0357         "\n"
0358         "This is explicitly typed plain US-ASCII text.\n"
0359         "It DOES end with a linebreak.\n"
0360         "\n"
0361         "--simple boundary--\n"
0362         "\n"
0363         "This is the epilogue.  It is also to be ignored.\n";
0364 
0365     // What we expect KMime to assemble the above data into.
0366     QByteArray assembled =
0367         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0368         "To: Ned Freed <ned@innosoft.com>\n"
0369         "Date: Sun, 21 Mar 1993 23:56:48 -0800\n"
0370         "Subject: Sample message\n"
0371         "MIME-Version: 1.0\n"
0372         "Content-Type: multipart/mixed; boundary=\"simple boundary\"\n"
0373         "Content-Transfer-Encoding: 7Bit\n"
0374         "\n"
0375         "--simple boundary\n"
0376         "\n"
0377         "This is implicitly typed plain US-ASCII text.\n"
0378         "It does NOT end with a linebreak.\n"
0379         "--simple boundary\n"
0380         "Content-Type: text/plain; charset=\"us-ascii\"\n"
0381         "\n"
0382         "This is explicitly typed plain US-ASCII text.\n"
0383         "It DOES end with a linebreak.\n"
0384         "\n"
0385         "--simple boundary--\n";
0386 
0387     // test parsing
0388     auto msg = new Message();
0389     msg->setContent(data);
0390     QCOMPARE(msg->encodedContent(), data);
0391     msg->parse();
0392     QVERIFY(msg->contentType()->isMultipart());
0393 
0394     auto list = msg->contents();
0395     QCOMPARE(list.count(), 2);
0396     Content *c = list.takeFirst();
0397     QCOMPARE(c->body(), part1);
0398     c = list.takeFirst();
0399     QCOMPARE(c->body(), part2);
0400 
0401     // assemble again
0402     msg->assemble();
0403     //qDebug() << "expected assembled content" << parsedWithPreambleAndEpilogue;
0404     //qDebug() << "actual new encoded content" << msg->encodedContent();
0405     QCOMPARE(msg->encodedContent(), parsedWithPreambleAndEpilogue);
0406     delete msg;
0407 
0408     // assembling from scratch
0409     // (The headers have to be in the same order, as we compare with the above assembled.)
0410     msg = new Message();
0411     msg->from()->from7BitString("Nathaniel Borenstein <nsb@bellcore.com>");
0412     msg->to()->from7BitString("Ned Freed <ned@innosoft.com>");
0413     msg->date()->from7BitString("Sun, 21 Mar 1993 23:56:48 -0800 (PST)");
0414     msg->subject()->from7BitString("Sample message");
0415     // HACK to make MIME-Version appear before Content-Type, as in the expected message.
0416     auto header = new Headers::MIMEVersion;
0417     header->from7BitString("1.234");
0418     msg->setHeader(header);
0419     msg->contentType()->from7BitString("multipart/mixed");
0420     msg->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit);
0421 
0422     c = new Content();
0423     c->setBody(part1);
0424     msg->appendContent(c);
0425 
0426     c = new Content();
0427     c->setBody(part2);
0428     c->contentType()->setMimeType("text/plain");
0429     c->contentType()->setCharset("us-ascii");
0430     msg->appendContent(c);
0431     msg->contentType()->setBoundary("simple boundary");
0432 
0433     list = msg->contents();
0434     QCOMPARE(list.count(), 2);
0435     c = list.takeFirst();
0436     QCOMPARE(c->body(), part1);
0437     c = list.takeFirst();
0438     QCOMPARE(c->body(), part2);
0439 
0440     msg->assemble();
0441     //QByteArray encc = msg->encodedContent();
0442     //qDebug().noquote() << "expected assembled content" << assembled;
0443     //qDebug().noquote() << "actual encoded content" << encc;
0444     QCOMPARE(msg->encodedContent(), assembled);
0445     delete msg;
0446 }
0447 
0448 void ContentTest::testParsingUuencoded()
0449 {
0450     const QByteArray body =
0451         "This is a test message that should appears as a text/plain part\n"
0452         "once this message is parsed and convert to a MIME tree.\n"
0453         "\n"
0454         "\n";
0455 
0456     const QString imageName = QStringLiteral("Name of the encoded file (oxygen 22x22 kde.png)");
0457     const QByteArray imageBase64 =
0458         "\n"
0459         "iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\n"
0460         "AAADdgAAA3YBfdWCzAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAU4SURB\n"
0461         "VBgZjcFbiF1XGcDx/7fW2pdznTOXNJlJc4WWVK3RqiC2FOyTiGCs+lKkohWTIl5QWrAp9ckLKpYi\n"
0462         "KFjwodgXoRDMk1VECj7UpkIqUZuCTUycyWXOzJxz9tnXtdfnxNqnvvj7iaryNhGxgPBOAh/gLa+y\n"
0463         "S3kn3dXyP6KqyEe+1Rm6tSc6nYVHO+loOXYR1hisFYRAIOBljtecyPaItEMkXeK4S2QTVAxVOZ1t\n"
0464         "TzaeG6//9fTWuR9MnOxyx7/xzaWjB548cvgAUeyJbGDYj9mzPGJl1GdpocOwlxCCMs1qtrKSrZ2c\n"
0465         "ze0Z126O2ZkWSJoO0rDylUabREROOsCoG3z58JEDrK4NIFQMBz0WBl2G3ZReGiNi+debO6gKC3sH\n"
0466         "DAcxNu6QpF1GiwtsTzMm04wrVyeY7upngEcdYIy4pSgVJtmMYb+HmBiVGE9Eo47ZdsHJj3eJnOHp\n"
0467         "M3P6exbIJxmffr/ibMK58zN+M4nwlGCTPmAMu8QYKasCFYd1CWoSgkT4YGmCoWggTRLiOKH0UFTK\n"
0468         "A8csdx0ZcnBfl/PXIuJ+j253gBED3CEGDluxVtqgGBcTJCKIZboxJq9bssozLxqiKMJZS1G3LIct\n"
0469         "7nvfAs5FvPDSjHlnEbER3f4AsUZYG1rD2t3GGIu4GIhosUSzCd9/5HZOvKtldnmd7evbRM7hnEOz\n"
0470         "CV/8xCrOWv52qeKVGx0CBpUIF3cwxsLwdmtYPGSMtaLW0WIIKuStIXLCh9+9wE++fgfWV4jwX489\n"
0471         "fJQkMswr5ee/26EMgaaFVoW6VsRaGXWWrFnqWyPWSV0rrULlA7M45dd/uEHZwOlfvMGW6yAiiAhr\n"
0472         "KwkgGIEiL8jrmryuqWpPWbWItYTlNWvauGeNs8xLT9W2FFXDdGPMwb0pz569wsUqpqgbQADhmecu\n"
0473         "IgK91HHqY7cxz0um85zxrKAVMNYSbGKNtqkIhtB6xptTvntiyJnv3MVH71niT3+fUHvQ1vC2F1+v\n"
0474         "efHPm9xy33sXubtXsj3NaJqKNjSA0KePEVsqKEE9dZWTOBCUtg1sZoamhrYFVQWUphV+dPYml67l\n"
0475         "3PLtz99Jr8zxdYn3NSJKRoYxhQZ2+aZCteWhZy7yydOvceHNOXeuWNRbQmMIIaCqGGJcOuL0s5fJ\n"
0476         "S8+gY3j8U4fQ2hPqEg0BqQnCsUcGg7XjNxZXV1MbJQx6I1ZW9vPge4QHPrjM47/cwXZ6VFmBaEsy\n"
0477         "6GPqgqEtqJqWsmq4OpmT+Sl1XTHdHIemeG3ZML3RBu+1rkp8mROahqiYceL+RQ7eZvnewwusyoRh\n"
0478         "f8hgtMywmfPUQ0Oe+sI+WlJ0tIrrJjR1SdMUBO/Z2fhn61g/68PRe7UqC4JraDo1h3oVsW1440rD\n"
0479         "718uOfXgiL1LEIKiOsI5IY0CT36uzxO/KvF1TV3MqX1D8F6Z/8U7QEPr1WCpyzlVVXJuo+WrP7xE\n"
0480         "ke5neeUA55+/ytNfSxAnPPazEnVdPntvweV/52R5oK4KqiqnqhtQr1y50jpAQ1PmvbTfG493mE62\n"
0481         "oYV/+CWGgzFN8EQm5vo4RxWmLKBty09/65nPC6bZDjuTLeZZhrMJWs8rdjkghOmlF3x57NTy4hrX\n"
0482         "b65T5zl1WVAWc7LuhDTpcvLHFcYY6E7xTUNZ5eT5jFm2w3S6RWRT9oz2cXX9lT8Cragqsv9DK93F\n"
0483         "48/3995zf7e/J41dhDMWawQkoNriTYbXnMj2ibRLJF3iuEtkE1SEfL7VXLv00qs3Xz/zpWp84YKo\n"
0484         "KreIiANGwH5AAOH/o7xlE7gOeN31H1IDp2dl3tAoAAAAAElFTkSuQmCC\n"
0485         ;
0486 
0487     const QByteArray uuencodedMsg =
0488         "Path: news.example.net!not-for-mail\n"
0489         "From: Coin coin <meuh@example.net>\n"
0490         "Newsgroups: test.kmime.uuencoded\n"
0491         "Subject: Kmime test\n"
0492         "Date: Thu, 14 Apr 2005 20:12:47 -0700\n"
0493         "Message-ID: <xxxxxxxxxxxxxxxxxx@xxxxx.kmime.example.net>\n"
0494         "X-Newsreader: Forte Agent 2.0/32.640\n"
0495         "Lines: 1283\n"
0496         "Organization: Ament\n"
0497         "Xref: news.example.net test.kmime.uuencoded:4584\n"
0498         "\n"
0499         "This is a test message that should appears as a text/plain part\n"
0500         "once this message is parsed and convert to a MIME tree.\n"
0501         "\n"
0502         "begin 644 Name of the encoded file (oxygen 22x22 kde.png)\n"
0503         "MB5!.1PT*&@H````-24A$4@```!8````6\"`8```#$M&P[````!'-\"250(\"`@(\n"
0504         "M?`ADB`````EP2%ES```#=@```W8!?=6\"S````!ET15AT4V]F='=A<F4`=W=W\n"
0505         "M+FEN:W-C87!E+F]R9YON/!H```4X241!5!@9C<%;B%U7&<#Q_[?6VI=SG3.7\n"
0506         "M-)E)<X665*W1JB\"V%.R3B&\"L^E*DHA63(EY06K`I]<D+*I8B*%CPH=@7H1#,\n"
0507         "MDU5$\"C[4ID(J49N\"34R<R67.S)QS]MG7M=?GQ-JGOOC[B:KR-A&Q@/!.`A_@\n"
0508         "M+:^R2WDGW=7R/Z*JR$>^U1FZM2<ZG85'.^EH.781UABL%81`(.!ECM><R/:(\n"
0509         "MM$,D7>*X2V035`Q5.9UM3S:>&Z__]?36N1],G.QRQ[_QS:6C!YX\\<O@`4>R)\n"
0510         "M;&#8C]FS/&)EU&=IH<.PEQ\"\",LUJMK*2K9V<S>T9UVZ.V9D62)H.TK#RE4:;\n"
0511         "M1$1..L\"H&WSY\\)$#K*X-(%0,!ST6!EV&W91>&B-B^=>;.Z@*\"WL'#`<Q-NZ0\n"
0512         "MI%U&BPML3S,FTXPK5R>8[NIG@$<=8(RXI2@5)MF,8;^'F!B5&$]$HX[9=L')\n"
0513         "MCW>)G.'I,W/Z>Q;()QF??K_B;,*Y\\S-^,XGPE&\"3/F`,N\\08*:L\"%8=U\"6H2\n"
0514         "M@D3X8&F\"H6@@31+B.*'T4%3*`\\<L=QT9<G!?E_/7(N)^CVYW@!$#W\"$&#ENQ\n"
0515         "M5MJ@&!<3)\"*(9;HQ)J];LLHS+QJB*,)92U&W+(<M[GO?`LY%O/#2C'EG$;$1\n"
0516         "MW?X`L498&UK#VMW&&(NX&(AHL42S\"=]_Y'9.O*ME=GF=[>O;1,[AG$.S\"5_\\\n"
0517         "MQ\"K.6OYVJ>*5&QT\"!I4(%W<PQL+P=FM8/&2,M:+6T6((*N2M(7+\"A]^]P$^^\n"
0518         "M?@?65XCP7X\\]?)0D,LPKY>>_VZ$,@::%5H6Z5L1:&766K%GJ6R/625TKK4+E\n"
0519         "M`[,XY==_N$'9P.E?O,&6ZR`BB`AK*PD@&($B+\\CKFKRNJ6I/6;6(M83E-6O:\n"
0520         "MN&>-L\\Q+3]6V%%7#=&/,P;TISYZ]PL4JIJ@;0`#AF><N(@*]U''J8[<QSTNF\n"
0521         "M\\YSQK*`5,-82;&*-MJD(AM!ZQIM3OGMBR)GOW,5'[UGB3W^?4'O0UO\"V%U^O\n"
0522         "M>?'/F]QRWWL7N;M7LCW-:)J*-C2`T*>/$5LJ*$$]=963.!\"4M@UL9H:FAK8%\n"
0523         "M5064IA5^=/8FEZ[EW/+MS]])K\\SQ=8GW-2)*1H8QA09V^:9\"M>6A9R[RR=.O\n"
0524         "M<>'-.7>N6-1;0F,((:\"J&&)<.N+TLY?)2\\^@8WC\\4X?0VA/J$@T!J0G\"L4<&\n"
0525         "M@[7C-Q975U,;)0QZ(U96]O/@>X0'/KC,X[_<P79Z5%F!:$LRZ&/J@J$MJ)J6\n"
0526         "MLFJX.IF3^2EU73'='(>F>&W9,+W1!N^UKDI\\F1.:AJB8<>+^10[>9OG>PPNL\n"
0527         "MRH1A?\\A@M,RPF?/40T.>^L(^6E)TM(KK)C1U2=,4!._9V?AGZU@_Z\\/1>[4J\n"
0528         "M\"X)K:#HUAWH5L6UXXTK#[U\\N.?7@B+U+$(*B.L(Y(8T\"3WZNSQ._*O%U35W,\n"
0529         "MJ7U#\\%Z9_\\4[0$/KU6\"IRSE557)NH^6K/[Q$D>YG>>4`YY^_RM-?2Q`G//:S\n"
0530         "M$G5=/GMOP>5_YV1YH*X*JBJGJAM0KURYTCI`0U/FO;3?&X]WF$ZVH85_^\"6&\n"
0531         "M@S%-\\$0FYOHX1Q6F+*!MRT]_ZYG/\"Z;9#CN3+>99AK,)6L\\K=CD@A.FE%WQY\n"
0532         "M[-3RXAK7;ZY3YSEU65`6<[+NA#3I<O+'%<88Z$[Q34-9Y>3YC%FVPW2Z1613\n"
0533         "M]HSV<77]E3\\\"K:@JLO]#*]W%X\\_W]]YS?[>_)XU=A#,6:P0DH-KB38;7G,CV\n"
0534         "MB;1+)%WBN$MD$U2$?+[57+OTTJLW7S_SI6I\\X8*H*K>(B`-&P'Y``.'_H[QE\n"
0535         ";$[@.>-WU'U(#IV=EWM`H`````$E%3D2N0F\"\"\n"
0536         "`\n"
0537         "end\n"
0538         "\n";
0539 
0540     auto msg = new Message();
0541     msg->setContent(uuencodedMsg);
0542     msg->parse();
0543     auto contents = msg->contents();
0544 
0545     // text + image
0546     QCOMPARE(contents.size(), 2);
0547 
0548     Content *c = nullptr;
0549 
0550     // Check the first text part
0551     c = contents.at(0);
0552     QVERIFY(c->contentType()->isPlainText());
0553     QCOMPARE(c->body(), body);
0554 
0555     // Check the image part
0556     c = contents.at(1);
0557     QVERIFY(!c->contentType()->isText());
0558     QCOMPARE(c->contentType()->name(), imageName);
0559     // The uuencoded content as been recoded as base64
0560     QCOMPARE(c->encodedContent().data(), imageBase64.data());
0561 
0562     delete msg;
0563 }
0564 
0565 void ContentTest::testParent()
0566 {
0567     auto c1 = new Content();
0568     c1->contentType()->from7BitString("multipart/mixed");
0569 
0570     auto c2 = new Content();
0571     c2->contentType()->from7BitString("text/plain");
0572     c2->setBody("textpart");
0573 
0574     auto c3 = new Content();
0575     c3->contentType()->from7BitString("text/html");
0576     c3->setBody("htmlpart");
0577 
0578     auto c4 = new Content();
0579     c4->contentType()->from7BitString("text/html");
0580     c4->setBody("htmlpart2");
0581 
0582     auto c5 = new Content();
0583     c5->contentType()->from7BitString("multipart/mixed");
0584 
0585     //c2 doesn't have a parent yet
0586     QCOMPARE(c2->parent(), (Content *)nullptr);
0587 
0588     c1->appendContent(c2);
0589     c1->appendContent(c3);
0590     c1->appendContent(c4);
0591     QCOMPARE(c1->contents().size(), 3);
0592 
0593     // c1 is the parent of those
0594     QCOMPARE(c2->parent(), c1);
0595     QCOMPARE(c3->parent(), c1);
0596 
0597     //test removal
0598     c1->takeContent(c2);
0599     QCOMPARE(c2->parent(), (Content *)nullptr);
0600     QCOMPARE(c1->contents().at(0), c3);
0601     QCOMPARE(c1->contents().size(), 2);
0602 
0603 //check if the content is moved correctly to another parent
0604     c5->appendContent(c4);
0605     QCOMPARE(c4->parent(), c5);
0606     QCOMPARE(c1->contents().size(), 1);
0607     QCOMPARE(c1->contents().at(0), c3);
0608     QCOMPARE(c5->contents().size(), 1);
0609     QCOMPARE(c5->contents().at(0), c4);
0610 
0611     // example taken from RFC 2046, section 5.1.1.
0612     QByteArray data =
0613         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0614         "To: Ned Freed <ned@innosoft.com>\n"
0615         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0616         "Subject: Sample message\n"
0617         "MIME-Version: 1.0\n"
0618         "Content-type: multipart/mixed; boundary=\"simple boundary\"\n"
0619         "\n"
0620         "This is the preamble.  It is to be ignored, though it\n"
0621         "is a handy place for composition agents to include an\n"
0622         "explanatory note to non-MIME conformant readers.\n"
0623         "\n"
0624         "--simple boundary\n"
0625         "\n"
0626         "This is implicitly typed plain US-ASCII text.\n"
0627         "It does NOT end with a linebreak.\n"
0628         "--simple boundary\n"
0629         "Content-type: text/plain; charset=us-ascii\n"
0630         "\n"
0631         "This is explicitly typed plain US-ASCII text.\n"
0632         "It DOES end with a linebreak.\n"
0633         "\n"
0634         "--simple boundary--\n"
0635         "\n"
0636         "This is the epilogue.  It is also to be ignored.\n";
0637 
0638     // test parsing
0639     auto msg = new Message();
0640     msg->setContent(data);
0641     msg->parse();
0642     QCOMPARE(msg->parent(), (Content *)nullptr);
0643     QCOMPARE(msg->contents().at(0)->parent(), msg);
0644     QCOMPARE(msg->contents().at(1)->parent(), msg);
0645     delete msg;
0646 
0647     delete c1;
0648     delete c2;
0649     delete c5;
0650 }
0651 
0652 void ContentTest::testFreezing()
0653 {
0654     // Example taken from RFC 2046, section 5.1.1.
0655     QByteArray data =
0656         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0657         "To: Ned Freed <ned@innosoft.com>\n"
0658         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0659         "Subject: Sample message\n"
0660         "MIME-Version: 1.0\n"
0661         "Content-type: multipart/mixed; boundary=\"simple boundary\"\n"
0662         "\n"
0663         "This is the preamble.  It is to be ignored, though it\n"
0664         "is a handy place for composition agents to include an\n"
0665         "explanatory note to non-MIME conformant readers.\n"
0666         "\n"
0667         "--simple boundary\n"
0668         "\n"
0669         "This is implicitly typed plain US-ASCII text.\n"
0670         "It does NOT end with a linebreak.\n"
0671         "--simple boundary\n"
0672         "Content-type: text/plain; charset=us-ascii\n"
0673         "\n"
0674         "This is explicitly typed plain US-ASCII text.\n"
0675         "It DOES end with a linebreak.\n"
0676         "\n"
0677         "--simple boundary--\n"
0678         "\n"
0679         "This is the epilogue.  It is also to be ignored.\n";
0680 
0681     auto msg = new Message;
0682     msg->setContent(data);
0683     msg->setFrozen(true);
0684 
0685     // The data should be untouched before parsing.
0686     //qDebug() << "original data" << data;
0687     //qDebug() << "data from message" << msg->encodedContent();
0688     QCOMPARE(msg->encodedContent(), data);
0689 
0690     // The data should remain untouched after parsing.
0691     msg->parse();
0692     QVERIFY(msg->contentType()->isMultipart());
0693     QCOMPARE(msg->contents().count(), 2);
0694     QCOMPARE(msg->encodedContent(), data);
0695 
0696     // Calling assemble() should not alter the data.
0697     msg->assemble();
0698     QCOMPARE(msg->encodedContent(), data);
0699     delete msg;
0700 }
0701 
0702 void ContentTest::testContentTypeMimetype_data()
0703 {
0704     QTest::addColumn<QByteArray>("data");
0705     QTest::addColumn<QByteArray>("mimetype");
0706     QTest::addColumn<int>("contentCount");
0707     QTest::addColumn<QList<QByteArray> >("contentMimeType");
0708     QByteArray data =
0709         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0710         "To: Ned Freed <ned@innosoft.com>\n"
0711         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0712         "Subject: Sample message\n"
0713         "MIME-Version: 1.0\n"
0714         "Content-type: multipart/mixed; boundary=\"simple boundary\"\n"
0715         "\n"
0716         "This is the preamble.  It is to be ignored, though it\n"
0717         "is a handy place for composition agents to include an\n"
0718         "explanatory note to non-MIME conformant readers.\n"
0719         "\n"
0720         "--simple boundary\n"
0721         "\n"
0722         "This is implicitly typed plain US-ASCII text.\n"
0723         "It does NOT end with a linebreak.\n"
0724         "--simple boundary\n"
0725         "Content-type: text/plain; charset=us-ascii\n"
0726         "\n"
0727         "This is explicitly typed plain US-ASCII text.\n"
0728         "It DOES end with a linebreak.\n"
0729         "\n"
0730         "--simple boundary--\n"
0731         "\n"
0732         "This is the epilogue.  It is also to be ignored.\n";
0733 
0734     QList<QByteArray> contentMimes = { "text/plain", "text/plain"};
0735     QTest::newRow("multipart") << data << QByteArrayLiteral("multipart/mixed") << 2 << contentMimes;
0736 
0737     data =
0738             "From: Montel Laurent <null@kde.org>\n"
0739             "To: kde-commits@kde.org\n"
0740             "Content-Type: text/plain; charset=\"utf-8\"\n"
0741             "MIME-Version: 1.0\n"
0742             "Content-Transfer-Encoding: quoted-printable\n"
0743             "Subject: [libksieve] src/ksieveui: coding style\n"
0744             "Date: Tue, 30 May 2017 19:25:59 +0000\n"
0745             "\n"
0746             "Git commit 5410a2b3b6eef29ba32d0ac5e363fd38a56f535b by Montel Laurent.\n"
0747             "Committed on 30/05/2017 at 19:25.\n";
0748     QTest::newRow("text/plain") << data << QByteArrayLiteral("text/plain") << 0 << QList<QByteArray>();
0749 
0750     data =
0751             "From: Montel Laurent <null@kde.org>\n"
0752             "To: kde-commits@kde.org\n"
0753             "Content-Type: %22%22%22%22%22%22%22%22%22%22%22%22%22%22%22%22=?windows-1252?q?ap%22/pdf;\n"
0754             "name=\"file.pdf\"\n"
0755             "MIME-Version: 1.0\n"
0756             "Content-Transfer-Encoding: quoted-printable\n"
0757             "Subject: [libksieve] src/ksieveui: coding style\n"
0758             "Date: Tue, 30 May 2017 19:25:59 +0000\n"
0759             "\n"
0760             "Git commit 5410a2b3b6eef29ba32d0ac5e363fd38a56f535b by Montel Laurent.\n"
0761             "Committed on 30/05/2017 at 19:25.\n";
0762     QTest::newRow("broken") << data << QByteArrayLiteral("text/plain") << 0 << QList<QByteArray>();
0763 
0764     data =
0765             "From: Montel Laurent <null@kde.org>\n"
0766             "To: kde-commits@kde.org\n"
0767             "Content-Type: application/pdf;\n"
0768             "name=\"file.pdf\"\n"
0769             "MIME-Version: 1.0\n"
0770             "Content-Transfer-Encoding: quoted-printable\n"
0771             "Subject: [libksieve] src/ksieveui: coding style\n"
0772             "Date: Tue, 30 May 2017 19:25:59 +0000\n"
0773             "\n"
0774             "Git commit 5410a2b3b6eef29ba32d0ac5e363fd38a56f535b by Montel Laurent.\n"
0775             "Committed on 30/05/2017 at 19:25.\n";
0776     QTest::newRow("not broken") << data << QByteArrayLiteral("application/pdf") << 0 << QList<QByteArray>();
0777 
0778     data =
0779             "From kde@kde.org Fri Jan 29 19:19:26 2010\n"
0780             "Return-Path: <kde@kde.org>\n"
0781             "Delivered-To: kde@kde.org\n"
0782             "Received: from localhost (localhost [127.0.0.1])\n"
0783             "        by mail.kde.org (Postfix) with ESMTP id 8A6FF2B23D\n"
0784             "        for <kde@kde.org>; Fri, 29 Jan 2010 19:19:27 +0100 (CET)\n"
0785             "From: KDE <kde@kde.org>\n"
0786             "To: kde@kde.org\n"
0787             "Subject: Attachment test Outlook\n"
0788             "Date: Fri, 29 Jan 2010 19:19:26 +0100\n"
0789             "User-Agent: KMail/1.13.0 (Linux/2.6.32-gentoo-r1; KDE/4.3.95; x86_64; ; )\n"
0790             "MIME-Version: 1.0\n"
0791             "Content-Type: Multipart/Mixed;\n"
0792             "  boundary=\"Boundary-00=_uayYLfn6pjD7iFW\"\n"
0793             "Message-Id: <201001291919.26518.kde@kde.org>\n"
0794             "X-Length: 10467\n"
0795             "X-UID: 17\n"
0796             "\n"
0797             "--Boundary-00=_uayYLfn6pjD7iFW\n"
0798             "Content-Type: Text/Plain;\n"
0799             "  charset=\"us-ascii\"\n"
0800             "Content-Transfer-Encoding: 7bit\n"
0801             "\n"
0802             "Attachment test Outlook\n"
0803             "\n"
0804             "--Boundary-00=_uayYLfn6pjD7iFW\n"
0805             "Content-Type: text/x-patch;\n"
0806             "  charset=\"UTF-8\";\n"
0807             "  name*=UTF-8''%C3%A5%2Ediff\n"
0808             "Content-Transfer-Encoding: 7bit\n"
0809             "Content-Disposition: attachment;\n"
0810             "  filename=\"=?iso-8859-1?q?=E5=2Ediff?=\"\n"
0811             "\n"
0812             "Index: kmime/kmime_headers_p.h\n"
0813             "===================================================================\n"
0814             "--- kmime/kmime_headers_p.h     (revision 1068282)\n"
0815             "+++ kmime/kmime_headers_p.h     (working copy)\n"
0816             "@@ -170,6 +170,7 @@\n"
0817             "     int lines;\n"
0818             " };\n"
0819             "\n"
0820             "+kmime_mk_empty_private( ContentID, Generics::SingleIdent )\n"
0821             " }\n"
0822             "\n"
0823             " }\n"
0824             "\n"
0825             "--Boundary-00=_uayYLfn6pjD7iFW--";
0826     contentMimes = { "text/plain", "text/x-patch"};
0827     QTest::newRow("example1") << data << QByteArrayLiteral("multipart/mixed") << 2 << contentMimes;
0828 }
0829 
0830 
0831 void ContentTest::testContentTypeMimetype()
0832 {
0833     QFETCH(QByteArray, data);
0834     QFETCH(QByteArray, mimetype);
0835     QFETCH(int, contentCount);
0836     QFETCH(QList<QByteArray> , contentMimeType);
0837 
0838     // test parsing
0839     Message msg;
0840     msg.setContent(data);
0841     msg.parse();
0842     //QEXPECT_FAIL("broken", "Problem with content type", Continue);
0843     QCOMPARE(msg.contentType(false)->mimeType(), mimetype);
0844     QCOMPARE(msg.contents().count(), contentCount);
0845     for (int i = 0; i < msg.contents().count(); ++i) {
0846         QVERIFY(msg.contents().at(i)->contentType(false));
0847         QCOMPARE(contentMimeType.count(), contentCount);
0848         QCOMPARE(msg.contents().at(i)->contentType(false)->mimeType(), contentMimeType.at(i));
0849     }
0850 }
0851 
0852 #include "moc_contenttest.cpp"