File indexing completed on 2025-02-16 04:59:22
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 <QTest> 0024 #include <QNetworkRequest> 0025 #include <QNetworkReply> 0026 #include <QStandardItemModel> 0027 0028 #include "test_Cryptography_MessageModel.h" 0029 #include "configure.cmake.h" 0030 #include "Cryptography/MessageModel.h" 0031 #include "Cryptography/MessagePart.h" 0032 #ifdef TROJITA_HAVE_MIMETIC 0033 #include "Cryptography/LocalMimeParser.h" 0034 #endif 0035 #include "Imap/data.h" 0036 #include "Imap/Model/ItemRoles.h" 0037 #include "Imap/Model/MailboxTree.h" 0038 #include "Streams/FakeSocket.h" 0039 0040 /** @short Verify passthrough of existing MIME parts without modifications */ 0041 void CryptographyMessageModelTest::testImapMessageParts() 0042 { 0043 QFETCH(QByteArray, bodystructure); 0044 QFETCH(QByteArray, partId); 0045 QFETCH(pathList, path); 0046 QFETCH(QByteArray, pathToPart); 0047 QFETCH(QByteArray, data); 0048 0049 // By default, there's a 50ms delay between the time we request a part download and the time it actually happens. 0050 // That's too long for a unit test. 0051 model->setProperty("trojita-imap-delayed-fetch-part", 0); 0052 0053 helperSyncBNoMessages(); 0054 cServer("* 1 EXISTS\r\n"); 0055 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); 0056 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); 0057 0058 QCOMPARE(model->rowCount(msgListB), 1); 0059 QModelIndex msg = msgListB.model()->index(0, 0, msgListB); 0060 QVERIFY(msg.isValid()); 0061 QCOMPARE(model->rowCount(msg), 0); 0062 Cryptography::MessageModel msgModel(0, msg); 0063 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); 0064 cServer("* 1 FETCH (UID 333 BODYSTRUCTURE (" + bodystructure + "))\r\n" + t.last("OK fetched\r\n")); 0065 cEmpty(); 0066 QVERIFY(model->rowCount(msg) > 0); 0067 QModelIndex mappedMsg = msgModel.index(0,0); 0068 QVERIFY(mappedMsg.isValid()); 0069 QVERIFY(msgModel.rowCount(mappedMsg) > 0); 0070 0071 QModelIndex mappedPart = mappedMsg; 0072 for (QList<QPair<int,int> >::const_iterator it = path.constBegin(), end = path.constEnd(); it != end; ++it) { 0073 mappedPart = mappedPart.model()->index(it->first, it->second, mappedPart); 0074 QVERIFY(mappedPart.isValid()); 0075 } 0076 QCOMPARE(mappedPart.data(Imap::Mailbox::RolePartData).toString(), QString()); 0077 cClient(t.mk(QByteArray("UID FETCH 333 (BODY.PEEK[" + partId + "])\r\n"))); 0078 cServer("* 1 FETCH (UID 333 BODY[" + partId + "] " + asLiteral(data) + ")\r\n" + t.last("OK fetched\r\n")); 0079 0080 QCOMPARE(mappedPart.data(Imap::Mailbox::RolePartData).toByteArray(), data); 0081 QCOMPARE(mappedPart.data(Imap::Mailbox::RolePartPathToPart).toByteArray(), pathToPart); 0082 0083 cEmpty(); 0084 QVERIFY(errorSpy->isEmpty()); 0085 } 0086 0087 void CryptographyMessageModelTest::testImapMessageParts_data() 0088 { 0089 QTest::addColumn<QByteArray>("bodystructure"); 0090 QTest::addColumn<QByteArray>("partId"); 0091 QTest::addColumn<QList<QPair<int,int> > >("path"); 0092 QTest::addColumn<QByteArray>("pathToPart"); 0093 QTest::addColumn<QByteArray>("data"); 0094 0095 QTest::newRow("plaintext-root") 0096 << bsPlaintext 0097 << QByteArray("1") 0098 << (pathList() << qMakePair(0,0)) 0099 << QByteArray("/0") 0100 << QByteArray("blesmrt"); 0101 0102 QTest::newRow("torture-plaintext") 0103 << bsTortureTest 0104 << QByteArray("1") 0105 << (pathList() << qMakePair(0,0) << qMakePair(0,0)) 0106 << QByteArray("/0/0") 0107 << QByteArray("plaintext"); 0108 0109 QTest::newRow("torture-richtext") 0110 << bsTortureTest 0111 << QByteArray("2.2.1") 0112 << (pathList() << qMakePair(0,0) << qMakePair(1,0) 0113 << qMakePair(0,0) << qMakePair(1,0) << qMakePair(0,0)) 0114 << QByteArray("/0/1/0/1/0") 0115 << QByteArray("text richtext"); 0116 } 0117 0118 /** @short Verify building and retrieving of a custom MIME tree structure */ 0119 void CryptographyMessageModelTest::testCustomMessageParts() 0120 { 0121 #if defined(__has_feature) 0122 # if __has_feature(address_sanitizer) 0123 // better would be checking for UBSAN, but I don't think that there's an appropriate feature for this 0124 # define SKIP_QSTANDARDITEMMODEL 0125 # endif 0126 #endif 0127 0128 #ifdef SKIP_QSTANDARDITEMMODEL 0129 QSKIP("ASAN build, https://bugreports.qt.io/browse/QTBUG-56027"); 0130 #else 0131 // Initialize model with a root item 0132 QStandardItemModel minimal; 0133 QStandardItem *dummy_root = new QStandardItem(); 0134 QStandardItem *root_mime = new QStandardItem(QStringLiteral("multipart/mixed")); 0135 dummy_root->appendRow(root_mime); 0136 minimal.invisibleRootItem()->appendRow(dummy_root); 0137 0138 // Make sure we didn't mess up until here 0139 QModelIndex index = minimal.index(0, 0); 0140 QVERIFY(index.model()->index(0, 0, index).isValid()); 0141 QCOMPARE(index.model()->index(0, 0, index).data(), root_mime->data(Qt::DisplayRole)); 0142 0143 Cryptography::MessageModel msgModel(0, index); 0144 0145 QModelIndex rootPartIndex = msgModel.index(0,0).model()->index(0, 0, msgModel.index(0, 0)); 0146 0147 QCOMPARE(rootPartIndex.data(), root_mime->data(Qt::DisplayRole)); 0148 0149 Cryptography::LocalMessagePart *localRoot = new Cryptography::LocalMessagePart(nullptr, 0, QByteArrayLiteral("multipart/mixed")); 0150 Cryptography::LocalMessagePart *localText = new Cryptography::LocalMessagePart(localRoot, 0, QByteArrayLiteral("text/plain")); 0151 localText->setData(QByteArrayLiteral("foobar")); 0152 localRoot->setChild(0, Cryptography::MessagePart::Ptr(localText)); 0153 Cryptography::LocalMessagePart *localHtml = new Cryptography::LocalMessagePart(localRoot, 1, QByteArrayLiteral("text/html")); 0154 localHtml->setData(QByteArrayLiteral("<html>foobar</html>")); 0155 localRoot->setChild(1, Cryptography::MessagePart::Ptr(localHtml)); 0156 0157 msgModel.insertSubtree(rootPartIndex, Cryptography::MessagePart::Ptr(localRoot)); 0158 0159 QVERIFY(msgModel.rowCount(rootPartIndex) > 0); 0160 0161 QModelIndex localRootIndex = rootPartIndex.model()->index(0, 0, rootPartIndex); 0162 QVERIFY(localRootIndex.isValid()); 0163 QCOMPARE(localRootIndex.data(Imap::Mailbox::RolePartMimeType), localRoot->data(Imap::Mailbox::RolePartMimeType)); 0164 QModelIndex localTextIndex = localRootIndex.model()->index(0, 0, localRootIndex); 0165 QVERIFY(localTextIndex.isValid()); 0166 QCOMPARE(localTextIndex.data(Imap::Mailbox::RolePartMimeType), localText->data(Imap::Mailbox::RolePartMimeType)); 0167 QModelIndex localHtmlIndex = localRootIndex.model()->index(1, 0, localRootIndex); 0168 QVERIFY(localHtmlIndex.isValid()); 0169 QCOMPARE(localHtmlIndex.data(Imap::Mailbox::RolePartMimeType), localHtml->data(Imap::Mailbox::RolePartMimeType)); 0170 #endif 0171 } 0172 0173 /** @short Check adding a custom MIME tree structure to an existing message 0174 * 0175 * This test fetches the structure of an IMAP message and adds some custom 0176 * structure to that message. Adding random data as child of a text/plain 0177 * MIME part does not make sense semantically but that's not what we want to 0178 * test here. 0179 */ 0180 void CryptographyMessageModelTest::testMixedMessageParts() 0181 { 0182 0183 // By default, there's a 50ms delay between the time we request a part download and the time it actually happens. 0184 // That's too long for a unit test. 0185 model->setProperty("trojita-imap-delayed-fetch-part", 0); 0186 0187 helperSyncBNoMessages(); 0188 cServer("* 1 EXISTS\r\n"); 0189 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); 0190 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); 0191 0192 QCOMPARE(model->rowCount(msgListB), 1); 0193 QModelIndex msg = msgListB.model()->index(0, 0, msgListB); 0194 QVERIFY(msg.isValid()); 0195 QCOMPARE(model->rowCount(msg), 0); 0196 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); 0197 const QByteArray bsEncrypted("\"encrypted\" (\"protocol\" \"application/pgp-encrypted\" \"boundary\" \"trojita=_7cf0b2b6-64c6-41ad-b381-853caf492c54\") NIL NIL NIL"); 0198 cServer("* 1 FETCH (UID 333 BODYSTRUCTURE (" + bsEncrypted + "))\r\n" + t.last("OK fetched\r\n")); 0199 cEmpty(); 0200 QVERIFY(model->rowCount(msg) > 0); 0201 Cryptography::MessageModel msgModel(0, msg); 0202 QModelIndex mappedMsg = msgModel.index(0,0); 0203 QVERIFY(mappedMsg.isValid()); 0204 QVERIFY(msgModel.rowCount(mappedMsg) > 0); 0205 QCOMPARE(msgModel.parent(mappedMsg), QModelIndex()); 0206 0207 QModelIndex mappedPart = mappedMsg.model()->index(0, 0, mappedMsg); 0208 QVERIFY(mappedPart.isValid()); 0209 QCOMPARE(mappedPart.data(Imap::Mailbox::RolePartPathToPart).toByteArray(), QByteArrayLiteral("/0")); 0210 0211 cEmpty(); 0212 QVERIFY(errorSpy->isEmpty()); 0213 0214 // Add some custom structure to the given IMAP message 0215 Cryptography::LocalMessagePart *localRoot = new Cryptography::LocalMessagePart(nullptr, 0, QByteArrayLiteral("multipart/mixed")); 0216 Cryptography::LocalMessagePart *localText = new Cryptography::LocalMessagePart(localRoot, 0, QByteArrayLiteral("text/plain")); 0217 localText->setData(QByteArrayLiteral("foobar")); 0218 localRoot->setChild(0, Cryptography::MessagePart::Ptr(localText)); 0219 Cryptography::LocalMessagePart *localHtml = new Cryptography::LocalMessagePart(localRoot, 1, QByteArrayLiteral("text/html")); 0220 localHtml->setData(QByteArrayLiteral("<html>foobar</html>")); 0221 localRoot->setChild(1, Cryptography::MessagePart::Ptr(localHtml)); 0222 0223 msgModel.insertSubtree(mappedPart, Cryptography::MessagePart::Ptr(localRoot)); 0224 0225 QVERIFY(msgModel.rowCount(mappedPart) > 0); 0226 0227 QModelIndex localRootIndex = mappedPart.model()->index(0, 0, mappedPart); 0228 QVERIFY(localRootIndex.isValid()); 0229 QCOMPARE(localRootIndex.data(Imap::Mailbox::RolePartMimeType), localRoot->data(Imap::Mailbox::RolePartMimeType)); 0230 QModelIndex localTextIndex = localRootIndex.model()->index(0, 0, localRootIndex); 0231 QVERIFY(localTextIndex.isValid()); 0232 QCOMPARE(localTextIndex.data(Imap::Mailbox::RolePartMimeType), localText->data(Imap::Mailbox::RolePartMimeType)); 0233 QModelIndex localHtmlIndex = localRootIndex.model()->index(1, 0, localRootIndex); 0234 QVERIFY(localHtmlIndex.isValid()); 0235 QCOMPARE(localHtmlIndex.data(Imap::Mailbox::RolePartMimeType), localHtml->data(Imap::Mailbox::RolePartMimeType)); 0236 } 0237 0238 /** @short Verify that we can handle data from Mimetic and use them locally */ 0239 void CryptographyMessageModelTest::testLocalMimeParsing() 0240 { 0241 #ifdef TROJITA_HAVE_MIMETIC 0242 model->setProperty("trojita-imap-delayed-fetch-part", 0); 0243 helperSyncBNoMessages(); 0244 cServer("* 1 EXISTS\r\n"); 0245 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); 0246 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); 0247 QCOMPARE(model->rowCount(msgListB), 1); 0248 QModelIndex msg = msgListB.model()->index(0, 0, msgListB); 0249 QVERIFY(msg.isValid()); 0250 QCOMPARE(model->rowCount(msg), 0); 0251 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); 0252 0253 Cryptography::MessageModel msgModel(0, msg); 0254 msgModel.registerPartHandler(std::make_shared<Cryptography::LocalMimeMessageParser>()); 0255 0256 const QByteArray bsTopLevelRfc822Message = QByteArrayLiteral( 0257 "\"messaGe\" \"rFc822\" NIL NIL NIL \"7bit\" 1511 (\"Thu, 8 Aug 2013 09:02:50 +0200\" " 0258 "\"Re: Your GSoC status\" ((\"Pali\" NIL \"pali.rohar\" \"gmail.com\")) " 0259 "((\"Pali\" NIL \"pali.rohar\" \"gmail.com\")) " 0260 "((\"Pali\" NIL \"pali.rohar\" \"gmail.com\")) ((\"Jan\" NIL \"jkt\" \"flaska.net\")) " 0261 "NIL NIL NIL \"<201308080902.51071@pali>\") " 0262 "((\"Text\" \"Plain\" (\"ChaRset\" \"uTf-8\") NIL NIL \"qUoted-printable\" 632 20 NIL NIL NIL NIL)" 0263 "(\"applicatioN\" \"pGp-signature\" (\"Name\" \"signature.asc\") NIL " 0264 "\"This is a digitally signed message part.\" \"7bit\" 205 NIL NIL NIL NIL) \"signed\" " 0265 "(\"boundary\" \"nextPart2106994.VznBGuL01i\" \"protocol\" \"application/pgp-signature\" \"micalg\" \"pgp-sha1\") " 0266 "NIL NIL NIL) 51 NIL NIL NIL NIL"); 0267 0268 cServer("* 1 FETCH (UID 333 BODYSTRUCTURE (" + bsTopLevelRfc822Message + "))\r\n" + t.last("OK fetched\r\n")); 0269 cEmpty(); 0270 QVERIFY(model->rowCount(msg) > 0); 0271 auto mappedMsg = msgModel.index(0,0); 0272 QVERIFY(mappedMsg.isValid()); 0273 QVERIFY(msgModel.rowCount(mappedMsg) > 0); 0274 0275 QPersistentModelIndex msgRoot = mappedMsg.model()->index(0, 0, mappedMsg); 0276 QModelIndex formerMsgRoot = msgRoot; 0277 QVERIFY(msgRoot.isValid()); 0278 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartPathToPart).toByteArray(), 0279 QByteArrayLiteral("/0")); 0280 0281 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("message/rfc822")); 0282 QCOMPARE(msgRoot.internalPointer(), formerMsgRoot.internalPointer()); 0283 QCOMPARE(msgModel.rowCount(msgRoot), 0); 0284 cClientRegExp(t.mk("UID FETCH 333 \\(BODY\\.PEEK\\[1\\.(TEXT|HEADER)\\] BODY\\.PEEK\\[1\\.(TEXT|HEADER)\\]\\)")); 0285 QByteArray myHeader = QByteArrayLiteral("Content-Type: mULTIpart/miXed; boundary=sep\r\n" 0286 "MIME-Version: 1.0\r\n" 0287 "Subject: =?ISO-8859-2?B?7Lno+L794e3p?=\r\n" 0288 "From: =?utf-8?B?xJs=?= <1@example.org>\r\n" 0289 "Sender: =?utf-8?B?xJq=?= <0@example.org>\r\n" 0290 "To: =?utf-8?B?xJs=?= <2@example.org>, =?iso-8859-1?Q?=E1?= <3@example.org>\r\n" 0291 "Cc: =?utf-8?B?xJz=?= <4@example.org>\r\n" 0292 "reply-to: =?utf-8?B?xJm=?= <r@example.org>\r\n" 0293 "BCC: =?utf-8?B?xJr=?= <5@example.org>\r\n\r\n"); 0294 QByteArray myBinaryBody = QByteArrayLiteral("This is the actual message body.\r\n"); 0295 QString myUnicode = QStringLiteral("Λέσβος"); 0296 QByteArray myBody = QByteArrayLiteral("preamble of a MIME message\r\n--sep\r\n" 0297 "Content-Type: text/plain; charset=\"utf-8\"\r\nContent-Transfer-Encoding: base64\r\n\r\n") 0298 + myUnicode.toUtf8().toBase64() 0299 + QByteArrayLiteral("\r\n--sep\r\nContent-Type: pWned/NOw\r\n\r\n") 0300 + myBinaryBody 0301 + QByteArrayLiteral("\r\n--sep--\r\n"); 0302 cServer("* 1 FETCH (UID 333 BODY[1.TEXT] " + asLiteral(myBody) + " BODY[1.HEADER] " + asLiteral(myHeader) + ")\r\n" 0303 + t.last("OK fetched\r\n")); 0304 0305 // the part got replaced, so our QModelIndex should be invalid now 0306 QVERIFY(msgRoot.internalPointer() != formerMsgRoot.internalPointer()); 0307 0308 QCOMPARE(msgModel.rowCount(msgRoot), 1); 0309 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("message/rfc822")); 0310 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageSubject).toString(), QStringLiteral("ěščřžýáíé")); 0311 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageSender), 0312 QVariant(QVariantList() << 0313 (QStringList() << QStringLiteral("Ě") << QString() << QStringLiteral("0") << QStringLiteral("example.org")))); 0314 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageFrom), 0315 QVariant(QVariantList() << 0316 (QStringList() << QStringLiteral("ě") << QString() << QStringLiteral("1") << QStringLiteral("example.org")))); 0317 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageTo), 0318 QVariant(QVariantList() << 0319 (QStringList() << QStringLiteral("ě") << QString() << QStringLiteral("2") << QStringLiteral("example.org")) << 0320 (QStringList() << QStringLiteral("á") << QString() << QStringLiteral("3") << QStringLiteral("example.org")))); 0321 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageCc), 0322 QVariant(QVariantList() << 0323 (QStringList() << QStringLiteral("Ĝ") << QString() << QStringLiteral("4") << QStringLiteral("example.org")))); 0324 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageBcc), 0325 QVariant(QVariantList() << 0326 (QStringList() << QStringLiteral("Ě") << QString() << QStringLiteral("5") << QStringLiteral("example.org")))); 0327 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageReplyTo), 0328 QVariant(QVariantList() << 0329 (QStringList() << QStringLiteral("ę") << QString() << QStringLiteral("r") << QStringLiteral("example.org")))); 0330 0331 // NOTE: the OFFSET_MIME parts are not implemented; that's more or less on purpose because they aren't being used through 0332 // the rest of the code so far. 0333 0334 auto mHeader = msgRoot.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_HEADER, msgRoot); 0335 QVERIFY(mHeader.isValid()); 0336 auto mText = msgRoot.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_TEXT, msgRoot); 0337 QVERIFY(mText.isValid()); 0338 auto mMime = msgRoot.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_MIME, msgRoot); 0339 QVERIFY(!mMime.isValid()); 0340 auto mRaw = msgRoot.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_RAW_CONTENTS, msgRoot); 0341 QVERIFY(mRaw.isValid()); 0342 // We cannot compare them for an exact byte-equality because Mimetic apparently mangles the data a bit, 0343 // for example there's an extra space after the comma in the To field in this case :( 0344 QCOMPARE(mHeader.data(Imap::Mailbox::RolePartData).toByteArray().left(30), myHeader.left(30)); 0345 QCOMPARE(mHeader.data(Imap::Mailbox::RolePartData).toByteArray().right(4), QByteArrayLiteral("\r\n\r\n")); 0346 QCOMPARE(mText.data(Imap::Mailbox::RolePartData).toByteArray(), myBody); 0347 0348 // still that new C++11 toy, oh yeah :) 0349 using bodyFldParam_t = std::result_of<decltype(&Imap::Mailbox::TreeItemPart::bodyFldParam)(Imap::Mailbox::TreeItemPart)>::type; 0350 bodyFldParam_t expectedBodyFldParam; 0351 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartBodyFldParam).value<bodyFldParam_t>(), expectedBodyFldParam); 0352 0353 auto multipartIdx = msgRoot.model()->index(0, 0, msgRoot); 0354 QVERIFY(multipartIdx.isValid()); 0355 QCOMPARE(multipartIdx.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("multipart/mixed")); 0356 QCOMPARE(msgModel.rowCount(multipartIdx), 2); 0357 expectedBodyFldParam.clear(); 0358 expectedBodyFldParam["BOUNDARY"] = "sep"; 0359 QCOMPARE(multipartIdx.data(Imap::Mailbox::RolePartBodyFldParam).value<bodyFldParam_t>(), expectedBodyFldParam); 0360 0361 auto c1 = multipartIdx.model()->index(0, 0, multipartIdx); 0362 QVERIFY(c1.isValid()); 0363 QCOMPARE(msgModel.rowCount(c1), 0); 0364 QCOMPARE(c1.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("text/plain")); 0365 QCOMPARE(c1.data(Imap::Mailbox::RolePartData).toString(), myUnicode); 0366 expectedBodyFldParam.clear(); 0367 expectedBodyFldParam["CHARSET"] = "utf-8"; 0368 QCOMPARE(c1.data(Imap::Mailbox::RolePartBodyFldParam).value<bodyFldParam_t>(), expectedBodyFldParam); 0369 0370 auto c1raw = c1.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_RAW_CONTENTS, c1); 0371 QVERIFY(c1raw.isValid()); 0372 QCOMPARE(c1raw.data(Imap::Mailbox::RolePartData).toByteArray(), myUnicode.toUtf8().toBase64()); 0373 QVERIFY(!c1.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_HEADER, c1).isValid()); 0374 QVERIFY(!c1.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_TEXT, c1).isValid()); 0375 QVERIFY(!c1.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_MIME, c1).isValid()); 0376 0377 auto c2 = multipartIdx.model()->index(1, 0, multipartIdx); 0378 QVERIFY(c2.isValid()); 0379 QCOMPARE(msgModel.rowCount(c2), 0); 0380 QCOMPARE(c2.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("pwned/now")); 0381 QCOMPARE(c2.data(Imap::Mailbox::RolePartData).toByteArray(), myBinaryBody); 0382 0383 auto c2raw = c2.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_RAW_CONTENTS, c2); 0384 QVERIFY(c2raw.isValid()); 0385 QCOMPARE(c2raw.data(Imap::Mailbox::RolePartData).toByteArray(), myBinaryBody); 0386 QVERIFY(!c2.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_HEADER, c2).isValid()); 0387 QVERIFY(!c2.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_TEXT, c2).isValid()); 0388 QVERIFY(!c2.model()->index(0, Imap::Mailbox::TreeItem::OFFSET_MIME, c2).isValid()); 0389 0390 cEmpty(); 0391 QVERIFY(errorSpy->isEmpty()); 0392 #else 0393 QSKIP("Mimetic not available, cannot test LocalMimeMessageParser"); 0394 #endif 0395 } 0396 0397 void CryptographyMessageModelTest::testDelayedLoading() 0398 { 0399 model->setProperty("trojita-imap-delayed-fetch-part", 0); 0400 helperSyncBNoMessages(); 0401 cServer("* 1 EXISTS\r\n"); 0402 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n")); 0403 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n")); 0404 QCOMPARE(model->rowCount(msgListB), 1); 0405 QModelIndex msg = msgListB.model()->index(0, 0, msgListB); 0406 QVERIFY(msg.isValid()); 0407 0408 Cryptography::MessageModel msgModel(0, msg); 0409 msgModel.setObjectName("msgModel"); 0410 0411 cEmpty(); 0412 QCOMPARE(msgModel.rowCount(QModelIndex()), 0); 0413 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n")); 0414 cServer("* 1 FETCH (UID 333 BODYSTRUCTURE (" + bsPlaintext + "))\r\n" + t.last("OK fetched\r\n")); 0415 cEmpty(); 0416 QCOMPARE(model->rowCount(msg), 1); 0417 QCOMPARE(msgModel.rowCount(QModelIndex()), 1); 0418 auto mappedMsg = msgModel.index(0,0); 0419 QVERIFY(mappedMsg.isValid()); 0420 0421 QPersistentModelIndex msgRoot = mappedMsg.model()->index(0, 0, mappedMsg); 0422 QVERIFY(msgRoot.isValid()); 0423 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartPathToPart).toByteArray(), 0424 QByteArrayLiteral("/0")); 0425 } 0426 0427 QTEST_GUILESS_MAIN(CryptographyMessageModelTest)