File indexing completed on 2024-04-14 05:12:18

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() &&
0255                 line != QLatin1StringView("body"));
0256         // The test should be (after the expected failure disappears):
0257         //QVERIFY( line.length() < 998 );
0258     }
0259     delete msg;
0260 
0261 }
0262 
0263 void ContentTest::testDecodedContent()
0264 {
0265     auto c = new Content();
0266     c->setBody("\0");
0267     QVERIFY(c->decodedContent() == QByteArray());
0268     c->setBody(QByteArray());
0269     QVERIFY(c->decodedContent() == QByteArray());
0270     c->setBody(" ");
0271     QVERIFY(c->decodedContent() == QByteArray(" "));
0272     delete c;
0273 }
0274 
0275 void ContentTest::testMultipleHeaderExtraction()
0276 {
0277     QByteArray data =
0278         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0279         "To: Ned Freed <ned@innosoft.com>\n"
0280         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0281         "Subject: Sample message\n"
0282         "Received: from ktown.kde.org ([192.168.100.1])\n"
0283         "Received: from dev1.kde.org ([192.168.100.2])\n"
0284         "\t by ktown.kde.org ([192.168.100.1])\n"
0285         "Received: from dev2.kde.org ([192.168.100.3])\n"
0286         "           by ktown.kde.org ([192.168.100.1])\n";
0287 
0288     auto msg = new Message();
0289     msg->setContent(data);
0290     // FAILS identically to ContentTest::testMultipartMixed
0291     //  QCOMPARE( msg->encodedContent(), data );
0292     msg->parse();
0293 
0294     auto result = msg->headersByType("Received");
0295     QCOMPARE(result.count(), 3);
0296     QCOMPARE(result[0]->asUnicodeString(),  QString::fromLatin1("from ktown.kde.org ([192.168.100.1])"));
0297     QCOMPARE(result[1]->asUnicodeString(),  QString::fromLatin1("from dev1.kde.org ([192.168.100.2]) by ktown.kde.org ([192.168.100.1])"));
0298     QCOMPARE(result[2]->asUnicodeString(),  QString::fromLatin1("from dev2.kde.org ([192.168.100.3]) by ktown.kde.org ([192.168.100.1])"));
0299     delete msg;
0300 }
0301 
0302 void ContentTest::testMultipartMixed()
0303 {
0304     // example taken from RFC 2046, section 5.1.1.
0305     QByteArray data =
0306         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0307         "To: Ned Freed <ned@innosoft.com>\n"
0308         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0309         "Subject: Sample message\n"
0310         "MIME-Version: 1.0\n"
0311         "Content-type: multipart/mixed; boundary=\"simple boundary\"\n"
0312         "\n"
0313         "This is the preamble.  It is to be ignored, though it\n"
0314         "is a handy place for composition agents to include an\n"
0315         "explanatory note to non-MIME conformant readers.\n"
0316         "\n"
0317         "--simple boundary\n"
0318         "\n"
0319         "This is implicitly typed plain US-ASCII text.\n"
0320         "It does NOT end with a linebreak.\n"
0321         "--simple boundary\n"
0322         "Content-type: text/plain; charset=us-ascii\n"
0323         "\n"
0324         "This is explicitly typed plain US-ASCII text.\n"
0325         "It DOES end with a linebreak.\n"
0326         "\n"
0327         "--simple boundary--\n"
0328         "\n"
0329         "This is the epilogue.  It is also to be ignored.\n";
0330 
0331     QByteArray part1 =
0332         "This is implicitly typed plain US-ASCII text.\n"
0333         "It does NOT end with a linebreak.";
0334 
0335     QByteArray part2 =
0336         "This is explicitly typed plain US-ASCII text.\n"
0337         "It DOES end with a linebreak.\n";
0338 
0339     // What we expect KMime to parse the above data into.
0340     QByteArray parsedWithPreambleAndEpilogue =
0341         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0342         "To: Ned Freed <ned@innosoft.com>\n"
0343         "Date: Sun, 21 Mar 1993 23:56:48 -0800\n"
0344         "Subject: Sample message\n"
0345         "MIME-Version: 1.0\n"
0346         "Content-Type: multipart/mixed; boundary=\"simple boundary\"\n"
0347         "\n"
0348         "This is the preamble.  It is to be ignored, though it\n"
0349         "is a handy place for composition agents to include an\n"
0350         "explanatory note to non-MIME conformant readers.\n"
0351         "\n"
0352         "--simple boundary\n"
0353         "Content-Type: text/plain; charset=\"us-ascii\"\n\n"
0354         "This is implicitly typed plain US-ASCII text.\n"
0355         "It does NOT end with a linebreak.\n"
0356         "--simple boundary\n"
0357         "Content-Type: text/plain; charset=\"us-ascii\"\n"
0358         "\n"
0359         "This is explicitly typed plain US-ASCII text.\n"
0360         "It DOES end with a linebreak.\n"
0361         "\n"
0362         "--simple boundary--\n"
0363         "\n"
0364         "This is the epilogue.  It is also to be ignored.\n";
0365 
0366     // What we expect KMime to assemble the above data into.
0367     QByteArray assembled =
0368         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0369         "To: Ned Freed <ned@innosoft.com>\n"
0370         "Date: Sun, 21 Mar 1993 23:56:48 -0800\n"
0371         "Subject: Sample message\n"
0372         "MIME-Version: 1.0\n"
0373         "Content-Type: multipart/mixed; boundary=\"simple boundary\"\n"
0374         "Content-Transfer-Encoding: 7Bit\n"
0375         "\n"
0376         "--simple boundary\n"
0377         "\n"
0378         "This is implicitly typed plain US-ASCII text.\n"
0379         "It does NOT end with a linebreak.\n"
0380         "--simple boundary\n"
0381         "Content-Type: text/plain; charset=\"us-ascii\"\n"
0382         "\n"
0383         "This is explicitly typed plain US-ASCII text.\n"
0384         "It DOES end with a linebreak.\n"
0385         "\n"
0386         "--simple boundary--\n";
0387 
0388     // test parsing
0389     auto msg = new Message();
0390     msg->setContent(data);
0391     QCOMPARE(msg->encodedContent(), data);
0392     msg->parse();
0393     QVERIFY(msg->contentType()->isMultipart());
0394 
0395     auto list = msg->contents();
0396     QCOMPARE(list.count(), 2);
0397     Content *c = list.takeFirst();
0398     QCOMPARE(c->body(), part1);
0399     c = list.takeFirst();
0400     QCOMPARE(c->body(), part2);
0401 
0402     // assemble again
0403     msg->assemble();
0404     //qDebug() << "expected assembled content" << parsedWithPreambleAndEpilogue;
0405     //qDebug() << "actual new encoded content" << msg->encodedContent();
0406     QCOMPARE(msg->encodedContent(), parsedWithPreambleAndEpilogue);
0407     delete msg;
0408 
0409     // assembling from scratch
0410     // (The headers have to be in the same order, as we compare with the above assembled.)
0411     msg = new Message();
0412     msg->from()->from7BitString("Nathaniel Borenstein <nsb@bellcore.com>");
0413     msg->to()->from7BitString("Ned Freed <ned@innosoft.com>");
0414     msg->date()->from7BitString("Sun, 21 Mar 1993 23:56:48 -0800 (PST)");
0415     msg->subject()->from7BitString("Sample message");
0416     // HACK to make MIME-Version appear before Content-Type, as in the expected message.
0417     auto header = new Headers::MIMEVersion;
0418     header->from7BitString("1.234");
0419     msg->setHeader(header);
0420     msg->contentType()->from7BitString("multipart/mixed");
0421     msg->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit);
0422 
0423     c = new Content();
0424     c->setBody(part1);
0425     msg->appendContent(c);
0426 
0427     c = new Content();
0428     c->setBody(part2);
0429     c->contentType()->setMimeType("text/plain");
0430     c->contentType()->setCharset("us-ascii");
0431     msg->appendContent(c);
0432     msg->contentType()->setBoundary("simple boundary");
0433 
0434     list = msg->contents();
0435     QCOMPARE(list.count(), 2);
0436     c = list.takeFirst();
0437     QCOMPARE(c->body(), part1);
0438     c = list.takeFirst();
0439     QCOMPARE(c->body(), part2);
0440 
0441     msg->assemble();
0442     //QByteArray encc = msg->encodedContent();
0443     //qDebug().noquote() << "expected assembled content" << assembled;
0444     //qDebug().noquote() << "actual encoded content" << encc;
0445     QCOMPARE(msg->encodedContent(), assembled);
0446     delete msg;
0447 }
0448 
0449 void ContentTest::testParsingUuencoded()
0450 {
0451     const QByteArray body =
0452         "This is a test message that should appears as a text/plain part\n"
0453         "once this message is parsed and convert to a MIME tree.\n"
0454         "\n"
0455         "\n";
0456 
0457     const QString imageName = QStringLiteral("Name of the encoded file (oxygen 22x22 kde.png)");
0458     const QByteArray imageBase64 =
0459         "\n"
0460         "iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\n"
0461         "AAADdgAAA3YBfdWCzAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAU4SURB\n"
0462         "VBgZjcFbiF1XGcDx/7fW2pdznTOXNJlJc4WWVK3RqiC2FOyTiGCs+lKkohWTIl5QWrAp9ckLKpYi\n"
0463         "KFjwodgXoRDMk1VECj7UpkIqUZuCTUycyWXOzJxz9tnXtdfnxNqnvvj7iaryNhGxgPBOAh/gLa+y\n"
0464         "S3kn3dXyP6KqyEe+1Rm6tSc6nYVHO+loOXYR1hisFYRAIOBljtecyPaItEMkXeK4S2QTVAxVOZ1t\n"
0465         "TzaeG6//9fTWuR9MnOxyx7/xzaWjB548cvgAUeyJbGDYj9mzPGJl1GdpocOwlxCCMs1qtrKSrZ2c\n"
0466         "ze0Z126O2ZkWSJoO0rDylUabREROOsCoG3z58JEDrK4NIFQMBz0WBl2G3ZReGiNi+debO6gKC3sH\n"
0467         "DAcxNu6QpF1GiwtsTzMm04wrVyeY7upngEcdYIy4pSgVJtmMYb+HmBiVGE9Eo47ZdsHJj3eJnOHp\n"
0468         "M3P6exbIJxmffr/ibMK58zN+M4nwlGCTPmAMu8QYKasCFYd1CWoSgkT4YGmCoWggTRLiOKH0UFTK\n"
0469         "A8csdx0ZcnBfl/PXIuJ+j253gBED3CEGDluxVtqgGBcTJCKIZboxJq9bssozLxqiKMJZS1G3LIct\n"
0470         "7nvfAs5FvPDSjHlnEbER3f4AsUZYG1rD2t3GGIu4GIhosUSzCd9/5HZOvKtldnmd7evbRM7hnEOz\n"
0471         "CV/8xCrOWv52qeKVGx0CBpUIF3cwxsLwdmtYPGSMtaLW0WIIKuStIXLCh9+9wE++fgfWV4jwX489\n"
0472         "fJQkMswr5ee/26EMgaaFVoW6VsRaGXWWrFnqWyPWSV0rrULlA7M45dd/uEHZwOlfvMGW6yAiiAhr\n"
0473         "KwkgGIEiL8jrmryuqWpPWbWItYTlNWvauGeNs8xLT9W2FFXDdGPMwb0pz569wsUqpqgbQADhmecu\n"
0474         "IgK91HHqY7cxz0um85zxrKAVMNYSbGKNtqkIhtB6xptTvntiyJnv3MVH71niT3+fUHvQ1vC2F1+v\n"
0475         "efHPm9xy33sXubtXsj3NaJqKNjSA0KePEVsqKEE9dZWTOBCUtg1sZoamhrYFVQWUphV+dPYml67l\n"
0476         "3PLtz99Jr8zxdYn3NSJKRoYxhQZ2+aZCteWhZy7yydOvceHNOXeuWNRbQmMIIaCqGGJcOuL0s5fJ\n"
0477         "S8+gY3j8U4fQ2hPqEg0BqQnCsUcGg7XjNxZXV1MbJQx6I1ZW9vPge4QHPrjM47/cwXZ6VFmBaEsy\n"
0478         "6GPqgqEtqJqWsmq4OpmT+Sl1XTHdHIemeG3ZML3RBu+1rkp8mROahqiYceL+RQ7eZvnewwusyoRh\n"
0479         "f8hgtMywmfPUQ0Oe+sI+WlJ0tIrrJjR1SdMUBO/Z2fhn61g/68PRe7UqC4JraDo1h3oVsW1440rD\n"
0480         "718uOfXgiL1LEIKiOsI5IY0CT36uzxO/KvF1TV3MqX1D8F6Z/8U7QEPr1WCpyzlVVXJuo+WrP7xE\n"
0481         "ke5neeUA55+/ytNfSxAnPPazEnVdPntvweV/52R5oK4KqiqnqhtQr1y50jpAQ1PmvbTfG493mE62\n"
0482         "oYV/+CWGgzFN8EQm5vo4RxWmLKBty09/65nPC6bZDjuTLeZZhrMJWs8rdjkghOmlF3x57NTy4hrX\n"
0483         "b65T5zl1WVAWc7LuhDTpcvLHFcYY6E7xTUNZ5eT5jFm2w3S6RWRT9oz2cXX9lT8Cragqsv9DK93F\n"
0484         "48/3995zf7e/J41dhDMWawQkoNriTYbXnMj2ibRLJF3iuEtkE1SEfL7VXLv00qs3Xz/zpWp84YKo\n"
0485         "KreIiANGwH5AAOH/o7xlE7gOeN31H1IDp2dl3tAoAAAAAElFTkSuQmCC\n"
0486         ;
0487 
0488     const QByteArray uuencodedMsg =
0489         "Path: news.example.net!not-for-mail\n"
0490         "From: Coin coin <meuh@example.net>\n"
0491         "Newsgroups: test.kmime.uuencoded\n"
0492         "Subject: Kmime test\n"
0493         "Date: Thu, 14 Apr 2005 20:12:47 -0700\n"
0494         "Message-ID: <xxxxxxxxxxxxxxxxxx@xxxxx.kmime.example.net>\n"
0495         "X-Newsreader: Forte Agent 2.0/32.640\n"
0496         "Lines: 1283\n"
0497         "Organization: Ament\n"
0498         "Xref: news.example.net test.kmime.uuencoded:4584\n"
0499         "\n"
0500         "This is a test message that should appears as a text/plain part\n"
0501         "once this message is parsed and convert to a MIME tree.\n"
0502         "\n"
0503         "begin 644 Name of the encoded file (oxygen 22x22 kde.png)\n"
0504         "MB5!.1PT*&@H````-24A$4@```!8````6\"`8```#$M&P[````!'-\"250(\"`@(\n"
0505         "M?`ADB`````EP2%ES```#=@```W8!?=6\"S````!ET15AT4V]F='=A<F4`=W=W\n"
0506         "M+FEN:W-C87!E+F]R9YON/!H```4X241!5!@9C<%;B%U7&<#Q_[?6VI=SG3.7\n"
0507         "M-)E)<X665*W1JB\"V%.R3B&\"L^E*DHA63(EY06K`I]<D+*I8B*%CPH=@7H1#,\n"
0508         "MDU5$\"C[4ID(J49N\"34R<R67.S)QS]MG7M=?GQ-JGOOC[B:KR-A&Q@/!.`A_@\n"
0509         "M+:^R2WDGW=7R/Z*JR$>^U1FZM2<ZG85'.^EH.781UABL%81`(.!ECM><R/:(\n"
0510         "MM$,D7>*X2V035`Q5.9UM3S:>&Z__]?36N1],G.QRQ[_QS:6C!YX\\<O@`4>R)\n"
0511         "M;&#8C]FS/&)EU&=IH<.PEQ\"\",LUJMK*2K9V<S>T9UVZ.V9D62)H.TK#RE4:;\n"
0512         "M1$1..L\"H&WSY\\)$#K*X-(%0,!ST6!EV&W91>&B-B^=>;.Z@*\"WL'#`<Q-NZ0\n"
0513         "MI%U&BPML3S,FTXPK5R>8[NIG@$<=8(RXI2@5)MF,8;^'F!B5&$]$HX[9=L')\n"
0514         "MCW>)G.'I,W/Z>Q;()QF??K_B;,*Y\\S-^,XGPE&\"3/F`,N\\08*:L\"%8=U\"6H2\n"
0515         "M@D3X8&F\"H6@@31+B.*'T4%3*`\\<L=QT9<G!?E_/7(N)^CVYW@!$#W\"$&#ENQ\n"
0516         "M5MJ@&!<3)\"*(9;HQ)J];LLHS+QJB*,)92U&W+(<M[GO?`LY%O/#2C'EG$;$1\n"
0517         "MW?X`L498&UK#VMW&&(NX&(AHL42S\"=]_Y'9.O*ME=GF=[>O;1,[AG$.S\"5_\\\n"
0518         "MQ\"K.6OYVJ>*5&QT\"!I4(%W<PQL+P=FM8/&2,M:+6T6((*N2M(7+\"A]^]P$^^\n"
0519         "M?@?65XCP7X\\]?)0D,LPKY>>_VZ$,@::%5H6Z5L1:&766K%GJ6R/625TKK4+E\n"
0520         "M`[,XY==_N$'9P.E?O,&6ZR`BB`AK*PD@&($B+\\CKFKRNJ6I/6;6(M83E-6O:\n"
0521         "MN&>-L\\Q+3]6V%%7#=&/,P;TISYZ]PL4JIJ@;0`#AF><N(@*]U''J8[<QSTNF\n"
0522         "M\\YSQK*`5,-82;&*-MJD(AM!ZQIM3OGMBR)GOW,5'[UGB3W^?4'O0UO\"V%U^O\n"
0523         "M>?'/F]QRWWL7N;M7LCW-:)J*-C2`T*>/$5LJ*$$]=963.!\"4M@UL9H:FAK8%\n"
0524         "M5064IA5^=/8FEZ[EW/+MS]])K\\SQ=8GW-2)*1H8QA09V^:9\"M>6A9R[RR=.O\n"
0525         "M<>'-.7>N6-1;0F,((:\"J&&)<.N+TLY?)2\\^@8WC\\4X?0VA/J$@T!J0G\"L4<&\n"
0526         "M@[7C-Q975U,;)0QZ(U96]O/@>X0'/KC,X[_<P79Z5%F!:$LRZ&/J@J$MJ)J6\n"
0527         "MLFJX.IF3^2EU73'='(>F>&W9,+W1!N^UKDI\\F1.:AJB8<>+^10[>9OG>PPNL\n"
0528         "MRH1A?\\A@M,RPF?/40T.>^L(^6E)TM(KK)C1U2=,4!._9V?AGZU@_Z\\/1>[4J\n"
0529         "M\"X)K:#HUAWH5L6UXXTK#[U\\N.?7@B+U+$(*B.L(Y(8T\"3WZNSQ._*O%U35W,\n"
0530         "MJ7U#\\%Z9_\\4[0$/KU6\"IRSE557)NH^6K/[Q$D>YG>>4`YY^_RM-?2Q`G//:S\n"
0531         "M$G5=/GMOP>5_YV1YH*X*JBJGJAM0KURYTCI`0U/FO;3?&X]WF$ZVH85_^\"6&\n"
0532         "M@S%-\\$0FYOHX1Q6F+*!MRT]_ZYG/\"Z;9#CN3+>99AK,)6L\\K=CD@A.FE%WQY\n"
0533         "M[-3RXAK7;ZY3YSEU65`6<[+NA#3I<O+'%<88Z$[Q34-9Y>3YC%FVPW2Z1613\n"
0534         "M]HSV<77]E3\\\"K:@JLO]#*]W%X\\_W]]YS?[>_)XU=A#,6:P0DH-KB38;7G,CV\n"
0535         "MB;1+)%WBN$MD$U2$?+[57+OTTJLW7S_SI6I\\X8*H*K>(B`-&P'Y``.'_H[QE\n"
0536         ";$[@.>-WU'U(#IV=EWM`H`````$E%3D2N0F\"\"\n"
0537         "`\n"
0538         "end\n"
0539         "\n";
0540 
0541     auto msg = new Message();
0542     msg->setContent(uuencodedMsg);
0543     msg->parse();
0544     auto contents = msg->contents();
0545 
0546     // text + image
0547     QCOMPARE(contents.size(), 2);
0548 
0549     Content *c = nullptr;
0550 
0551     // Check the first text part
0552     c = contents.at(0);
0553     QVERIFY(c->contentType()->isPlainText());
0554     QCOMPARE(c->body(), body);
0555 
0556     // Check the image part
0557     c = contents.at(1);
0558     QVERIFY(!c->contentType()->isText());
0559     QCOMPARE(c->contentType()->name(), imageName);
0560     // The uuencoded content as been recoded as base64
0561     QCOMPARE(c->encodedContent().data(), imageBase64.data());
0562 
0563     delete msg;
0564 }
0565 
0566 void ContentTest::testParent()
0567 {
0568     auto c1 = new Content();
0569     c1->contentType()->from7BitString("multipart/mixed");
0570 
0571     auto c2 = new Content();
0572     c2->contentType()->from7BitString("text/plain");
0573     c2->setBody("textpart");
0574 
0575     auto c3 = new Content();
0576     c3->contentType()->from7BitString("text/html");
0577     c3->setBody("htmlpart");
0578 
0579     auto c4 = new Content();
0580     c4->contentType()->from7BitString("text/html");
0581     c4->setBody("htmlpart2");
0582 
0583     auto c5 = new Content();
0584     c5->contentType()->from7BitString("multipart/mixed");
0585 
0586     //c2 doesn't have a parent yet
0587     QCOMPARE(c2->parent(), (Content *)nullptr);
0588 
0589     c1->appendContent(c2);
0590     c1->appendContent(c3);
0591     c1->appendContent(c4);
0592     QCOMPARE(c1->contents().size(), 3);
0593 
0594     // c1 is the parent of those
0595     QCOMPARE(c2->parent(), c1);
0596     QCOMPARE(c3->parent(), c1);
0597 
0598     //test removal
0599     c1->takeContent(c2);
0600     QCOMPARE(c2->parent(), (Content *)nullptr);
0601     QCOMPARE(c1->contents().at(0), c3);
0602     QCOMPARE(c1->contents().size(), 2);
0603 
0604 //check if the content is moved correctly to another parent
0605     c5->appendContent(c4);
0606     QCOMPARE(c4->parent(), c5);
0607     QCOMPARE(c1->contents().size(), 1);
0608     QCOMPARE(c1->contents().at(0), c3);
0609     QCOMPARE(c5->contents().size(), 1);
0610     QCOMPARE(c5->contents().at(0), c4);
0611 
0612     // example taken from RFC 2046, section 5.1.1.
0613     QByteArray data =
0614         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0615         "To: Ned Freed <ned@innosoft.com>\n"
0616         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0617         "Subject: Sample message\n"
0618         "MIME-Version: 1.0\n"
0619         "Content-type: multipart/mixed; boundary=\"simple boundary\"\n"
0620         "\n"
0621         "This is the preamble.  It is to be ignored, though it\n"
0622         "is a handy place for composition agents to include an\n"
0623         "explanatory note to non-MIME conformant readers.\n"
0624         "\n"
0625         "--simple boundary\n"
0626         "\n"
0627         "This is implicitly typed plain US-ASCII text.\n"
0628         "It does NOT end with a linebreak.\n"
0629         "--simple boundary\n"
0630         "Content-type: text/plain; charset=us-ascii\n"
0631         "\n"
0632         "This is explicitly typed plain US-ASCII text.\n"
0633         "It DOES end with a linebreak.\n"
0634         "\n"
0635         "--simple boundary--\n"
0636         "\n"
0637         "This is the epilogue.  It is also to be ignored.\n";
0638 
0639     // test parsing
0640     auto msg = new Message();
0641     msg->setContent(data);
0642     msg->parse();
0643     QCOMPARE(msg->parent(), (Content *)nullptr);
0644     QCOMPARE(msg->contents().at(0)->parent(), msg);
0645     QCOMPARE(msg->contents().at(1)->parent(), msg);
0646     delete msg;
0647 
0648     delete c1;
0649     delete c2;
0650     delete c5;
0651 }
0652 
0653 void ContentTest::testFreezing()
0654 {
0655     // Example taken from RFC 2046, section 5.1.1.
0656     QByteArray data =
0657         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0658         "To: Ned Freed <ned@innosoft.com>\n"
0659         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0660         "Subject: Sample message\n"
0661         "MIME-Version: 1.0\n"
0662         "Content-type: multipart/mixed; boundary=\"simple boundary\"\n"
0663         "\n"
0664         "This is the preamble.  It is to be ignored, though it\n"
0665         "is a handy place for composition agents to include an\n"
0666         "explanatory note to non-MIME conformant readers.\n"
0667         "\n"
0668         "--simple boundary\n"
0669         "\n"
0670         "This is implicitly typed plain US-ASCII text.\n"
0671         "It does NOT end with a linebreak.\n"
0672         "--simple boundary\n"
0673         "Content-type: text/plain; charset=us-ascii\n"
0674         "\n"
0675         "This is explicitly typed plain US-ASCII text.\n"
0676         "It DOES end with a linebreak.\n"
0677         "\n"
0678         "--simple boundary--\n"
0679         "\n"
0680         "This is the epilogue.  It is also to be ignored.\n";
0681 
0682     auto msg = new Message;
0683     msg->setContent(data);
0684     msg->setFrozen(true);
0685 
0686     // The data should be untouched before parsing.
0687     //qDebug() << "original data" << data;
0688     //qDebug() << "data from message" << msg->encodedContent();
0689     QCOMPARE(msg->encodedContent(), data);
0690 
0691     // The data should remain untouched after parsing.
0692     msg->parse();
0693     QVERIFY(msg->contentType()->isMultipart());
0694     QCOMPARE(msg->contents().count(), 2);
0695     QCOMPARE(msg->encodedContent(), data);
0696 
0697     // Calling assemble() should not alter the data.
0698     msg->assemble();
0699     QCOMPARE(msg->encodedContent(), data);
0700     delete msg;
0701 }
0702 
0703 void ContentTest::testContentTypeMimetype_data()
0704 {
0705     QTest::addColumn<QByteArray>("data");
0706     QTest::addColumn<QByteArray>("mimetype");
0707     QTest::addColumn<int>("contentCount");
0708     QTest::addColumn<QList<QByteArray> >("contentMimeType");
0709     QByteArray data =
0710         "From: Nathaniel Borenstein <nsb@bellcore.com>\n"
0711         "To: Ned Freed <ned@innosoft.com>\n"
0712         "Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\n"
0713         "Subject: Sample message\n"
0714         "MIME-Version: 1.0\n"
0715         "Content-type: multipart/mixed; boundary=\"simple boundary\"\n"
0716         "\n"
0717         "This is the preamble.  It is to be ignored, though it\n"
0718         "is a handy place for composition agents to include an\n"
0719         "explanatory note to non-MIME conformant readers.\n"
0720         "\n"
0721         "--simple boundary\n"
0722         "\n"
0723         "This is implicitly typed plain US-ASCII text.\n"
0724         "It does NOT end with a linebreak.\n"
0725         "--simple boundary\n"
0726         "Content-type: text/plain; charset=us-ascii\n"
0727         "\n"
0728         "This is explicitly typed plain US-ASCII text.\n"
0729         "It DOES end with a linebreak.\n"
0730         "\n"
0731         "--simple boundary--\n"
0732         "\n"
0733         "This is the epilogue.  It is also to be ignored.\n";
0734 
0735     QList<QByteArray> contentMimes = { "text/plain", "text/plain"};
0736     QTest::newRow("multipart") << data << QByteArrayLiteral("multipart/mixed") << 2 << contentMimes;
0737 
0738     data =
0739             "From: Montel Laurent <null@kde.org>\n"
0740             "To: kde-commits@kde.org\n"
0741             "Content-Type: text/plain; charset=\"utf-8\"\n"
0742             "MIME-Version: 1.0\n"
0743             "Content-Transfer-Encoding: quoted-printable\n"
0744             "Subject: [libksieve] src/ksieveui: coding style\n"
0745             "Date: Tue, 30 May 2017 19:25:59 +0000\n"
0746             "\n"
0747             "Git commit 5410a2b3b6eef29ba32d0ac5e363fd38a56f535b by Montel Laurent.\n"
0748             "Committed on 30/05/2017 at 19:25.\n";
0749     QTest::newRow("text/plain") << data << QByteArrayLiteral("text/plain") << 0 << QList<QByteArray>();
0750 
0751     data =
0752             "From: Montel Laurent <null@kde.org>\n"
0753             "To: kde-commits@kde.org\n"
0754             "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"
0755             "name=\"file.pdf\"\n"
0756             "MIME-Version: 1.0\n"
0757             "Content-Transfer-Encoding: quoted-printable\n"
0758             "Subject: [libksieve] src/ksieveui: coding style\n"
0759             "Date: Tue, 30 May 2017 19:25:59 +0000\n"
0760             "\n"
0761             "Git commit 5410a2b3b6eef29ba32d0ac5e363fd38a56f535b by Montel Laurent.\n"
0762             "Committed on 30/05/2017 at 19:25.\n";
0763     QTest::newRow("broken") << data << QByteArrayLiteral("text/plain") << 0 << QList<QByteArray>();
0764 
0765     data =
0766             "From: Montel Laurent <null@kde.org>\n"
0767             "To: kde-commits@kde.org\n"
0768             "Content-Type: application/pdf;\n"
0769             "name=\"file.pdf\"\n"
0770             "MIME-Version: 1.0\n"
0771             "Content-Transfer-Encoding: quoted-printable\n"
0772             "Subject: [libksieve] src/ksieveui: coding style\n"
0773             "Date: Tue, 30 May 2017 19:25:59 +0000\n"
0774             "\n"
0775             "Git commit 5410a2b3b6eef29ba32d0ac5e363fd38a56f535b by Montel Laurent.\n"
0776             "Committed on 30/05/2017 at 19:25.\n";
0777     QTest::newRow("not broken") << data << QByteArrayLiteral("application/pdf") << 0 << QList<QByteArray>();
0778 
0779     data =
0780             "From kde@kde.org Fri Jan 29 19:19:26 2010\n"
0781             "Return-Path: <kde@kde.org>\n"
0782             "Delivered-To: kde@kde.org\n"
0783             "Received: from localhost (localhost [127.0.0.1])\n"
0784             "        by mail.kde.org (Postfix) with ESMTP id 8A6FF2B23D\n"
0785             "        for <kde@kde.org>; Fri, 29 Jan 2010 19:19:27 +0100 (CET)\n"
0786             "From: KDE <kde@kde.org>\n"
0787             "To: kde@kde.org\n"
0788             "Subject: Attachment test Outlook\n"
0789             "Date: Fri, 29 Jan 2010 19:19:26 +0100\n"
0790             "User-Agent: KMail/1.13.0 (Linux/2.6.32-gentoo-r1; KDE/4.3.95; x86_64; ; )\n"
0791             "MIME-Version: 1.0\n"
0792             "Content-Type: Multipart/Mixed;\n"
0793             "  boundary=\"Boundary-00=_uayYLfn6pjD7iFW\"\n"
0794             "Message-Id: <201001291919.26518.kde@kde.org>\n"
0795             "X-Length: 10467\n"
0796             "X-UID: 17\n"
0797             "\n"
0798             "--Boundary-00=_uayYLfn6pjD7iFW\n"
0799             "Content-Type: Text/Plain;\n"
0800             "  charset=\"us-ascii\"\n"
0801             "Content-Transfer-Encoding: 7bit\n"
0802             "\n"
0803             "Attachment test Outlook\n"
0804             "\n"
0805             "--Boundary-00=_uayYLfn6pjD7iFW\n"
0806             "Content-Type: text/x-patch;\n"
0807             "  charset=\"UTF-8\";\n"
0808             "  name*=UTF-8''%C3%A5%2Ediff\n"
0809             "Content-Transfer-Encoding: 7bit\n"
0810             "Content-Disposition: attachment;\n"
0811             "  filename=\"=?iso-8859-1?q?=E5=2Ediff?=\"\n"
0812             "\n"
0813             "Index: kmime/kmime_headers_p.h\n"
0814             "===================================================================\n"
0815             "--- kmime/kmime_headers_p.h     (revision 1068282)\n"
0816             "+++ kmime/kmime_headers_p.h     (working copy)\n"
0817             "@@ -170,6 +170,7 @@\n"
0818             "     int lines;\n"
0819             " };\n"
0820             "\n"
0821             "+kmime_mk_empty_private( ContentID, Generics::SingleIdent )\n"
0822             " }\n"
0823             "\n"
0824             " }\n"
0825             "\n"
0826             "--Boundary-00=_uayYLfn6pjD7iFW--";
0827     contentMimes = { "text/plain", "text/x-patch"};
0828     QTest::newRow("example1") << data << QByteArrayLiteral("multipart/mixed") << 2 << contentMimes;
0829 }
0830 
0831 
0832 void ContentTest::testContentTypeMimetype()
0833 {
0834     QFETCH(QByteArray, data);
0835     QFETCH(QByteArray, mimetype);
0836     QFETCH(int, contentCount);
0837     QFETCH(QList<QByteArray> , contentMimeType);
0838 
0839     // test parsing
0840     Message msg;
0841     msg.setContent(data);
0842     msg.parse();
0843     //QEXPECT_FAIL("broken", "Problem with content type", Continue);
0844     QCOMPARE(msg.contentType(false)->mimeType(), mimetype);
0845     QCOMPARE(msg.contents().count(), contentCount);
0846     for (int i = 0; i < msg.contents().count(); ++i) {
0847         QVERIFY(msg.contents().at(i)->contentType(false));
0848         QCOMPARE(contentMimeType.count(), contentCount);
0849         QCOMPARE(msg.contents().at(i)->contentType(false)->mimeType(), contentMimeType.at(i));
0850     }
0851 }
0852 
0853 #include "moc_contenttest.cpp"