File indexing completed on 2025-03-09 04:53:53

0001 /*
0002   SPDX-FileCopyrightText: 2014-2024 Laurent Montel <montel@kde.org>
0003 
0004   SPDX-License-Identifier: GPL-2.0-only
0005 */
0006 
0007 #include "composerviewbasetest.h"
0008 #include "cryptofunctions.h"
0009 #include "qtest_messagecomposer.h"
0010 #include "setupenv.h"
0011 
0012 #include <MessageComposer/Composer>
0013 #include <MessageComposer/ComposerViewBase>
0014 #include <MessageComposer/MessageComposerSettings>
0015 #include <MessageComposer/RecipientsEditor>
0016 #include <MessageComposer/RichTextComposerNg>
0017 
0018 #include <MessageCore/NodeHelper>
0019 
0020 #include <MimeTreeParser/ObjectTreeParser>
0021 #include <MimeTreeParser/SimpleObjectTreeSource>
0022 
0023 #include <KIdentityManagementCore/Identity>
0024 #include <KIdentityManagementCore/IdentityManager>
0025 #include <KIdentityManagementWidgets/IdentityCombo>
0026 
0027 #include <MailTransport/TransportComboBox>
0028 
0029 #include <QStandardPaths>
0030 #include <QTest>
0031 
0032 using namespace MessageComposer;
0033 
0034 Q_DECLARE_METATYPE(Kleo::CryptoMessageFormat)
0035 
0036 QTEST_MAIN(ComposerViewBaseTest)
0037 
0038 ComposerViewBaseTest::ComposerViewBaseTest(QObject *parent)
0039     : QObject(parent)
0040 {
0041     Test::setupEnv();
0042 }
0043 
0044 ComposerViewBaseTest::~ComposerViewBaseTest() = default;
0045 
0046 void ComposerViewBaseTest::cleanupTestCase()
0047 {
0048     delete mIdentMan;
0049     mIdentMan = nullptr;
0050     delete mIdentCombo;
0051     mIdentCombo = nullptr;
0052 }
0053 
0054 void ComposerViewBaseTest::initTestCase()
0055 {
0056     mIdentMan = new KIdentityManagementCore::IdentityManager;
0057 
0058     KIdentityManagementCore::Identity &ident = mIdentMan->modifyIdentityForUoid(mIdentMan->defaultIdentity().uoid());
0059     ident.setAutocryptEnabled(true);
0060     ident.setPGPEncryptionKey("1BA323932B3FAA826132C79E8D9860C58F246DE6");
0061     ident.setPGPSigningKey("1BA323932B3FAA826132C79E8D9860C58F246DE6");
0062     mIdentMan->commit();
0063     mIdentCombo = new KIdentityManagementWidgets::IdentityCombo(mIdentMan);
0064 
0065     MessageComposerSettings::self()->setCryptoShowKeysForApproval(false);
0066 
0067     const QDir genericDataLocation(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation));
0068     autocryptDir = QDir(genericDataLocation.filePath(QStringLiteral("autocrypt")));
0069 }
0070 
0071 void ComposerViewBaseTest::init()
0072 {
0073     autocryptDir.removeRecursively();
0074     autocryptDir.mkpath(QStringLiteral("."));
0075 }
0076 
0077 void ComposerViewBaseTest::cleanup()
0078 {
0079     autocryptDir.removeRecursively();
0080 }
0081 
0082 void ComposerViewBaseTest::shouldHaveDefaultValue()
0083 {
0084     ComposerViewBase composerViewBase;
0085     QVERIFY(!composerViewBase.attachmentModel());
0086     QVERIFY(!composerViewBase.attachmentController());
0087     QVERIFY(!composerViewBase.recipientsEditor());
0088     QVERIFY(!composerViewBase.signatureController());
0089     QVERIFY(!composerViewBase.identityCombo());
0090     QVERIFY(!composerViewBase.identityManager());
0091     QVERIFY(!composerViewBase.editor());
0092     QVERIFY(!composerViewBase.transportComboBox());
0093     QVERIFY(!composerViewBase.fccCombo());
0094     QVERIFY(!composerViewBase.dictionary());
0095     QVERIFY(composerViewBase.to().isEmpty());
0096     QVERIFY(composerViewBase.cc().isEmpty());
0097     QVERIFY(composerViewBase.bcc().isEmpty());
0098     QVERIFY(composerViewBase.from().isEmpty());
0099     QVERIFY(composerViewBase.replyTo().isEmpty());
0100     QVERIFY(composerViewBase.subject().isEmpty());
0101     QCOMPARE(composerViewBase.autoSaveInterval(), 60000);
0102 }
0103 
0104 void ComposerViewBaseTest::testAutoSaveMessage()
0105 {
0106     QString fileName(QStringLiteral("testfile"));
0107 
0108     MessageComposer::RichTextComposerNg editor;
0109     MessageComposer::RecipientsEditor recipientsEditor;
0110     MailTransport::TransportComboBox transpCombo;
0111     ComposerViewBase composerViewBase;
0112     composerViewBase.setAutoSaveInterval(0);
0113     composerViewBase.setAutoSaveFileName(fileName);
0114     composerViewBase.setIdentityCombo(mIdentCombo);
0115     composerViewBase.setIdentityManager(mIdentMan);
0116     composerViewBase.setEditor(&editor);
0117     composerViewBase.setTransportCombo(&transpCombo);
0118     composerViewBase.setRecipientsEditor(&recipientsEditor);
0119     composerViewBase.setFrom(QStringLiteral("me@me.example"));
0120     KMime::Types::Mailbox mb;
0121     mb.from7BitString("to@to.example");
0122     recipientsEditor.setRecipientString({mb}, Recipient::To);
0123     editor.setPlainText(QStringLiteral("Hello,\n\nThis is a test message\n\nGreez"));
0124 
0125     composerViewBase.autoSaveMessage();
0126 
0127     // It may be possible that we need to wait till the autosave message is ready composed.
0128     if (composerViewBase.m_composers.size() > 0) {
0129         QEventLoop loop;
0130         bool notFinished = false;
0131         for (const auto composer : composerViewBase.m_composers) {
0132             if (!composer->finished()) {
0133                 notFinished = true;
0134                 connect(composer, &MessageComposer::Composer::result, [&composerViewBase, &loop]() {
0135                     if (composerViewBase.m_composers.size() < 1) {
0136                         loop.quit();
0137                     }
0138                 });
0139             }
0140         }
0141         if (notFinished) {
0142             loop.exec();
0143         }
0144     }
0145 
0146     const QString autosavePath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1StringView("/kmail2/autosave/");
0147     auto msg = Test::loadMessage(autosavePath + fileName);
0148     msg->messageID()->from7BitString("<test@autotest.example>");
0149     msg->date()->from7BitString("Tue, 22 Jan 2019 12:56:25 +0100");
0150     msg->assemble();
0151     Test::compareFile(msg.data(), QStringLiteral(MAIL_DATA_DIR "/autosave.mbox"));
0152 }
0153 
0154 void ComposerViewBaseTest::testGenerateCryptoMessagesAutocrypt_data()
0155 {
0156     QTest::addColumn<QString>("data");
0157     QTest::addColumn<bool>("sign");
0158     QTest::addColumn<bool>("encrypt");
0159     QTest::addColumn<Kleo::CryptoMessageFormat>("format");
0160 
0161     QString data = QStringLiteral("Hello,\n\nThis is a test message\n\nGreez");
0162 
0163     QTest::newRow("Plain") << data << false << false << Kleo::AutoFormat;
0164     QTest::newRow("Sign") << data << true << false << Kleo::AutoFormat;
0165     QTest::newRow("Encrypt") << data << false << true << Kleo::AutoFormat;
0166     QTest::newRow("SignEncrypt") << data << true << true << Kleo::AutoFormat;
0167 }
0168 
0169 void ComposerViewBaseTest::testGenerateCryptoMessagesAutocrypt()
0170 {
0171     QFETCH(QString, data);
0172     QFETCH(bool, sign);
0173     QFETCH(bool, encrypt);
0174     QFETCH(Kleo::CryptoMessageFormat, format);
0175 
0176     MessageComposer::RichTextComposerNg editor;
0177     MessageComposer::RecipientsEditor recipientsEditor;
0178     MailTransport::TransportComboBox transpCombo;
0179     ComposerViewBase composerViewBase;
0180     composerViewBase.setIdentityCombo(mIdentCombo);
0181     composerViewBase.setIdentityManager(mIdentMan);
0182     composerViewBase.setEditor(&editor);
0183     composerViewBase.setTransportCombo(&transpCombo);
0184     composerViewBase.setRecipientsEditor(&recipientsEditor);
0185     composerViewBase.setFrom(QStringLiteral("me@me.example"));
0186     composerViewBase.mExpandedTo << QStringLiteral("you@you.com");
0187     composerViewBase.setAkonadiLookupEnabled(false);
0188     KMime::Types::Mailbox mb;
0189     mb.from7BitString("you@you.com");
0190     recipientsEditor.setRecipientString({mb}, Recipient::To);
0191     editor.setPlainText(data);
0192 
0193     bool wasCanceled = false;
0194     composerViewBase.setCryptoOptions(sign, encrypt, format, false);
0195     auto composers = composerViewBase.generateCryptoMessages(wasCanceled);
0196     QCOMPARE(wasCanceled, false);
0197 
0198     QCOMPARE(composers.size(), 1); // No additional composers are created
0199 
0200     auto composer = composers.first();
0201     composerViewBase.fillComposer(composer);
0202 
0203     VERIFYEXEC(composer);
0204 
0205     QCOMPARE(composer->resultMessages().size(), 1);
0206 
0207     auto msg = composer->resultMessages().first();
0208     msg->assemble();
0209 
0210     // Every message should have an Autocrypt in the outer message structure
0211     // as it should always been possible to start an encrypted reply
0212     QCOMPARE(msg->hasHeader("Autocrypt"), true);
0213 
0214     MimeTreeParser::SimpleObjectTreeSource testSource;
0215     testSource.setDecryptMessage(true);
0216     auto nh = new MimeTreeParser::NodeHelper;
0217     MimeTreeParser::ObjectTreeParser otp(&testSource, nh);
0218 
0219     otp.parseObjectTree(msg.data());
0220 
0221     KMime::Content *content = msg.data();
0222 
0223     if (encrypt) {
0224         QCOMPARE(nh->encryptionState(msg.data()), MimeTreeParser::KMMsgFullyEncrypted);
0225         const auto extra = nh->extraContents(msg.data());
0226         QCOMPARE(extra.size(), 1);
0227         content = extra.first();
0228     } else {
0229         QCOMPARE(nh->encryptionState(msg.data()), MimeTreeParser::KMMsgNotEncrypted);
0230     }
0231 
0232     if (sign) {
0233         content = MessageCore::NodeHelper::firstChild(content);
0234         QCOMPARE(nh->signatureState(content), MimeTreeParser::KMMsgFullySigned);
0235     } else {
0236         QCOMPARE(nh->signatureState(msg.data()), MimeTreeParser::KMMsgNotSigned);
0237     }
0238 
0239     QCOMPARE(QString::fromUtf8(content->decodedContent()), data);
0240 }
0241 
0242 void ComposerViewBaseTest::testGenerateCryptoMessagesAutocryptSMime()
0243 {
0244     QString data = QStringLiteral("Hello,\n\nThis is a test message\n\nGreez");
0245 
0246     MessageComposer::RichTextComposerNg editor;
0247     MessageComposer::RecipientsEditor recipientsEditor;
0248     MailTransport::TransportComboBox transpCombo;
0249     ComposerViewBase composerViewBase;
0250     composerViewBase.setIdentityCombo(mIdentCombo);
0251     composerViewBase.setIdentityManager(mIdentMan);
0252     composerViewBase.setEditor(&editor);
0253     composerViewBase.setTransportCombo(&transpCombo);
0254     composerViewBase.setRecipientsEditor(&recipientsEditor);
0255     composerViewBase.setFrom(QStringLiteral("me@me.example"));
0256     composerViewBase.mExpandedTo << QStringLiteral("you@you.com");
0257     composerViewBase.setAkonadiLookupEnabled(false);
0258     KMime::Types::Mailbox mb;
0259     mb.from7BitString("you@you.com");
0260     recipientsEditor.setRecipientString({mb}, Recipient::To);
0261     editor.setPlainText(data);
0262 
0263     bool wasCanceled = false;
0264     composerViewBase.setCryptoOptions(false, false, Kleo::AnySMIME, false);
0265     auto composers = composerViewBase.generateCryptoMessages(wasCanceled);
0266     QCOMPARE(wasCanceled, false);
0267 
0268     QCOMPARE(composers.size(), 1); // No additional composers are created
0269 
0270     auto composer = composers.first();
0271     composerViewBase.fillComposer(composer);
0272 
0273     VERIFYEXEC(composer);
0274 
0275     QCOMPARE(composer->resultMessages().size(), 1);
0276 
0277     auto msg = composer->resultMessages().first();
0278     msg->assemble();
0279 
0280     QCOMPARE(QString::fromUtf8(msg->decodedContent()), data);
0281     QCOMPARE(msg->hasHeader("Autocrypt"), false);
0282 }
0283 
0284 void ComposerViewBaseTest::testAutocryptKey()
0285 {
0286     QFile file(QLatin1StringView(MESSAGECORE_DATA_DIR) + QStringLiteral("/autocrypt/recipient.json"));
0287     QVERIFY(file.copy(autocryptDir.path() + QStringLiteral("/recipient%40autocrypt.example.json")));
0288 
0289     qDebug() << autocryptDir.path();
0290 
0291     QString data = QStringLiteral("Hello,\n\nThis is a test message\n\nGreez");
0292 
0293     MessageComposer::RichTextComposerNg editor;
0294     MessageComposer::RecipientsEditor recipientsEditor;
0295     MailTransport::TransportComboBox transpCombo;
0296     ComposerViewBase composerViewBase;
0297     composerViewBase.setIdentityCombo(mIdentCombo);
0298     composerViewBase.setIdentityManager(mIdentMan);
0299     composerViewBase.setEditor(&editor);
0300     composerViewBase.setTransportCombo(&transpCombo);
0301     composerViewBase.setRecipientsEditor(&recipientsEditor);
0302     composerViewBase.setFrom(QStringLiteral("me@me.example"));
0303     composerViewBase.mExpandedTo << QStringLiteral("recipient@autocrypt.example");
0304     composerViewBase.setAkonadiLookupEnabled(false);
0305     KMime::Types::Mailbox mb;
0306     mb.from7BitString("recipient@autocrypt.example");
0307     recipientsEditor.setRecipientString({mb}, Recipient::To);
0308     editor.setPlainText(data);
0309 
0310     bool wasCanceled = false;
0311     composerViewBase.setCryptoOptions(false, true, Kleo::AutoFormat, false);
0312     auto composers = composerViewBase.generateCryptoMessages(wasCanceled);
0313     QCOMPARE(wasCanceled, false);
0314 
0315     QCOMPARE(composers.size(), 1); // No additional composers are created
0316 
0317     auto composer = composers.first();
0318     composerViewBase.fillComposer(composer);
0319 
0320     VERIFYEXEC(composer);
0321 
0322     QCOMPARE(composer->resultMessages().size(), 1);
0323 
0324     // The key for recipient@autocrypt.example is only available in Autocrypt
0325     // test is we created a special GNUPGHOME to contain all needed keys.
0326     QVERIFY(!composer->gnupgHome().isEmpty());
0327 
0328     auto msg = composer->resultMessages().first();
0329     msg->assemble();
0330 
0331     // Every message should have an Autocrypt in the outer message structure
0332     // as it should always been possible to start an encrypted reply
0333     QCOMPARE(msg->hasHeader("Autocrypt"), true);
0334 
0335     MimeTreeParser::SimpleObjectTreeSource testSource;
0336     testSource.setDecryptMessage(true);
0337     auto nh = new MimeTreeParser::NodeHelper;
0338     MimeTreeParser::ObjectTreeParser otp(&testSource, nh);
0339 
0340     otp.parseObjectTree(msg.data());
0341 
0342     KMime::Content *content = msg.data();
0343 
0344     QCOMPARE(nh->encryptionState(msg.data()), MimeTreeParser::KMMsgFullyEncrypted);
0345     const auto extra = nh->extraContents(msg.data());
0346     QCOMPARE(extra.size(), 1);
0347     content = extra.first();
0348 
0349     QCOMPARE(QString::fromUtf8(content->decodedContent()), data);
0350 }
0351 
0352 #include "moc_composerviewbasetest.cpp"