File indexing completed on 2024-11-24 04:53:30
0001 /* Copyright (C) 2014-2015 Stephan Platz <trojita@paalsteek.de> 0002 0003 This file is part of the Trojita Qt IMAP e-mail client, 0004 http://trojita.flaska.net/ 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include <QtTest/QtTest> 0024 0025 #include "test_Cryptography_PGP.h" 0026 #include "configure.cmake.h" 0027 #include "crypto_test_data.h" 0028 #include "Cryptography/MessageModel.h" 0029 #include "Imap/data.h" 0030 #include "Imap/Model/ItemRoles.h" 0031 #include "Streams/FakeSocket.h" 0032 0033 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0034 # ifdef TROJITA_HAVE_GPGMEPP 0035 # include "Cryptography/GpgMe++.h" 0036 # endif 0037 #endif 0038 0039 /* TODO: test cases: 0040 * * decrypt with not yet valid key 0041 * * verify signature of modified text 0042 * * verify signature with expired key 0043 * * verify signature with not yet valid key 0044 * * verify signature with unknown key 0045 * * nested scenarios 0046 */ 0047 0048 void CryptographyPGPTest::initTestCase() 0049 { 0050 LibMailboxSync::initTestCase(); 0051 if (!qputenv("GNUPGHOME", QByteArray(QCoreApplication::applicationDirPath().toUtf8() + "/keys").constData())) { 0052 QFAIL("Unable to set GNUPGHOME environment variable"); 0053 } 0054 } 0055 0056 void CryptographyPGPTest::testDecryption() 0057 { 0058 QFETCH(QByteArray, bodystructure); 0059 QFETCH(QByteArray, cyphertext); 0060 QFETCH(QByteArray, plaintext); 0061 QFETCH(QString, from); 0062 QFETCH(bool, successful); 0063 0064 // By default, there's a 50ms delay between the time we request a part download and the time it actually happens. 0065 // That's too long for a unit test. 0066 model->setProperty("trojita-imap-delayed-fetch-part", 0); 0067 0068 helperSyncBNoMessages(); 0069 cServer("* 1 EXISTS\r\n"); 0070 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); 0071 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); 0072 0073 QCOMPARE(model->rowCount(msgListB), 1); 0074 QModelIndex msg = msgListB.model()->index(0, 0, msgListB); 0075 QVERIFY(msg.isValid()); 0076 QCOMPARE(model->rowCount(msg), 0); 0077 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); 0078 cServer(helperCreateTrivialEnvelope(1, 333, QStringLiteral("subj"), from, bodystructure) 0079 + t.last("OK fetched\r\n")); 0080 cEmpty(); 0081 QVERIFY(model->rowCount(msg) > 0); 0082 Cryptography::MessageModel msgModel(0, msg); 0083 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0084 # ifdef TROJITA_HAVE_GPGMEPP 0085 msgModel.registerPartHandler(std::make_shared<Cryptography::GpgMeReplacer>()); 0086 # endif 0087 #endif 0088 QModelIndex mappedMsg = msgModel.index(0,0); 0089 QVERIFY(mappedMsg.isValid()); 0090 QVERIFY(msgModel.rowCount(mappedMsg) > 0); 0091 0092 QModelIndex data = mappedMsg.model()->index(0, 0, mappedMsg); 0093 QVERIFY(data.isValid()); 0094 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0095 QCOMPARE(msgModel.rowCount(data), 0); 0096 QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), false); 0097 0098 cClientRegExp(t.mk("UID FETCH 333 \\((BODY\\.PEEK\\[2\\] BODY\\.PEEK\\[1\\]|BODY.PEEK\\[1\\] BODY\\.PEEK\\[2\\])\\)")); 0099 cServer("* 1 FETCH (UID 333 BODY[2] " + asLiteral(cyphertext) + " BODY[1] " + asLiteral("Version: 1\r\n") + ")\r\n" 0100 + t.last("OK fetched")); 0101 0102 QSignalSpy qcaSuccessSpy(&msgModel, SIGNAL(rowsInserted(const QModelIndex &,int,int))); 0103 QSignalSpy qcaErrorSpy(&msgModel, SIGNAL(error(const QModelIndex &,QString,QString))); 0104 0105 int i = 0; 0106 while (data.isValid() && data.data(Imap::Mailbox::RolePartCryptoNotFinishedYet).toBool() && i++ < 1000) { 0107 QTest::qWait(10); 0108 } 0109 // allow for event processing, so that the model can retrieve the results 0110 QCoreApplication::processEvents(); 0111 QVERIFY(!data.data(Imap::Mailbox::RolePartCryptoNotFinishedYet).toBool()); 0112 0113 if (!qcaErrorSpy.isEmpty() && successful) { 0114 qDebug() << "Unexpected failure in crypto"; 0115 for (int i = 0; i < qcaErrorSpy.size(); ++i) { 0116 qDebug() << qcaErrorSpy[i][1].toString(); 0117 qDebug() << qcaErrorSpy[i][2].toString(); 0118 } 0119 } 0120 0121 if (successful) { 0122 QCOMPARE(qcaErrorSpy.empty(), successful); 0123 QCOMPARE(qcaSuccessSpy.empty(), !successful); 0124 } 0125 0126 QVERIFY(data.data(Imap::Mailbox::RoleIsFetched).toBool()); 0127 0128 cEmpty(); 0129 QVERIFY(errorSpy->empty()); 0130 #else 0131 QCOMPARE(msgModel.rowCount(data), 2); 0132 QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), true); 0133 0134 QCOMPARE(data.child(0, 0).data(Imap::Mailbox::RolePartMimeType).toString(), QLatin1String("application/pgp-encrypted")); 0135 QCOMPARE(data.child(1, 0).data(Imap::Mailbox::RolePartMimeType).toString(), QLatin1String("application/octet-stream")); 0136 cEmpty(); 0137 0138 QSKIP("Some tests were skipped because this build doesn't have GpgME++ support"); 0139 #endif 0140 } 0141 0142 void CryptographyPGPTest::testDecryption_data() 0143 { 0144 QTest::addColumn<QByteArray>("bodystructure"); 0145 QTest::addColumn<QByteArray>("cyphertext"); 0146 QTest::addColumn<QByteArray>("plaintext"); 0147 QTest::addColumn<QString>("from"); 0148 QTest::addColumn<bool>("successful"); 0149 0150 // everything is correct 0151 QTest::newRow("valid") 0152 << bsEncrypted 0153 << encValid 0154 << QByteArray("plaintext") 0155 << QStringLiteral("valid@test.trojita.flaska.net") 0156 << true; 0157 0158 // corrupted data 0159 QTest::newRow("invalid") 0160 << bsEncrypted 0161 << encInvalid 0162 << QByteArray("plaintext") 0163 << QStringLiteral("valid@test.trojita.flaska.net") 0164 << false; 0165 0166 // the key used for encryption is expired 0167 QTest::newRow("expiredKey") 0168 << bsEncrypted 0169 << encExpired 0170 << QByteArray("plaintext") 0171 // NOTE (jkt): This is how my QCA/2.1.0.3, GnuPG/2.0.28 behaves. 0172 << QStringLiteral("valid@test.trojita.flaska.net") 0173 << true; 0174 0175 // we don't have any key which is needed for encryption 0176 QTest::newRow("unknownKey") 0177 << bsEncrypted 0178 << encUnknown 0179 << QByteArray("plaintext") 0180 << QStringLiteral("valid@test.trojita.flaska.net") 0181 << false; 0182 } 0183 0184 /** @short What happens when ENVELOPE doesn't arrive at the time that parts are already there? */ 0185 void CryptographyPGPTest::testDecryptWithoutEnvelope() 0186 { 0187 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0188 model->setProperty("trojita-imap-delayed-fetch-part", 0); 0189 0190 helperSyncBNoMessages(); 0191 cServer("* 1 EXISTS\r\n"); 0192 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); 0193 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); 0194 QCOMPARE(model->rowCount(msgListB), 1); 0195 QModelIndex msg = msgListB.model()->index(0, 0, msgListB); 0196 QVERIFY(msg.isValid()); 0197 Cryptography::MessageModel msgModel(0, msg); 0198 msgModel.registerPartHandler(std::make_shared<Cryptography::GpgMeReplacer>()); 0199 0200 QCOMPARE(model->rowCount(msg), 0); 0201 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); 0202 cServer("* 1 FETCH (UID 333 BODYSTRUCTURE (" + bsEncrypted + "))\r\n" + t.last("OK fetched\r\n")); 0203 // notice that the ENVELOPE never arrived 0204 cEmpty(); 0205 QVERIFY(model->rowCount(msg) > 0); 0206 QModelIndex mappedMsg = msgModel.index(0,0); 0207 QVERIFY(mappedMsg.isValid()); 0208 QVERIFY(msgModel.rowCount(mappedMsg) > 0); 0209 0210 QModelIndex data = mappedMsg.model()->index(0, 0, mappedMsg); 0211 QVERIFY(data.isValid()); 0212 QCOMPARE(msgModel.rowCount(data), 0); 0213 QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), false); 0214 0215 cClientRegExp(t.mk("UID FETCH 333 \\((BODY\\.PEEK\\[2\\] BODY\\.PEEK\\[1\\]|BODY.PEEK\\[1\\] BODY\\.PEEK\\[2\\])\\)")); 0216 cServer("* 1 FETCH (UID 333 BODY[2] " + asLiteral(encValid) + " BODY[1] " + asLiteral("Version: 1\r\n") + ")\r\n" 0217 + t.last("OK fetched")); 0218 0219 QSignalSpy qcaSuccessSpy(&msgModel, SIGNAL(rowsInserted(const QModelIndex &,int,int))); 0220 QSignalSpy qcaErrorSpy(&msgModel, SIGNAL(error(const QModelIndex &,QString,QString))); 0221 0222 int i = 0; 0223 while (data.isValid() && data.data(Imap::Mailbox::RolePartCryptoNotFinishedYet).toBool() && i++ < 1000) { 0224 QTest::qWait(10); 0225 } 0226 // allow for event processing, so that the model can retrieve the results 0227 QCoreApplication::processEvents(); 0228 QVERIFY(!data.data(Imap::Mailbox::RolePartCryptoNotFinishedYet).toBool()); 0229 0230 QVERIFY(qcaSuccessSpy.isEmpty()); 0231 QVERIFY(qcaErrorSpy.isEmpty()); 0232 0233 QVERIFY(!data.data(Imap::Mailbox::RoleIsFetched).toBool()); // because the ENVELOPE hasn't arrived yet 0234 0235 cEmpty(); 0236 QVERIFY(errorSpy->empty()); 0237 #else 0238 QSKIP("Cannot test without GpgME++ support"); 0239 #endif 0240 } 0241 0242 void CryptographyPGPTest::testVerification() 0243 { 0244 QFETCH(QByteArray, signature); 0245 QFETCH(QByteArray, ptMimeHdr); 0246 QFETCH(QByteArray, plaintext); 0247 QFETCH(bool, successful); 0248 QFETCH(QString, from); 0249 QFETCH(QString, tldr); 0250 QFETCH(QString, longDesc); 0251 QFETCH(bool, validDisregardingTrust); 0252 QFETCH(bool, validCompletely); 0253 0254 model->setProperty("trojita-imap-delayed-fetch-part", 0); 0255 helperSyncBNoMessages(); 0256 cServer("* 1 EXISTS\r\n"); 0257 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); 0258 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); 0259 QCOMPARE(model->rowCount(msgListB), 1); 0260 QModelIndex msg = msgListB.model()->index(0, 0, msgListB); 0261 QVERIFY(msg.isValid()); 0262 QCOMPARE(model->rowCount(msg), 0); 0263 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); 0264 cServer(helperCreateTrivialEnvelope(1, 333, QStringLiteral("subj"), from, QStringLiteral( 0265 "(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 423 14 NIL NIL NIL NIL)" 0266 "(\"application\" \"pgp-signature\" NIL NIL NIL \"7bit\" 851 NIL NIL NIL NIL)" 0267 " \"signed\" (\"boundary\" \"=-=-=\" \"micalg\" \"pgp-sha256\" \"protocol\" \"application/pgp-signature\")" 0268 " NIL NIL NIL")) 0269 + t.last("OK fetched\r\n")); 0270 cEmpty(); 0271 QVERIFY(model->rowCount(msg) > 0); 0272 Cryptography::MessageModel msgModel(0, msg); 0273 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0274 # ifdef TROJITA_HAVE_GPGMEPP 0275 msgModel.registerPartHandler(std::make_shared<Cryptography::GpgMeReplacer>()); 0276 # endif 0277 #endif 0278 QModelIndex mappedMsg = msgModel.index(0,0); 0279 QVERIFY(mappedMsg.isValid()); 0280 QVERIFY(msgModel.rowCount(mappedMsg) > 0); 0281 0282 QModelIndex data = mappedMsg.model()->index(0, 0, mappedMsg); 0283 QVERIFY(data.isValid()); 0284 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0285 QCOMPARE(msgModel.rowCount(data), 0); 0286 QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), false); 0287 0288 cClientRegExp(t.mk("UID FETCH 333 \\((BODY\\.PEEK\\[(2|1|1\\.MIME)\\] ?){3}\\)")); 0289 cServer("* 1 FETCH (UID 333 BODY[2] " + asLiteral(signature) + " BODY[1] " + asLiteral(plaintext) 0290 + " BODY[1.MIME] " + asLiteral(ptMimeHdr) + ")\r\n" 0291 + t.last("OK fetched")); 0292 0293 QSignalSpy qcaErrorSpy(&msgModel, SIGNAL(error(const QModelIndex &,QString,QString))); 0294 0295 int i = 0; 0296 while (data.isValid() && data.data(Imap::Mailbox::RolePartCryptoNotFinishedYet).toBool() && qcaErrorSpy.empty() && i++ < 1000) { 0297 QTest::qWait(10); 0298 } 0299 // allow for event processing, so that the model can retrieve the results 0300 QCoreApplication::processEvents(); 0301 QVERIFY(!data.data(Imap::Mailbox::RolePartCryptoNotFinishedYet).toBool()); 0302 0303 if (!qcaErrorSpy.isEmpty() && successful) { 0304 qDebug() << "Unexpected failure in crypto"; 0305 for (int i = 0; i < qcaErrorSpy.size(); ++i) { 0306 qDebug() << qcaErrorSpy[i][1].toString(); 0307 qDebug() << qcaErrorSpy[i][2].toString(); 0308 } 0309 } 0310 0311 QCOMPARE(qcaErrorSpy.empty(), successful); 0312 QCOMPARE(data.data(Imap::Mailbox::RolePartCryptoTLDR).toString(), tldr); 0313 auto actualLongDesc = data.data(Imap::Mailbox::RolePartCryptoDetailedMessage).toString(); 0314 if (!actualLongDesc.startsWith(longDesc)) { 0315 QCOMPARE(actualLongDesc, longDesc); // let's reuse this for debug output, and don't be scared about the misleading implications 0316 } 0317 0318 QCOMPARE(data.data(Imap::Mailbox::RolePartSignatureVerifySupported).toBool(), successful); 0319 QCOMPARE(data.data(Imap::Mailbox::RolePartSignatureValidDisregardingTrust).toBool(), validDisregardingTrust); 0320 QCOMPARE(data.data(Imap::Mailbox::RolePartSignatureValidTrusted).toBool(), validCompletely); 0321 QCOMPARE(data.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArray("multipart/signed")); 0322 QVERIFY(data.data(Imap::Mailbox::RoleIsFetched).toBool() == successful); 0323 0324 auto partIdx = data.model()->index(0, 0, data); 0325 QCOMPARE(partIdx.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArray("text/plain")); 0326 QCOMPARE(partIdx.data(Imap::Mailbox::RolePartUnicodeText).toString(), QString::fromUtf8(plaintext)); 0327 0328 cEmpty(); 0329 QVERIFY(errorSpy->empty()); 0330 #else 0331 QCOMPARE(msgModel.rowCount(data), 2); 0332 QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), true); 0333 0334 QCOMPARE(data.child(0, 0).data(Imap::Mailbox::RolePartMimeType).toString(), QLatin1String("application/pgp-encrypted")); 0335 QCOMPARE(data.child(1, 0).data(Imap::Mailbox::RolePartMimeType).toString(), QLatin1String("application/octet-stream")); 0336 cEmpty(); 0337 0338 QSKIP("Some tests were skipped because this build doesn't have GpgME++ support"); 0339 #endif 0340 } 0341 0342 void CryptographyPGPTest::testVerification_data() 0343 { 0344 QTest::addColumn<QByteArray>("signature"); 0345 QTest::addColumn<QByteArray>("ptMimeHdr"); 0346 QTest::addColumn<QByteArray>("plaintext"); 0347 QTest::addColumn<bool>("successful"); 0348 QTest::addColumn<QString>("from"); 0349 QTest::addColumn<QString>("tldr"); 0350 QTest::addColumn<QString>("longDesc"); 0351 QTest::addColumn<bool>("validDisregardingTrust"); 0352 QTest::addColumn<bool>("validCompletely"); 0353 0354 QByteArray ptMimeHdr = QByteArrayLiteral("Content-Type: text/plain\r\n\r\n"); 0355 0356 // everything is correct 0357 QTest::newRow("valid-me") 0358 << sigFromMe 0359 << ptMimeHdr 0360 << QByteArray("plaintext\r\n") 0361 << true 0362 << QStringLiteral("valid@test.trojita.flaska.net") 0363 << QStringLiteral("Verified signature") 0364 << QStringLiteral("Verified signature from Valid <valid@test.trojita.flaska.net> (") 0365 << true 0366 << true; 0367 0368 // my signature, but a different identity 0369 QTest::newRow("my-signature-different-identity") 0370 << sigFromMe 0371 << ptMimeHdr 0372 << QByteArray("plaintext\r\n") 0373 << true 0374 << QStringLiteral("evil@example.org") 0375 << QStringLiteral("Signed by stranger") 0376 << QStringLiteral("Verified signature, but the signer is someone else:\nValid <valid@test.trojita.flaska.net> (") 0377 << true 0378 << false; 0379 0380 // my signature, different data 0381 QTest::newRow("my-signature-different-data") 0382 << sigFromMe 0383 << ptMimeHdr 0384 << QByteArray("I will pay you right now\r\n") 0385 << true 0386 << QStringLiteral("valid@test.trojita.flaska.net") 0387 << QStringLiteral("Bad signature") 0388 << QStringLiteral("Bad signature by Valid <valid@test.trojita.flaska.net> (") 0389 << false 0390 << false; 0391 0392 // A missing Content-Type header (and also an invalid signature) 0393 QTest::newRow("invalid-implicit-content-type") 0394 << sigFromMe 0395 << QByteArrayLiteral("Content-Transfer-Encoding: 8bit\r\n\r\n") 0396 << QByteArray("plaintext\r\n") 0397 << true 0398 << QStringLiteral("valid@test.trojita.flaska.net") 0399 << QStringLiteral("Bad signature") 0400 << QStringLiteral("Bad signature by Valid <valid@test.trojita.flaska.net> (") 0401 << false 0402 << false; 0403 } 0404 0405 void CryptographyPGPTest::testMalformed() 0406 { 0407 QFETCH(QByteArray, bodystructure); 0408 QFETCH(int, rowCount); 0409 QFETCH(QString, tldr); 0410 QFETCH(QString, detail); 0411 0412 model->setProperty("trojita-imap-delayed-fetch-part", 0); 0413 helperSyncBNoMessages(); 0414 cServer("* 1 EXISTS\r\n"); 0415 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); 0416 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); 0417 QCOMPARE(model->rowCount(msgListB), 1); 0418 QModelIndex msg = msgListB.model()->index(0, 0, msgListB); 0419 QVERIFY(msg.isValid()); 0420 QCOMPARE(model->rowCount(msg), 0); 0421 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); 0422 cServer(helperCreateTrivialEnvelope(1, 333, QStringLiteral("subj"), QStringLiteral("foo@example.org"), QString::fromUtf8(bodystructure)) 0423 + t.last("OK fetched\r\n")); 0424 cEmpty(); 0425 QVERIFY(model->rowCount(msg) > 0); 0426 Cryptography::MessageModel msgModel(0, msg); 0427 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0428 # ifdef TROJITA_HAVE_GPGMEPP 0429 msgModel.registerPartHandler(std::make_shared<Cryptography::GpgMeReplacer>()); 0430 # endif 0431 #endif 0432 QModelIndex mappedMsg = msgModel.index(0,0); 0433 QVERIFY(mappedMsg.isValid()); 0434 QVERIFY(msgModel.rowCount(mappedMsg) > 0); 0435 0436 QModelIndex data = mappedMsg.model()->index(0, 0, mappedMsg); 0437 QVERIFY(data.isValid()); 0438 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0439 QCoreApplication::processEvents(); 0440 QCOMPARE(msgModel.rowCount(data), rowCount); 0441 QCOMPARE(data.data(Imap::Mailbox::RolePartCryptoTLDR).toString(), tldr); 0442 QCOMPARE(data.data(Imap::Mailbox::RolePartCryptoDetailedMessage).toString(), detail); 0443 0444 cEmpty(); 0445 QVERIFY(errorSpy->empty()); 0446 #else 0447 QCOMPARE(msgModel.rowCount(data), rowCount); 0448 QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), true); 0449 0450 cEmpty(); 0451 0452 QSKIP("Some tests were skipped because this build doesn't have GpgME++ support"); 0453 #endif 0454 } 0455 0456 void CryptographyPGPTest::testMalformed_data() 0457 { 0458 QTest::addColumn<QByteArray>("bodystructure"); 0459 QTest::addColumn<int>("rowCount"); 0460 QTest::addColumn<QString>("tldr"); 0461 QTest::addColumn<QString>("detail"); 0462 0463 // Due to the missing "protocol", the part replacer won't even react 0464 QTest::newRow("signed-missing-protocol-micalg") 0465 << bsMultipartSignedTextPlain 0466 << 2 0467 << QString() 0468 << QString(); 0469 0470 // A mailing list has stripped the signature 0471 QTest::newRow("signed-ml-stripped-gpg-signature") 0472 << QByteArray("(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 423 14 NIL NIL NIL NIL)" 0473 " \"signed\" (\"boundary\" \"=-=-=\" \"micalg\" \"pgp-sha256\" \"protocol\" \"application/pgp-signature\")" 0474 " NIL NIL NIL") 0475 << 1 0476 << QStringLiteral("Malformed Signed Message") 0477 << QStringLiteral("Expected 2 parts, but found 1."); 0478 0479 } 0480 0481 /** @short Check operation when some data are not available */ 0482 void CryptographyPGPTest::testOffline() 0483 { 0484 QFETCH(QByteArray, bodystructure); 0485 QFETCH(QByteArray, fetchRegex); 0486 0487 model->setProperty("trojita-imap-delayed-fetch-part", 0); 0488 helperSyncBNoMessages(); 0489 cServer("* 1 EXISTS\r\n"); 0490 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); 0491 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); 0492 QCOMPARE(model->rowCount(msgListB), 1); 0493 QModelIndex msg = msgListB.model()->index(0, 0, msgListB); 0494 QVERIFY(msg.isValid()); 0495 QCOMPARE(model->rowCount(msg), 0); 0496 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); 0497 cServer(helperCreateTrivialEnvelope(1, 333, QStringLiteral("subj"), QStringLiteral("foo@example.org"), bodystructure) 0498 + t.last("OK fetched\r\n")); 0499 cEmpty(); 0500 QVERIFY(model->rowCount(msg) > 0); 0501 Cryptography::MessageModel msgModel(0, msg); 0502 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0503 # ifdef TROJITA_HAVE_GPGMEPP 0504 msgModel.registerPartHandler(std::make_shared<Cryptography::GpgMeReplacer>()); 0505 # endif 0506 #endif 0507 QModelIndex mappedMsg = msgModel.index(0,0); 0508 QVERIFY(mappedMsg.isValid()); 0509 QVERIFY(msgModel.rowCount(mappedMsg) > 0); 0510 0511 QModelIndex data = mappedMsg.model()->index(0, 0, mappedMsg); 0512 QVERIFY(data.isValid()); 0513 #ifdef TROJITA_HAVE_CRYPTO_MESSAGES 0514 QCOMPARE(msgModel.rowCount(mappedMsg), 1); 0515 QCOMPARE(msgModel.rowCount(data), 0); 0516 QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), false); 0517 0518 cClientRegExp(t.mk(fetchRegex)); 0519 auto fetchResp = t.last("NO offline\r\n"); 0520 LibMailboxSync::setModelNetworkPolicy(model, Imap::Mailbox::NETWORK_OFFLINE); 0521 cClient(t.mk("LOGOUT\r\n")); 0522 cServer(fetchResp + t.last("OK logout\r\n")); 0523 0524 QSignalSpy qcaErrorSpy(&msgModel, SIGNAL(error(const QModelIndex &,QString,QString))); 0525 0526 int i = 0; 0527 while (data.isValid() && data.data(Imap::Mailbox::RolePartCryptoNotFinishedYet).toBool() && qcaErrorSpy.empty() && i++ < 1000) { 0528 QTest::qWait(10); 0529 } 0530 // allow for event processing, so that the model can retrieve the results 0531 QCoreApplication::processEvents(); 0532 QVERIFY(!data.data(Imap::Mailbox::RolePartCryptoNotFinishedYet).toBool()); 0533 0534 QCOMPARE(data.data(Imap::Mailbox::RolePartCryptoTLDR), QVariant(QStringLiteral("Data Unavailable"))); 0535 QCOMPARE(msgModel.rowCount(data), 2); 0536 0537 if (!qcaErrorSpy.isEmpty()) { 0538 qDebug() << "Unexpected failure in crypto"; 0539 for (int i = 0; i < qcaErrorSpy.size(); ++i) { 0540 qDebug() << qcaErrorSpy[i][1].toString(); 0541 qDebug() << qcaErrorSpy[i][2].toString(); 0542 } 0543 } 0544 0545 // We're offline, we cannot call cEmpty(), that would assert-crash due to no active parsers 0546 //cEmpty(); 0547 0548 QVERIFY(errorSpy->empty()); 0549 #else 0550 QCOMPARE(msgModel.rowCount(data), 2); 0551 QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), true); 0552 cEmpty(); 0553 0554 QSKIP("Some tests were skipped because this build doesn't have GpgME++ support"); 0555 #endif 0556 } 0557 0558 void CryptographyPGPTest::testOffline_data() 0559 { 0560 QTest::addColumn<QByteArray>("bodystructure"); 0561 QTest::addColumn<QByteArray>("fetchRegex"); 0562 0563 QTest::newRow("signed") 0564 << QByteArray("(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 423 14 NIL NIL NIL NIL)" 0565 "(\"application\" \"pgp-signature\" NIL NIL NIL \"7bit\" 851 NIL NIL NIL NIL)" 0566 " \"signed\" (\"boundary\" \"=-=-=\" \"micalg\" \"pgp-sha256\" \"protocol\" \"application/pgp-signature\")" 0567 " NIL NIL NIL") 0568 << QByteArray("UID FETCH 333 \\((BODY\\.PEEK\\[(2|1|1\\.MIME)\\] ?){3}\\)"); 0569 0570 QTest::newRow("encrypted") 0571 << bsEncrypted 0572 << QByteArray("UID FETCH 333 \\((BODY\\.PEEK\\[(1|2)\\] ?){2}\\)"); 0573 } 0574 0575 QTEST_GUILESS_MAIN(CryptographyPGPTest)