1 /* Copyright (C) 2014 - 2015 Stephan Platz <trojita@paalsteek.de>
2
3 This file is part of the Trojita Qt IMAP e-mail client,
4 http://trojita.flaska.net/
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License or (at your option) version 3 or any later version
10 accepted by the membership of KDE e.V. (or its successor approved
11 by the membership of KDE e.V.), which shall act as a proxy
12 defined in Section 14 of version 3 of the license.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include <QTest>
24 #include <QNetworkRequest>
25 #include <QNetworkReply>
26 #include <QStandardItemModel>
27
28 #include "test_Cryptography_MessageModel.h"
29 #include "configure.cmake.h"
30 #include "Cryptography/MessageModel.h"
31 #include "Cryptography/MessagePart.h"
32 #ifdef TROJITA_HAVE_MIMETIC
33 #include "Cryptography/LocalMimeParser.h"
34 #endif
35 #include "Imap/data.h"
36 #include "Imap/Model/ItemRoles.h"
37 #include "Imap/Model/MailboxTree.h"
38 #include "Streams/FakeSocket.h"
39
40 /** @short Verify passthrough of existing MIME parts without modifications */
testImapMessageParts()41 void CryptographyMessageModelTest::testImapMessageParts()
42 {
43 QFETCH(QByteArray, bodystructure);
44 QFETCH(QByteArray, partId);
45 QFETCH(pathList, path);
46 QFETCH(QByteArray, pathToPart);
47 QFETCH(QByteArray, data);
48
49 // By default, there's a 50ms delay between the time we request a part download and the time it actually happens.
50 // That's too long for a unit test.
51 model->setProperty("trojita-imap-delayed-fetch-part", 0);
52
53 helperSyncBNoMessages();
54 cServer("* 1 EXISTS\r\n");
55 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n"));
56 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n"));
57
58 QCOMPARE(model->rowCount(msgListB), 1);
59 QModelIndex msg = msgListB.child(0, 0);
60 QVERIFY(msg.isValid());
61 QCOMPARE(model->rowCount(msg), 0);
62 Cryptography::MessageModel msgModel(0, msg);
63 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n"));
64 cServer("* 1 FETCH (UID 333 BODYSTRUCTURE (" + bodystructure + "))\r\n" + t.last("OK fetched\r\n"));
65 cEmpty();
66 QVERIFY(model->rowCount(msg) > 0);
67 QModelIndex mappedMsg = msgModel.index(0,0);
68 QVERIFY(mappedMsg.isValid());
69 QVERIFY(msgModel.rowCount(mappedMsg) > 0);
70
71 QModelIndex mappedPart = mappedMsg;
72 for (QList<QPair<int,int> >::const_iterator it = path.constBegin(), end = path.constEnd(); it != end; ++it) {
73 mappedPart = mappedPart.child(it->first, it->second);
74 QVERIFY(mappedPart.isValid());
75 }
76 QCOMPARE(mappedPart.data(Imap::Mailbox::RolePartData).toString(), QString());
77 cClient(t.mk(QByteArray("UID FETCH 333 (BODY.PEEK[" + partId + "])\r\n")));
78 cServer("* 1 FETCH (UID 333 BODY[" + partId + "] " + asLiteral(data) + ")\r\n" + t.last("OK fetched\r\n"));
79
80 QCOMPARE(mappedPart.data(Imap::Mailbox::RolePartData).toByteArray(), data);
81 QCOMPARE(mappedPart.data(Imap::Mailbox::RolePartPathToPart).toByteArray(), pathToPart);
82
83 cEmpty();
84 QVERIFY(errorSpy->isEmpty());
85 }
86
testImapMessageParts_data()87 void CryptographyMessageModelTest::testImapMessageParts_data()
88 {
89 QTest::addColumn<QByteArray>("bodystructure");
90 QTest::addColumn<QByteArray>("partId");
91 QTest::addColumn<QList<QPair<int,int> > >("path");
92 QTest::addColumn<QByteArray>("pathToPart");
93 QTest::addColumn<QByteArray>("data");
94
95 QTest::newRow("plaintext-root")
96 << bsPlaintext
97 << QByteArray("1")
98 << (pathList() << qMakePair(0,0))
99 << QByteArray("/0")
100 << QByteArray("blesmrt");
101
102 QTest::newRow("torture-plaintext")
103 << bsTortureTest
104 << QByteArray("1")
105 << (pathList() << qMakePair(0,0) << qMakePair(0,0))
106 << QByteArray("/0/0")
107 << QByteArray("plaintext");
108
109 QTest::newRow("torture-richtext")
110 << bsTortureTest
111 << QByteArray("2.2.1")
112 << (pathList() << qMakePair(0,0) << qMakePair(1,0)
113 << qMakePair(0,0) << qMakePair(1,0) << qMakePair(0,0))
114 << QByteArray("/0/1/0/1/0")
115 << QByteArray("text richtext");
116 }
117
118 /** @short Verify building and retrieving of a custom MIME tree structure */
testCustomMessageParts()119 void CryptographyMessageModelTest::testCustomMessageParts()
120 {
121 // Initialize model with a root item
122 QStandardItemModel *minimal = new QStandardItemModel();
123 QStandardItem *dummy_root = new QStandardItem();
124 QStandardItem *root_mime = new QStandardItem(QStringLiteral("multipart/mixed"));
125 dummy_root->appendRow(root_mime);
126 minimal->invisibleRootItem()->appendRow(dummy_root);
127
128 // Make sure we didn't mess up until here
129 QVERIFY(minimal->index(0,0).child(0,0).isValid());
130 QCOMPARE(minimal->index(0,0).child(0,0).data(), root_mime->data(Qt::DisplayRole));
131
132 Cryptography::MessageModel msgModel(0, minimal->index(0,0));
133
134 QModelIndex rootPartIndex = msgModel.index(0,0).child(0,0);
135
136 QCOMPARE(rootPartIndex.data(), root_mime->data(Qt::DisplayRole));
137
138 Cryptography::LocalMessagePart *localRoot = new Cryptography::LocalMessagePart(nullptr, 0, QByteArrayLiteral("multipart/mixed"));
139 Cryptography::LocalMessagePart *localText = new Cryptography::LocalMessagePart(localRoot, 0, QByteArrayLiteral("text/plain"));
140 localText->setData(QByteArrayLiteral("foobar"));
141 localRoot->setChild(0, Cryptography::MessagePart::Ptr(localText));
142 Cryptography::LocalMessagePart *localHtml = new Cryptography::LocalMessagePart(localRoot, 1, QByteArrayLiteral("text/html"));
143 localHtml->setData(QByteArrayLiteral("<html>foobar</html>"));
144 localRoot->setChild(1, Cryptography::MessagePart::Ptr(localHtml));
145
146 msgModel.insertSubtree(rootPartIndex, Cryptography::MessagePart::Ptr(localRoot));
147
148 QVERIFY(msgModel.rowCount(rootPartIndex) > 0);
149
150 QModelIndex localRootIndex = rootPartIndex.child(0, 0);
151 QVERIFY(localRootIndex.isValid());
152 QCOMPARE(localRootIndex.data(Imap::Mailbox::RolePartMimeType), localRoot->data(Imap::Mailbox::RolePartMimeType));
153 QModelIndex localTextIndex = localRootIndex.child(0, 0);
154 QVERIFY(localTextIndex.isValid());
155 QCOMPARE(localTextIndex.data(Imap::Mailbox::RolePartMimeType), localText->data(Imap::Mailbox::RolePartMimeType));
156 QModelIndex localHtmlIndex = localRootIndex.child(1, 0);
157 QVERIFY(localHtmlIndex.isValid());
158 QCOMPARE(localHtmlIndex.data(Imap::Mailbox::RolePartMimeType), localHtml->data(Imap::Mailbox::RolePartMimeType));
159 }
160
161 /** @short Check adding a custom MIME tree structure to an existing message
162 *
163 * This test fetches the structure of an IMAP message and adds some custom
164 * structure to that message. Adding random data as child of a text/plain
165 * MIME part does not make sense semantically but that's not what we want to
166 * test here.
167 */
testMixedMessageParts()168 void CryptographyMessageModelTest::testMixedMessageParts()
169 {
170
171 // By default, there's a 50ms delay between the time we request a part download and the time it actually happens.
172 // That's too long for a unit test.
173 model->setProperty("trojita-imap-delayed-fetch-part", 0);
174
175 helperSyncBNoMessages();
176 cServer("* 1 EXISTS\r\n");
177 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n"));
178 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n"));
179
180 QCOMPARE(model->rowCount(msgListB), 1);
181 QModelIndex msg = msgListB.child(0, 0);
182 QVERIFY(msg.isValid());
183 QCOMPARE(model->rowCount(msg), 0);
184 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n"));
185 const QByteArray bsEncrypted("\"encrypted\" (\"protocol\" \"application/pgp-encrypted\" \"boundary\" \"trojita=_7cf0b2b6-64c6-41ad-b381-853caf492c54\") NIL NIL NIL");
186 cServer("* 1 FETCH (UID 333 BODYSTRUCTURE (" + bsEncrypted + "))\r\n" + t.last("OK fetched\r\n"));
187 cEmpty();
188 QVERIFY(model->rowCount(msg) > 0);
189 Cryptography::MessageModel msgModel(0, msg);
190 QModelIndex mappedMsg = msgModel.index(0,0);
191 QVERIFY(mappedMsg.isValid());
192 QVERIFY(msgModel.rowCount(mappedMsg) > 0);
193 QCOMPARE(msgModel.parent(mappedMsg), QModelIndex());
194
195 QModelIndex mappedPart = mappedMsg.child(0, 0);
196 QVERIFY(mappedPart.isValid());
197 QCOMPARE(mappedPart.data(Imap::Mailbox::RolePartPathToPart).toByteArray(), QByteArrayLiteral("/0"));
198
199 cEmpty();
200 QVERIFY(errorSpy->isEmpty());
201
202 // Add some custom structure to the given IMAP message
203 Cryptography::LocalMessagePart *localRoot = new Cryptography::LocalMessagePart(nullptr, 0, QByteArrayLiteral("multipart/mixed"));
204 Cryptography::LocalMessagePart *localText = new Cryptography::LocalMessagePart(localRoot, 0, QByteArrayLiteral("text/plain"));
205 localText->setData(QByteArrayLiteral("foobar"));
206 localRoot->setChild(0, Cryptography::MessagePart::Ptr(localText));
207 Cryptography::LocalMessagePart *localHtml = new Cryptography::LocalMessagePart(localRoot, 1, QByteArrayLiteral("text/html"));
208 localHtml->setData(QByteArrayLiteral("<html>foobar</html>"));
209 localRoot->setChild(1, Cryptography::MessagePart::Ptr(localHtml));
210
211 msgModel.insertSubtree(mappedPart, Cryptography::MessagePart::Ptr(localRoot));
212
213 QVERIFY(msgModel.rowCount(mappedPart) > 0);
214
215 QModelIndex localRootIndex = mappedPart.child(0, 0);
216 QVERIFY(localRootIndex.isValid());
217 QCOMPARE(localRootIndex.data(Imap::Mailbox::RolePartMimeType), localRoot->data(Imap::Mailbox::RolePartMimeType));
218 QModelIndex localTextIndex = localRootIndex.child(0, 0);
219 QVERIFY(localTextIndex.isValid());
220 QCOMPARE(localTextIndex.data(Imap::Mailbox::RolePartMimeType), localText->data(Imap::Mailbox::RolePartMimeType));
221 QModelIndex localHtmlIndex = localRootIndex.child(1, 0);
222 QVERIFY(localHtmlIndex.isValid());
223 QCOMPARE(localHtmlIndex.data(Imap::Mailbox::RolePartMimeType), localHtml->data(Imap::Mailbox::RolePartMimeType));
224 }
225
226 /** @short Verify that we can handle data from Mimetic and use them locally */
testLocalMimeParsing()227 void CryptographyMessageModelTest::testLocalMimeParsing()
228 {
229 #ifdef TROJITA_HAVE_MIMETIC
230 model->setProperty("trojita-imap-delayed-fetch-part", 0);
231 helperSyncBNoMessages();
232 cServer("* 1 EXISTS\r\n");
233 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n"));
234 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n"));
235 QCOMPARE(model->rowCount(msgListB), 1);
236 QModelIndex msg = msgListB.child(0, 0);
237 QVERIFY(msg.isValid());
238 QCOMPARE(model->rowCount(msg), 0);
239 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n"));
240
241 Cryptography::MessageModel msgModel(0, msg);
242 msgModel.registerPartHandler(std::make_shared<Cryptography::LocalMimeMessageParser>());
243
244 const QByteArray bsTopLevelRfc822Message = QByteArrayLiteral(
245 "\"messaGe\" \"rFc822\" NIL NIL NIL \"7bit\" 1511 (\"Thu, 8 Aug 2013 09:02:50 +0200\" "
246 "\"Re: Your GSoC status\" ((\"Pali\" NIL \"pali.rohar\" \"gmail.com\")) "
247 "((\"Pali\" NIL \"pali.rohar\" \"gmail.com\")) "
248 "((\"Pali\" NIL \"pali.rohar\" \"gmail.com\")) ((\"Jan\" NIL \"jkt\" \"flaska.net\")) "
249 "NIL NIL NIL \"<201308080902.51071@pali>\") "
250 "((\"Text\" \"Plain\" (\"ChaRset\" \"uTf-8\") NIL NIL \"qUoted-printable\" 632 20 NIL NIL NIL NIL)"
251 "(\"applicatioN\" \"pGp-signature\" (\"Name\" \"signature.asc\") NIL "
252 "\"This is a digitally signed message part.\" \"7bit\" 205 NIL NIL NIL NIL) \"signed\" "
253 "(\"boundary\" \"nextPart2106994.VznBGuL01i\" \"protocol\" \"application/pgp-signature\" \"micalg\" \"pgp-sha1\") "
254 "NIL NIL NIL) 51 NIL NIL NIL NIL");
255
256 cServer("* 1 FETCH (UID 333 BODYSTRUCTURE (" + bsTopLevelRfc822Message + "))\r\n" + t.last("OK fetched\r\n"));
257 cEmpty();
258 QVERIFY(model->rowCount(msg) > 0);
259 auto mappedMsg = msgModel.index(0,0);
260 QVERIFY(mappedMsg.isValid());
261 QVERIFY(msgModel.rowCount(mappedMsg) > 0);
262
263 QPersistentModelIndex msgRoot = mappedMsg.child(0, 0);
264 QModelIndex formerMsgRoot = msgRoot;
265 QVERIFY(msgRoot.isValid());
266 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartPathToPart).toByteArray(),
267 QByteArrayLiteral("/0"));
268
269 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("message/rfc822"));
270 QCOMPARE(msgRoot.internalPointer(), formerMsgRoot.internalPointer());
271 QCOMPARE(msgModel.rowCount(msgRoot), 0);
272 cClientRegExp(t.mk("UID FETCH 333 \\(BODY\\.PEEK\\[1\\.(TEXT|HEADER)\\] BODY\\.PEEK\\[1\\.(TEXT|HEADER)\\]\\)"));
273 QByteArray myHeader = QByteArrayLiteral("Content-Type: mULTIpart/miXed; boundary=sep\r\n"
274 "MIME-Version: 1.0\r\n"
275 "Subject: =?ISO-8859-2?B?7Lno+L794e3p?=\r\n"
276 "From: =?utf-8?B?xJs=?= <1@example.org>\r\n"
277 "Sender: =?utf-8?B?xJq=?= <0@example.org>\r\n"
278 "To: =?utf-8?B?xJs=?= <2@example.org>, =?iso-8859-1?Q?=E1?= <3@example.org>\r\n"
279 "Cc: =?utf-8?B?xJz=?= <4@example.org>\r\n"
280 "reply-to: =?utf-8?B?xJm=?= <r@example.org>\r\n"
281 "BCC: =?utf-8?B?xJr=?= <5@example.org>\r\n\r\n");
282 QByteArray myBinaryBody = QByteArrayLiteral("This is the actual message body.\r\n");
283 QString myUnicode = QStringLiteral("Λέσβος");
284 QByteArray myBody = QByteArrayLiteral("preamble of a MIME message\r\n--sep\r\n"
285 "Content-Type: text/plain; charset=\"utf-8\"\r\nContent-Transfer-Encoding: base64\r\n\r\n")
286 + myUnicode.toUtf8().toBase64()
287 + QByteArrayLiteral("\r\n--sep\r\nContent-Type: pWned/NOw\r\n\r\n")
288 + myBinaryBody
289 + QByteArrayLiteral("\r\n--sep--\r\n");
290 cServer("* 1 FETCH (UID 333 BODY[1.TEXT] " + asLiteral(myBody) + " BODY[1.HEADER] " + asLiteral(myHeader) + ")\r\n"
291 + t.last("OK fetched\r\n"));
292
293 // the part got replaced, so our QModelIndex should be invalid now
294 QVERIFY(msgRoot.internalPointer() != formerMsgRoot.internalPointer());
295
296 QCOMPARE(msgModel.rowCount(msgRoot), 1);
297 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("message/rfc822"));
298 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageSubject).toString(), QStringLiteral("ěščřžýáíé"));
299 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageSender),
300 QVariant(QVariantList() <<
301 (QStringList() << QStringLiteral("Ě") << QString() << QStringLiteral("0") << QStringLiteral("example.org"))));
302 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageFrom),
303 QVariant(QVariantList() <<
304 (QStringList() << QStringLiteral("ě") << QString() << QStringLiteral("1") << QStringLiteral("example.org"))));
305 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageTo),
306 QVariant(QVariantList() <<
307 (QStringList() << QStringLiteral("ě") << QString() << QStringLiteral("2") << QStringLiteral("example.org")) <<
308 (QStringList() << QStringLiteral("á") << QString() << QStringLiteral("3") << QStringLiteral("example.org"))));
309 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageCc),
310 QVariant(QVariantList() <<
311 (QStringList() << QStringLiteral("Ĝ") << QString() << QStringLiteral("4") << QStringLiteral("example.org"))));
312 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageBcc),
313 QVariant(QVariantList() <<
314 (QStringList() << QStringLiteral("Ě") << QString() << QStringLiteral("5") << QStringLiteral("example.org"))));
315 QCOMPARE(msgRoot.data(Imap::Mailbox::RoleMessageReplyTo),
316 QVariant(QVariantList() <<
317 (QStringList() << QStringLiteral("ę") << QString() << QStringLiteral("r") << QStringLiteral("example.org"))));
318
319 // NOTE: the OFFSET_MIME parts are not implemented; that's more or less on purpose because they aren't being used through
320 // the rest of the code so far.
321
322 auto mHeader = msgRoot.child(0, Imap::Mailbox::TreeItem::OFFSET_HEADER);
323 QVERIFY(mHeader.isValid());
324 auto mText = msgRoot.child(0, Imap::Mailbox::TreeItem::OFFSET_TEXT);
325 QVERIFY(mText.isValid());
326 auto mMime = msgRoot.child(0, Imap::Mailbox::TreeItem::OFFSET_MIME);
327 QVERIFY(!mMime.isValid());
328 auto mRaw = msgRoot.child(0, Imap::Mailbox::TreeItem::OFFSET_RAW_CONTENTS);
329 QVERIFY(mRaw.isValid());
330 // We cannot compare them for an exact byte-equality because Mimetic apparently mangles the data a bit,
331 // for example there's an extra space after the comma in the To field in this case :(
332 QCOMPARE(mHeader.data(Imap::Mailbox::RolePartData).toByteArray().left(30), myHeader.left(30));
333 QCOMPARE(mHeader.data(Imap::Mailbox::RolePartData).toByteArray().right(4), QByteArrayLiteral("\r\n\r\n"));
334 QCOMPARE(mText.data(Imap::Mailbox::RolePartData).toByteArray(), myBody);
335
336 // still that new C++11 toy, oh yeah :)
337 using bodyFldParam_t = std::result_of<decltype(&Imap::Mailbox::TreeItemPart::bodyFldParam)(Imap::Mailbox::TreeItemPart)>::type;
338 bodyFldParam_t expectedBodyFldParam;
339 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartBodyFldParam).value<bodyFldParam_t>(), expectedBodyFldParam);
340
341 auto multipartIdx = msgRoot.child(0, 0);
342 QVERIFY(multipartIdx.isValid());
343 QCOMPARE(multipartIdx.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("multipart/mixed"));
344 QCOMPARE(msgModel.rowCount(multipartIdx), 2);
345 expectedBodyFldParam.clear();
346 expectedBodyFldParam["BOUNDARY"] = "sep";
347 QCOMPARE(multipartIdx.data(Imap::Mailbox::RolePartBodyFldParam).value<bodyFldParam_t>(), expectedBodyFldParam);
348
349 auto c1 = multipartIdx.child(0, 0);
350 QVERIFY(c1.isValid());
351 QCOMPARE(msgModel.rowCount(c1), 0);
352 QCOMPARE(c1.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("text/plain"));
353 QCOMPARE(c1.data(Imap::Mailbox::RolePartData).toString(), myUnicode);
354 expectedBodyFldParam.clear();
355 expectedBodyFldParam["CHARSET"] = "utf-8";
356 QCOMPARE(c1.data(Imap::Mailbox::RolePartBodyFldParam).value<bodyFldParam_t>(), expectedBodyFldParam);
357
358 auto c1raw = c1.child(0, Imap::Mailbox::TreeItem::OFFSET_RAW_CONTENTS);
359 QVERIFY(c1raw.isValid());
360 QCOMPARE(c1raw.data(Imap::Mailbox::RolePartData).toByteArray(), myUnicode.toUtf8().toBase64());
361 QVERIFY(!c1.child(0, Imap::Mailbox::TreeItem::OFFSET_HEADER).isValid());
362 QVERIFY(!c1.child(0, Imap::Mailbox::TreeItem::OFFSET_TEXT).isValid());
363 QVERIFY(!c1.child(0, Imap::Mailbox::TreeItem::OFFSET_MIME).isValid());
364
365 auto c2 = multipartIdx.child(1, 0);
366 QVERIFY(c2.isValid());
367 QCOMPARE(msgModel.rowCount(c2), 0);
368 QCOMPARE(c2.data(Imap::Mailbox::RolePartMimeType).toByteArray(), QByteArrayLiteral("pwned/now"));
369 QCOMPARE(c2.data(Imap::Mailbox::RolePartData).toByteArray(), myBinaryBody);
370
371 auto c2raw = c2.child(0, Imap::Mailbox::TreeItem::OFFSET_RAW_CONTENTS);
372 QVERIFY(c2raw.isValid());
373 QCOMPARE(c2raw.data(Imap::Mailbox::RolePartData).toByteArray(), myBinaryBody);
374 QVERIFY(!c2.child(0, Imap::Mailbox::TreeItem::OFFSET_HEADER).isValid());
375 QVERIFY(!c2.child(0, Imap::Mailbox::TreeItem::OFFSET_TEXT).isValid());
376 QVERIFY(!c2.child(0, Imap::Mailbox::TreeItem::OFFSET_MIME).isValid());
377
378 cEmpty();
379 QVERIFY(errorSpy->isEmpty());
380 #else
381 QSKIP("Mimetic not available, cannot test LocalMimeMessageParser");
382 #endif
383 }
384
testDelayedLoading()385 void CryptographyMessageModelTest::testDelayedLoading()
386 {
387 model->setProperty("trojita-imap-delayed-fetch-part", 0);
388 helperSyncBNoMessages();
389 cServer("* 1 EXISTS\r\n");
390 cClient(t.mk("UID FETCH 1:* (FLAGS)\r\n"));
391 cServer("* 1 FETCH (UID 333 FLAGS ())\r\n" + t.last("OK fetched\r\n"));
392 QCOMPARE(model->rowCount(msgListB), 1);
393 QModelIndex msg = msgListB.child(0, 0);
394 QVERIFY(msg.isValid());
395
396 Cryptography::MessageModel msgModel(0, msg);
397 msgModel.setObjectName("msgModel");
398
399 cEmpty();
400 QCOMPARE(msgModel.rowCount(QModelIndex()), 0);
401 cClient(t.mk("UID FETCH 333 (" FETCH_METADATA_ITEMS ")\r\n"));
402 cServer("* 1 FETCH (UID 333 BODYSTRUCTURE (" + bsPlaintext + "))\r\n" + t.last("OK fetched\r\n"));
403 cEmpty();
404 QCOMPARE(model->rowCount(msg), 1);
405 QCOMPARE(msgModel.rowCount(QModelIndex()), 1);
406 auto mappedMsg = msgModel.index(0,0);
407 QVERIFY(mappedMsg.isValid());
408
409 QPersistentModelIndex msgRoot = mappedMsg.child(0, 0);
410 QVERIFY(msgRoot.isValid());
411 QCOMPARE(msgRoot.data(Imap::Mailbox::RolePartPathToPart).toByteArray(),
412 QByteArrayLiteral("/0"));
413 }
414
415 QTEST_GUILESS_MAIN(CryptographyMessageModelTest)
416