File indexing completed on 2024-12-22 05:07:43

0001 // SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
0002 // SPDX-License-Identifier: GPL-2.0-or-later
0003 
0004 #include <QDebug>
0005 #include <QDir>
0006 #include <QSignalSpy>
0007 #include <QStandardPaths>
0008 #include <QTest>
0009 #include <functional>
0010 
0011 #include "../mailcrypto.h"
0012 #include "../mailtemplates.h"
0013 
0014 static KMime::Content *getSubpart(KMime::Content *msg, const QByteArray &mimeType)
0015 {
0016     for (const auto c : msg->contents()) {
0017         if (c->contentType(false)->mimeType() == mimeType) {
0018             return c;
0019         }
0020     }
0021     return nullptr;
0022 }
0023 
0024 static QByteArray readMailFromFile(const QString &mailFile)
0025 {
0026     Q_ASSERT(!QString::fromLatin1(MAIL_DATA_DIR).isEmpty());
0027     QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile);
0028     file.open(QIODevice::ReadOnly);
0029     Q_ASSERT(file.isOpen());
0030     return file.readAll();
0031 }
0032 
0033 static KMime::Message::Ptr readMail(const QString &mailFile)
0034 {
0035     auto msg = KMime::Message::Ptr::create();
0036     msg->setContent(readMailFromFile(mailFile));
0037     msg->parse();
0038     return msg;
0039 }
0040 
0041 static QString removeFirstLine(const QString &s)
0042 {
0043     return s.mid(s.indexOf(QLatin1String("\n")) + 1);
0044 }
0045 
0046 static QString normalize(const QString &s)
0047 {
0048     auto text = s;
0049     text.replace(QLatin1String(">"), QString());
0050     text.replace(QLatin1String("\n"), QString());
0051     text.replace(QLatin1String("="), QString());
0052     text.replace(QLatin1String(" "), QString());
0053     return text;
0054 }
0055 
0056 static QString unquote(const QString &s)
0057 {
0058     auto text = s;
0059     text.replace(QLatin1String("> "), QString());
0060     return text;
0061 }
0062 
0063 class MailTemplateTest : public QObject
0064 {
0065     Q_OBJECT
0066 
0067     bool validate(KMime::Message::Ptr msg)
0068     {
0069         const auto data = msg->encodedContent();
0070         // IMAP compat: The ASCII NUL character, %x00, MUST NOT be used at any time.
0071         if (data.contains('\0')) {
0072             return false;
0073         }
0074         return true;
0075     }
0076 
0077 private Q_SLOTS:
0078 
0079     // Ensures we don't crash on garbage
0080     void testEmpty()
0081     {
0082         MailTemplates::reply(KMime::Message::Ptr::create(), [&](const KMime::Message::Ptr &) {});
0083     }
0084 
0085     void testPlainReply()
0086     {
0087         auto msg = readMail(QLatin1String("plaintext.mbox"));
0088         KMime::Message::Ptr result;
0089         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0090             result = r;
0091         });
0092         QTRY_VERIFY(result);
0093         QCOMPARE(normalize(removeFirstLine(QString::fromUtf8(result->body()))), normalize(QString::fromUtf8(msg->body())));
0094         QCOMPARE(result->to()->addresses(), {{"konqi@example.org"}});
0095         QCOMPARE(result->subject()->asUnicodeString(), QLatin1String{"RE: A random subject with alternative contenttype"});
0096     }
0097 
0098     void testHtmlReply()
0099     {
0100         auto msg = readMail(QLatin1String("html.mbox"));
0101         KMime::Message::Ptr result;
0102         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0103             result = r;
0104         });
0105         QTRY_VERIFY(result);
0106         QCOMPARE(unquote(removeFirstLine(QString::fromUtf8(result->body()))), QLatin1String("HTML text"));
0107     }
0108 
0109     void testStripSignatureReply()
0110     {
0111         auto msg = readMail(QLatin1String("plaintext-with-signature.mbox"));
0112         KMime::Message::Ptr result;
0113         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0114             result = r;
0115         });
0116         QTRY_VERIFY(result);
0117         QVERIFY(!result->body().contains("This is a signature"));
0118     }
0119 
0120     void testStripSignatureHtmlReply()
0121     {
0122         auto msg = readMail(QLatin1String("html-with-signature.mbox"));
0123         KMime::Message::Ptr result;
0124         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0125             result = r;
0126         });
0127         QTRY_VERIFY(result);
0128         QVERIFY(!result->body().contains("This is a signature"));
0129     }
0130 
0131     // We can't test this because we can't commit a CRLF file due to commit hooks.
0132     //  void testStripSignatureCrlfReply()
0133     //  {
0134     //      auto msg = readMail("crlf-with-signature.mbox");
0135     //      KMime::Message::Ptr result;
0136     //      MailTemplates::reply(msg, [&] (const KMime::Message::Ptr &r) {
0137     //          result = r;
0138     //      });
0139     //      QTRY_VERIFY(result);
0140     //      QVERIFY(!result->body().contains("This is a signature"));
0141     //  }
0142 
0143     void testStripEncryptedCRLFReply()
0144     {
0145         auto msg = readMail(QLatin1String("crlf-encrypted-with-signature.mbox"));
0146         KMime::Message::Ptr result;
0147         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0148             result = r;
0149         });
0150         QTRY_VERIFY(result);
0151         QVERIFY(!result->body().contains("This is a signature"));
0152     }
0153 
0154     void testHtml8BitEncodedReply()
0155     {
0156         auto msg = readMail(QLatin1String("8bitencoded.mbox"));
0157         KMime::Message::Ptr result;
0158         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0159             result = r;
0160         });
0161         QTRY_VERIFY(result);
0162         QVERIFY(MailTemplates::plaintextContent(result).contains(QString::fromUtf8("Why Pisa’s Tower")));
0163     }
0164 
0165     void testMultipartSignedReply()
0166     {
0167         auto msg = readMail(QLatin1String("openpgp-signed-mailinglist.mbox"));
0168         KMime::Message::Ptr result;
0169         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0170             result = r;
0171         });
0172         QTRY_VERIFY(result);
0173         auto content = removeFirstLine(QString::fromUtf8(result->body()));
0174         QVERIFY(!content.isEmpty());
0175         QVERIFY(content.contains(QLatin1String("i noticed a new branch")));
0176     }
0177 
0178     void testMultipartAlternativeReply()
0179     {
0180         auto msg = readMail(QLatin1String("alternative.mbox"));
0181         KMime::Message::Ptr result;
0182         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0183             result = r;
0184         });
0185         QTRY_VERIFY(result);
0186         auto content = removeFirstLine(QString::fromUtf8(result->body()));
0187         QVERIFY(!content.isEmpty());
0188         QCOMPARE(unquote(content),
0189                  QLatin1String("If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to "
0190                                "view the newsletter on our website: http://www.gog.com/newsletter/\n"));
0191     }
0192 
0193     void testAttachmentReply()
0194     {
0195         auto msg = readMail(QLatin1String("plaintextattachment.mbox"));
0196         KMime::Message::Ptr result;
0197         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0198             result = r;
0199         });
0200         QTRY_VERIFY(result);
0201         auto content = removeFirstLine(QString::fromUtf8(result->body()));
0202         QVERIFY(!content.isEmpty());
0203         QCOMPARE(unquote(content), QLatin1String("sdlkjsdjf"));
0204     }
0205 
0206     void testMultiRecipientReply()
0207     {
0208         auto msg = readMail(QLatin1String("multirecipients.mbox"));
0209         KMime::Message::Ptr result;
0210         MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
0211             result = r;
0212         });
0213         QTRY_VERIFY(result);
0214         auto content = removeFirstLine(QString::fromUtf8(result->body()));
0215         QVERIFY(!content.isEmpty());
0216         QCOMPARE(unquote(content), QLatin1String("test"));
0217         QCOMPARE(result->to()->addresses(), {{"from@example.org"}});
0218         auto l = QVector<QByteArray>{{"to1@example.org"}, {"to2@example.org"}, {"cc1@example.org"}, {"cc2@example.org"}};
0219         QCOMPARE(result->cc()->addresses(), l);
0220     }
0221 
0222     void testMultiRecipientReplyFilteringMe()
0223     {
0224         KMime::Types::AddrSpecList me;
0225         KMime::Types::Mailbox mb;
0226         mb.setAddress("to1@example.org");
0227         me << mb.addrSpec();
0228 
0229         auto msg = readMail(QLatin1String("multirecipients.mbox"));
0230         KMime::Message::Ptr result;
0231         MailTemplates::reply(
0232             msg,
0233             [&](const KMime::Message::Ptr &r) {
0234                 result = r;
0235             },
0236             me);
0237         QTRY_VERIFY(result);
0238         QCOMPARE(result->to()->addresses(), {{"from@example.org"}});
0239         auto l = QVector<QByteArray>{{"to2@example.org"}, {"cc1@example.org"}, {"cc2@example.org"}};
0240         QCOMPARE(result->cc()->addresses(), l);
0241     }
0242 
0243     void testMultiRecipientReplyOwnMessage()
0244     {
0245         KMime::Types::AddrSpecList me;
0246         KMime::Types::Mailbox mb;
0247         mb.setAddress("from@example.org");
0248         me << mb.addrSpec();
0249 
0250         auto msg = readMail(QLatin1String("multirecipients.mbox"));
0251         KMime::Message::Ptr result;
0252         MailTemplates::reply(
0253             msg,
0254             [&](const KMime::Message::Ptr &r) {
0255                 result = r;
0256             },
0257             me);
0258         QTRY_VERIFY(result);
0259 
0260         auto to = QVector<QByteArray>{{"to1@example.org"}, {"to2@example.org"}};
0261         QCOMPARE(result->to()->addresses(), to);
0262         auto cc = QVector<QByteArray>{{"cc1@example.org"}, {"cc2@example.org"}};
0263         QCOMPARE(result->cc()->addresses(), cc);
0264     }
0265 
0266     void testReplyList()
0267     {
0268         KMime::Types::AddrSpecList me;
0269         KMime::Types::Mailbox mb;
0270         mb.setAddress("me@example.org");
0271         me << mb.addrSpec();
0272 
0273         auto msg = readMail(QLatin1String("listmessage.mbox"));
0274         KMime::Message::Ptr result;
0275         MailTemplates::reply(
0276             msg,
0277             [&](const KMime::Message::Ptr &r) {
0278                 result = r;
0279             },
0280             me);
0281         QTRY_VERIFY(result);
0282 
0283         auto to = QVector<QByteArray>{{"list@example.org"}};
0284         QCOMPARE(result->to()->addresses(), to);
0285         auto cc = QVector<QByteArray>{{"to@example.org"}, {"cc1@example.org"}};
0286         QCOMPARE(result->cc()->addresses(), cc);
0287     }
0288 
0289     void testForwardAsAttachment()
0290     {
0291         auto msg = readMail(QString::fromUtf8("plaintext.mbox"));
0292         KMime::Message::Ptr result;
0293         MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
0294             result = r;
0295         });
0296         QTRY_VERIFY(result);
0297         QCOMPARE(result->subject(false)->asUnicodeString(), QLatin1String{"FW: A random subject with alternative contenttype"});
0298         QCOMPARE(result->to()->addresses(), {});
0299         QCOMPARE(result->cc()->addresses(), {});
0300         QCOMPARE(result->references()->identifiers(), {"1505824.VT1nqpAGu0@vkpc5"});
0301         QCOMPARE(result->inReplyTo()->identifiers(), {});
0302 
0303         auto attachments = result->attachments();
0304         QCOMPARE(attachments.size(), 1);
0305         auto attachment = attachments[0];
0306         QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline);
0307         QCOMPARE(attachment->contentDisposition(false)->filename(), QLatin1String{"A random subject with alternative contenttype.eml"});
0308         QVERIFY(attachment->bodyIsMessage());
0309 
0310         attachment->parse();
0311         auto origMsg = attachment->bodyAsMessage();
0312         QCOMPARE(origMsg->subject(false)->asUnicodeString(), QLatin1String{"A random subject with alternative contenttype"});
0313     }
0314 
0315     void testEncryptedForwardAsAttachment()
0316     {
0317         auto msg = readMail(QLatin1String("openpgp-encrypted.mbox"));
0318         KMime::Message::Ptr result;
0319         MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
0320             result = r;
0321         });
0322         QTRY_VERIFY(result);
0323         QCOMPARE(result->subject(false)->asUnicodeString(), QLatin1String{"FW: OpenPGP encrypted"});
0324         QCOMPARE(result->to()->addresses(), {});
0325         QCOMPARE(result->cc()->addresses(), {});
0326         QCOMPARE(result->references()->identifiers(), {"1505824.VT2nqpAGu0@vkpc5"});
0327         QCOMPARE(result->inReplyTo()->identifiers(), {});
0328 
0329         auto attachments = result->attachments();
0330         QCOMPARE(attachments.size(), 1);
0331         auto attachment = attachments[0];
0332         QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline);
0333         QCOMPARE(attachment->contentDisposition(false)->filename(), QLatin1String{"OpenPGP encrypted.eml"});
0334         QVERIFY(attachment->bodyIsMessage());
0335 
0336         attachment->parse();
0337         auto origMsg = attachment->bodyAsMessage();
0338         QCOMPARE(origMsg->subject(false)->asUnicodeString(), QLatin1String{"OpenPGP encrypted"});
0339     }
0340 
0341     void testEncryptedWithAttachmentsForwardAsAttachment()
0342     {
0343         auto msg = readMail(QLatin1String("openpgp-encrypted-two-attachments.mbox"));
0344         KMime::Message::Ptr result;
0345         MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
0346             result = r;
0347         });
0348         QTRY_VERIFY(result);
0349         QCOMPARE(result->subject(false)->asUnicodeString(), QLatin1String{"FW: OpenPGP encrypted with 2 text attachments"});
0350         QCOMPARE(result->to()->addresses(), {});
0351         QCOMPARE(result->cc()->addresses(), {});
0352         QCOMPARE(result->references()->identifiers(), {"1505824.VT0nqpAGu0@vkpc5"});
0353         QCOMPARE(result->inReplyTo()->identifiers(), {});
0354 
0355         auto attachments = result->attachments();
0356         QCOMPARE(attachments.size(), 1);
0357         auto attachment = attachments[0];
0358         QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline);
0359         QCOMPARE(attachment->contentDisposition(false)->filename(), QLatin1String{"OpenPGP encrypted with 2 text attachments.eml"});
0360         QVERIFY(attachment->bodyIsMessage());
0361 
0362         attachment->parse();
0363         auto origMsg = attachment->bodyAsMessage();
0364         QCOMPARE(origMsg->subject(false)->asUnicodeString(), QLatin1String{"OpenPGP encrypted with 2 text attachments"});
0365 
0366         auto attattachments = origMsg->attachments();
0367         QCOMPARE(attattachments.size(), 2);
0368         QCOMPARE(attattachments[0]->contentDisposition(false)->filename(), QLatin1String{"attachment1.txt"});
0369         QCOMPARE(attattachments[1]->contentDisposition(false)->filename(), QLatin1String{"attachment2.txt"});
0370     }
0371 
0372     void testForwardAlreadyForwarded()
0373     {
0374         auto msg = readMail(QLatin1String("cid-links-forwarded-inline.mbox"));
0375         KMime::Message::Ptr result;
0376         MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
0377             result = r;
0378         });
0379         QTRY_VERIFY(result);
0380         QCOMPARE(result->subject(false)->asUnicodeString(), QLatin1String{"FW: Html Hello (inlin)"});
0381         QCOMPARE(result->to()->addresses(), {});
0382         QCOMPARE(result->cc()->addresses(), {});
0383         const QVector references{QByteArray{"a1777ec781546ccc5dcd4918a5e4e03d@info"}, QByteArray{"46b164308eb6056361c866932a740a3c@info"}};
0384         QCOMPARE(result->references()->identifiers(), references);
0385         QCOMPARE(result->inReplyTo()->identifiers(), {});
0386     }
0387 
0388     void testCreatePlainMail()
0389     {
0390         QStringList to = {{QLatin1String("to@example.org")}};
0391         QStringList cc = {{QLatin1String("cc@example.org")}};
0392         QStringList bcc = {{QLatin1String("bcc@example.org")}};
0393 
0394         KMime::Types::Mailbox from;
0395         from.fromUnicodeString(QLatin1String("from@example.org"));
0396         QString subject = QLatin1String("subject");
0397         QString body = QLatin1String("body");
0398         QList<Attachment> attachments;
0399 
0400         auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments);
0401 
0402         QVERIFY(result);
0403         QVERIFY(validate(result));
0404         QCOMPARE(result->subject()->asUnicodeString(), subject);
0405         QCOMPARE(result->body(), body.toUtf8());
0406         QVERIFY(result->date(false)->dateTime().isValid());
0407         QVERIFY(result->contentType()->isMimeType("text/plain"));
0408         QVERIFY(result->messageID(false) && !result->messageID(false)->isEmpty());
0409     }
0410 
0411     void testCreateHtmlMail()
0412     {
0413         QStringList to = {{QLatin1String("to@example.org")}};
0414         QStringList cc = {{QLatin1String("cc@example.org")}};
0415         QStringList bcc = {{QLatin1String("bcc@example.org")}};
0416 
0417         KMime::Types::Mailbox from;
0418         from.fromUnicodeString(QLatin1String("from@example.org"));
0419         QString subject = QLatin1String("subject");
0420         QString body = QLatin1String("body");
0421         QList<Attachment> attachments;
0422 
0423         auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, true, attachments);
0424 
0425         QVERIFY(result);
0426         QVERIFY(validate(result));
0427         QCOMPARE(result->subject()->asUnicodeString(), subject);
0428         QVERIFY(result->date(false)->dateTime().isValid());
0429         QVERIFY(result->contentType()->isMimeType("multipart/alternative"));
0430         const auto contents = result->contents();
0431         // 1 Plain + 1 Html
0432         QCOMPARE(contents.size(), 2);
0433     }
0434 
0435     void testCreatePlainMailWithAttachments()
0436     {
0437         QStringList to = {{QLatin1String("to@example.org")}};
0438         QStringList cc = {{QLatin1String("cc@example.org")}};
0439         QStringList bcc = {{QLatin1String("bcc@example.org")}};
0440 
0441         KMime::Types::Mailbox from;
0442         from.fromUnicodeString(QLatin1String("from@example.org"));
0443         QString subject = QLatin1String("subject");
0444         QString body = QLatin1String("body");
0445         QList<Attachment> attachments = {{QLatin1String("name"), QLatin1String("filename"), "mimetype", true, "inlineAttachment"},
0446                                          {QLatin1String("name"), QLatin1String("filename"), "mimetype", false, "nonInlineAttachment"}};
0447 
0448         auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments);
0449 
0450         QVERIFY(result);
0451         QVERIFY(validate(result));
0452         QCOMPARE(result->subject()->asUnicodeString(), subject);
0453         QVERIFY(result->contentType()->isMimeType("multipart/mixed"));
0454         QVERIFY(result->date(false)->dateTime().isValid());
0455         const auto contents = result->contents();
0456         // 1 Plain + 2 Attachments
0457         QCOMPARE(contents.size(), 3);
0458         auto p = getSubpart(result.data(), "text/plain");
0459         QVERIFY(p);
0460     }
0461 
0462     void testCreateHtmlMailWithAttachments()
0463     {
0464         QStringList to = {{QLatin1String("to@example.org")}};
0465         QStringList cc = {{QLatin1String("cc@example.org")}};
0466         QStringList bcc = {{QLatin1String("bcc@example.org")}};
0467 
0468         KMime::Types::Mailbox from;
0469         from.fromUnicodeString(QLatin1String("from@example.org"));
0470         QString subject = QLatin1String("subject");
0471         QString body = QLatin1String("body");
0472         QList<Attachment> attachments = {
0473             {QLatin1String("name"), QLatin1String("filename"), "mimetype", true, "inlineAttachment"},
0474             {QLatin1String("name"), QLatin1String("filename"), "mimetype", false, "nonInlineAttachment"},
0475         };
0476 
0477         auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, true, attachments);
0478 
0479         QVERIFY(result);
0480         QVERIFY(validate(result));
0481         QCOMPARE(result->subject()->asUnicodeString(), subject);
0482         QVERIFY(result->contentType()->isMimeType("multipart/mixed"));
0483         QVERIFY(result->date(false)->dateTime().isValid());
0484         const auto contents = result->contents();
0485         // 1 alternative + 2 Attachments
0486         QCOMPARE(contents.size(), 3);
0487         auto p = getSubpart(result.data(), "multipart/alternative");
0488         QVERIFY(p);
0489     }
0490 
0491     void testCreatePlainMailSigned()
0492     {
0493         QStringList to = {{QLatin1String("to@example.org")}};
0494         QStringList cc = {{QLatin1String("cc@example.org")}};
0495         QStringList bcc = {{QLatin1String("bcc@example.org")}};
0496 
0497         KMime::Types::Mailbox from;
0498         from.fromUnicodeString(QLatin1String("from@example.org"));
0499         QString subject = QLatin1String("subject");
0500         QString body = QLatin1String("body");
0501         QList<Attachment> attachments;
0502 
0503         auto keys = Crypto::findKeys({}, true, false);
0504         auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, keys, {}, keys[0]);
0505 
0506         QVERIFY(result);
0507         QVERIFY(validate(result));
0508         // qWarning() << "---------------------------------";
0509         // qWarning().noquote() << result->encodedContent();
0510         // qWarning() << "---------------------------------";
0511         QCOMPARE(result->subject()->asUnicodeString(), subject);
0512         QVERIFY(result->date(false)->dateTime().isValid());
0513 
0514         QCOMPARE(result->contentType()->mimeType(), QByteArray{"multipart/signed"});
0515         QCOMPARE(result->attachments().size(), 1);
0516         QCOMPARE(result->attachments()[0]->contentDisposition()->filename(), QLatin1String{"0x8F246DE6.asc"});
0517         QCOMPARE(result->contents().size(), 2);
0518 
0519         auto signedMessage = result->contents()[0];
0520         QVERIFY(signedMessage->contentType()->isMimeType("multipart/mixed"));
0521         const auto contents = signedMessage->contents();
0522         QCOMPARE(contents.size(), 2);
0523         QCOMPARE(contents[0]->contentType()->mimeType(), QByteArray{"text/plain"});
0524         QCOMPARE(contents[1]->contentType()->mimeType(), QByteArray{"application/pgp-keys"});
0525         QCOMPARE(contents[1]->contentDisposition()->filename(), QLatin1String{"0x8F246DE6.asc"});
0526 
0527         auto signature = result->contents()[1];
0528         QCOMPARE(signature->contentDisposition()->filename(), QLatin1String{"signature.asc"});
0529         QVERIFY(signature->contentType()->isMimeType("application/pgp-signature"));
0530     }
0531 
0532     void testCreatePlainMailWithAttachmentsSigned()
0533     {
0534         QStringList to = {{QLatin1String("to@example.org")}};
0535         QStringList cc = {{QLatin1String("cc@example.org")}};
0536         QStringList bcc = {{QLatin1String("bcc@example.org")}};
0537 
0538         KMime::Types::Mailbox from;
0539         from.fromUnicodeString(QLatin1String("from@example.org"));
0540         QString subject = QLatin1String("subject");
0541         QString body = QLatin1String("body");
0542         QList<Attachment> attachments = {
0543             {QLatin1String("name"), QLatin1String("filename1"), "mimetype1", true, "inlineAttachment"},
0544             {QLatin1String("name"), QLatin1String("filename2"), "mimetype2", false, "nonInlineAttachment"},
0545         };
0546 
0547         auto signingKeys = Crypto::findKeys({}, true, false);
0548         auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, signingKeys, {}, signingKeys[0]);
0549 
0550         QVERIFY(result);
0551         QVERIFY(validate(result));
0552         qWarning() << "---------------------------------";
0553         qWarning().noquote() << result->encodedContent();
0554         qWarning() << "---------------------------------";
0555         QCOMPARE(result->subject()->asUnicodeString(), subject);
0556         QVERIFY(result->date(false)->dateTime().isValid());
0557 
0558         QCOMPARE(result->contentType()->mimeType(), QByteArray{"multipart/signed"});
0559         QCOMPARE(result->attachments().size(), 3);
0560         QCOMPARE(result->attachments()[0]->contentDisposition()->filename(), QLatin1String{"filename1"});
0561         QCOMPARE(result->attachments()[1]->contentDisposition()->filename(), QLatin1String{"filename2"});
0562         QCOMPARE(result->attachments()[2]->contentDisposition()->filename(), QLatin1String{"0x8F246DE6.asc"});
0563 
0564         QCOMPARE(result->contents().size(), 2);
0565 
0566         auto signedMessage = result->contents()[0];
0567         QVERIFY(signedMessage->contentType()->isMimeType("multipart/mixed"));
0568         const auto contents = signedMessage->contents();
0569         QCOMPARE(contents.size(), 4);
0570         QCOMPARE(contents[0]->contentType()->mimeType(), QByteArray{"text/plain"});
0571         QCOMPARE(contents[1]->contentDisposition()->filename(), QLatin1String{"filename1"});
0572         QCOMPARE(contents[2]->contentDisposition()->filename(), QLatin1String{"filename2"});
0573         QCOMPARE(contents[3]->contentType()->mimeType(), QByteArray{"application/pgp-keys"});
0574         QCOMPARE(contents[3]->contentDisposition()->filename(), QLatin1String{"0x8F246DE6.asc"});
0575     }
0576 
0577     void testCreateIMipMessage()
0578     {
0579         QStringList to = {{QLatin1String("to@example.org")}};
0580         QStringList cc = {{QLatin1String("cc@example.org")}};
0581         QStringList bcc = {{QLatin1String("bcc@example.org")}};
0582         QString from = {QLatin1String("from@example.org")};
0583         QString subject = QLatin1String("subject");
0584         QString body = QLatin1String("body");
0585 
0586         QString ical = QLatin1String("ical");
0587 
0588         auto result = MailTemplates::createIMipMessage(from, {to, cc, bcc}, subject, body, ical);
0589 
0590         QVERIFY(result);
0591         QVERIFY(validate(result));
0592         qWarning() << "---------------------------------";
0593         qWarning().noquote() << result->encodedContent();
0594         qWarning() << "---------------------------------";
0595 
0596         QCOMPARE(result->contentType()->mimeType(), QByteArray{"multipart/alternative"});
0597 
0598         QCOMPARE(result->attachments().size(), 0);
0599 
0600         QCOMPARE(result->contents().size(), 2);
0601         QVERIFY(result->contents()[0]->contentType()->isMimeType("text/plain"));
0602         QVERIFY(result->contents()[1]->contentType()->isMimeType("text/calendar"));
0603         QCOMPARE(result->contents()[1]->contentType()->name(), QLatin1String{"event.ics"});
0604     }
0605 
0606     void testEncryptedWithProtectedHeadersReply()
0607     {
0608         KMime::Types::AddrSpecList me;
0609         KMime::Types::Mailbox mb;
0610         mb.setAddress("to1@example.org");
0611         me << mb.addrSpec();
0612 
0613         auto msg = readMail(QLatin1String("openpgp-encrypted-memoryhole2.mbox"));
0614         KMime::Message::Ptr result;
0615         MailTemplates::reply(
0616             msg,
0617             [&](const KMime::Message::Ptr &r) {
0618                 result = r;
0619             },
0620             me);
0621         QTRY_VERIFY(result);
0622         QCOMPARE(result->subject()->asUnicodeString(), QLatin1String{"RE: This is the subject"});
0623         QCOMPARE(result->to()->addresses(), {{"jane@doe.com"}});
0624         QCOMPARE(result->cc()->addresses(), {{"john@doe.com"}});
0625         QCOMPARE(result->inReplyTo()->asUnicodeString(), QLatin1String{"<03db3530-0000-0000-95a2-8a148f00000@example.com>"});
0626         QCOMPARE(result->references()->asUnicodeString(),
0627                  QLatin1String{"<dbe9d22b-0a3f-cb1e-e883-8a148f00000@example.com> <03db3530-0000-0000-95a2-8a148f00000@example.com>"});
0628         QCOMPARE(normalize(removeFirstLine(QString::fromUtf8(result->body()))),
0629                  QLatin1String{"FsdflkjdslfjHappyMonday!Belowyouwillfindaquickoverviewofthecurrenton-goings.Remember"});
0630     }
0631 
0632     void testEncryptedWithProtectedHeadersForwardAsAttachment()
0633     {
0634         auto msg = readMail(QLatin1String("openpgp-encrypted-memoryhole2.mbox"));
0635         KMime::Message::Ptr result;
0636         MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
0637             result = r;
0638         });
0639         QTRY_VERIFY(result);
0640         QCOMPARE(result->subject()->asUnicodeString(), QLatin1String{"FW: This is the subject"});
0641         QCOMPARE(result->to()->addresses(), {});
0642         QCOMPARE(result->cc()->addresses(), {});
0643         QCOMPARE(result->references()->asUnicodeString(),
0644                  QLatin1String{"<dbe9d22b-0a3f-cb1e-e883-8a148f00000@example.com> <03db3530-0000-0000-95a2-8a148f00000@example.com>"});
0645         QCOMPARE(result->inReplyTo()->identifiers(), {});
0646 
0647         auto attachments = result->attachments();
0648         QCOMPARE(attachments.size(), 1);
0649         auto attachment = attachments[0];
0650         QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline);
0651         QCOMPARE(attachment->contentDisposition(false)->filename(), QLatin1String{"This is the subject.eml"});
0652         QVERIFY(attachment->bodyIsMessage());
0653 
0654         attachment->parse();
0655         auto origMsg = attachment->bodyAsMessage();
0656         QCOMPARE(origMsg->subject(false)->asUnicodeString(), QLatin1String{"..."});
0657     }
0658 };
0659 
0660 QTEST_MAIN(MailTemplateTest)
0661 #include "mailtemplatetest.moc"