From kde-commits Mon Jun 20 18:53:56 2016 From: =?utf-8?q?Thomas_L=C3=BCbking?= Date: Mon, 20 Jun 2016 18:53:56 +0000 To: kde-commits Subject: [trojita] /: Crypto: gracefully handle unavailable data Message-Id: X-MARC-Message: https://marc.info/?l=kde-commits&m=146644884701755 Git commit a80757e6e55bbdfd1617462636ff851a47c3c801 by Thomas L=C3=BCbking,= on behalf of Jan Kundr=C3=A1t. Committed on 18/06/2016 at 08:05. Pushed by gerrit into branch 'master'. Crypto: gracefully handle unavailable data The crypto code should be transparent for network failures, that is, it should act as if the signature/encryption was not available for some reason, and should just use the underlying data in whatever form it's currently available. Change-Id: I61816d306b17fbcbfc63d04188cd563426606ffb M +14 -0 src/Cryptography/GpgMe++.cpp M +94 -0 tests/Cryptography/test_Cryptography_PGP.cpp M +2 -0 tests/Cryptography/test_Cryptography_PGP.h http://commits.kde.org/trojita/a80757e6e55bbdfd1617462636ff851a47c3c801 diff --git a/src/Cryptography/GpgMe++.cpp b/src/Cryptography/GpgMe++.cpp index 414dcb6..4285799 100644 --- a/src/Cryptography/GpgMe++.cpp +++ b/src/Cryptography/GpgMe++.cpp @@ -630,6 +630,13 @@ void GpgMeSigned::handleDataChanged(const QModelIndex = &topLeft, const QModelInde Q_ASSERT(m_plaintextPart.isValid()); Q_ASSERT(m_plaintextMimePart.isValid()); Q_ASSERT(m_signaturePart.isValid()); + if (m_plaintextPart.data(RoleIsUnavailable).toBool() || m_plaintextMim= ePart.data(RoleIsUnavailable).toBool() + || m_signaturePart.data(RoleIsUnavailable).toBool() || m_enclo= singMessage.data(RoleIsUnavailable).toBool()) { + forwardFailure(tr("Data Unavailable"), + tr("Some data are not available, perhaps due to an = offline network connection"), + QStringLiteral("state-offline")); + return; + } if (!m_plaintextPart.data(RoleIsFetched).toBool() || !m_plaintextMimeP= art.data(RoleIsFetched).toBool() || !m_signaturePart.data(RoleIsFetched).toBool() || !m_enclosingM= essage.data(RoleMessageEnvelope).isValid()) { return; @@ -766,6 +773,13 @@ void GpgMeEncrypted::handleDataChanged(const QModelInd= ex &topLeft, const QModelI } Q_ASSERT(m_versionPart.isValid()); Q_ASSERT(m_encPart.isValid()); + if (m_versionPart.data(RoleIsUnavailable).toBool() || m_encPart.data(R= oleIsUnavailable).toBool() + || m_enclosingMessage.data(RoleIsUnavailable).toBool()) { + forwardFailure(tr("Data Unavailable"), + tr("Cannot decrypt. Some data are not available, pe= rhaps due to an offline network connection."), + QStringLiteral("state-offline")); + return; + } if (!m_versionPart.data(RoleIsFetched).toBool() || !m_encPart.data(Rol= eIsFetched).toBool() || !m_enclosingMessage.data(RoleMessageEnvelope).isValid()) { return; diff --git a/tests/Cryptography/test_Cryptography_PGP.cpp b/tests/Cryptogra= phy/test_Cryptography_PGP.cpp index 99694c5..c2b4eb7 100644 --- a/tests/Cryptography/test_Cryptography_PGP.cpp +++ b/tests/Cryptography/test_Cryptography_PGP.cpp @@ -478,4 +478,98 @@ void CryptographyPGPTest::testMalformed_data() = } = +/** @short Check operation when some data are not available */ +void CryptographyPGPTest::testOffline() +{ + QFETCH(QByteArray, bodystructure); + QFETCH(QByteArray, fetchRegex); + + model->setProperty("trojita-imap-delayed-fetch-part", 0); + helperSyncBNoMessages(); + cServer("* 1 EXISTS\r\n"); + cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); + cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); + QCOMPARE(model->rowCount(msgListB), 1); + QModelIndex msg =3D msgListB.child(0, 0); + QVERIFY(msg.isValid()); + QCOMPARE(model->rowCount(msg), 0); + cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); + cServer(helperCreateTrivialEnvelope(1, 333, QStringLiteral("subj"), QS= tringLiteral("foo@example.org"), bodystructure) + + t.last("OK fetched\r\n")); + cEmpty(); + QVERIFY(model->rowCount(msg) > 0); + Cryptography::MessageModel msgModel(0, msg); +#ifdef TROJITA_HAVE_CRYPTO_MESSAGES +# ifdef TROJITA_HAVE_GPGMEPP + msgModel.registerPartHandler(std::make_shared()); +# endif +#endif + QModelIndex mappedMsg =3D msgModel.index(0,0); + QVERIFY(mappedMsg.isValid()); + QVERIFY(msgModel.rowCount(mappedMsg) > 0); + + QModelIndex data =3D mappedMsg.child(0, 0); + QVERIFY(data.isValid()); +#ifdef TROJITA_HAVE_CRYPTO_MESSAGES + QCOMPARE(msgModel.rowCount(mappedMsg), 1); + QCOMPARE(msgModel.rowCount(data), 0); + QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), false); + + cClientRegExp(t.mk(fetchRegex)); + auto fetchResp =3D t.last("NO offline\r\n"); + LibMailboxSync::setModelNetworkPolicy(model, Imap::Mailbox::NETWORK_OF= FLINE); + cClient(t.mk("LOGOUT\r\n")); + cServer(fetchResp + t.last("OK logout\r\n")); + + QSignalSpy qcaErrorSpy(&msgModel, SIGNAL(error(const QModelIndex &,QSt= ring,QString))); + + int i =3D 0; + while (data.isValid() && data.data(Imap::Mailbox::RolePartCryptoNotFin= ishedYet).toBool() && qcaErrorSpy.empty() && i++ < 1000) { + QTest::qWait(10); + } + // allow for event processing, so that the model can retrieve the resu= lts + QCoreApplication::processEvents(); + + QCOMPARE(data.data(Imap::Mailbox::RolePartCryptoNotFinishedYet), QVari= ant(false)); + QCOMPARE(data.data(Imap::Mailbox::RolePartCryptoTLDR), QVariant(QStrin= gLiteral("Data Unavailable"))); + QCOMPARE(msgModel.rowCount(data), 2); + + if (!qcaErrorSpy.isEmpty()) { + qDebug() << "Unexpected failure in crypto"; + for (int i =3D 0; i < qcaErrorSpy.size(); ++i) { + qDebug() << qcaErrorSpy[i][1].toString(); + qDebug() << qcaErrorSpy[i][2].toString(); + } + } + + // We're offline, we cannot call cEmpty(), that would assert-crash due= to no active parsers + //cEmpty(); + + QVERIFY(errorSpy->empty()); +#else + QCOMPARE(msgModel.rowCount(data), 2); + QCOMPARE(data.data(Imap::Mailbox::RoleIsFetched).toBool(), true); + cEmpty(); + + QSKIP("Some tests were skipped because this build doesn't have GpgME++= support"); +#endif +} + +void CryptographyPGPTest::testOffline_data() +{ + QTest::addColumn("bodystructure"); + QTest::addColumn("fetchRegex"); + + QTest::newRow("signed") + << QByteArray("(\"text\" \"plain\" (\"charset\" \"us-ascii\") = NIL NIL \"7bit\" 423 14 NIL NIL NIL NIL)" + "(\"application\" \"pgp-signature\" NIL NIL NIL \"7bit\" 851 N= IL NIL NIL NIL)" + " \"signed\" (\"boundary\" \"=3D-=3D-=3D\" \"micalg\" \"pgp-sh= a256\" \"protocol\" \"application/pgp-signature\")" + " NIL NIL NIL") + << QByteArray("UID FETCH 333 \\((BODY\\.PEEK\\[(2|1|1\\.MIME)\= \] ?){3}\\)"); + + QTest::newRow("encrypted") + << bsEncrypted + << QByteArray("UID FETCH 333 \\((BODY\\.PEEK\\[(1|2)\\] ?){2}\= \)"); +} + QTEST_GUILESS_MAIN(CryptographyPGPTest) diff --git a/tests/Cryptography/test_Cryptography_PGP.h b/tests/Cryptograph= y/test_Cryptography_PGP.h index 9cf3946..5eff0d5 100644 --- a/tests/Cryptography/test_Cryptography_PGP.h +++ b/tests/Cryptography/test_Cryptography_PGP.h @@ -40,6 +40,8 @@ private Q_SLOTS: void testVerification_data(); void testMalformed(); void testMalformed_data(); + void testOffline(); + void testOffline_data(); }; = Q_DECLARE_METATYPE(CryptographyPGPTest::pathList)