File indexing completed on 2024-05-12 05:17:19
0001 /* 0002 Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0003 Author: Kevin Ottens <kevin@kdab.com> 0004 0005 This program is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This program is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 General Public License for more details. 0014 0015 You should have received a copy of the GNU General Public License 0016 along with this program; if not, write to the Free Software 0017 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include <qtest.h> 0021 0022 #include "kimap2test/fakeserver.h" 0023 #include "kimap2/session.h" 0024 #include "kimap2/fetchjob.h" 0025 0026 #include <QtTest> 0027 0028 Q_DECLARE_METATYPE(KIMAP2::FetchJob::FetchScope) 0029 0030 using namespace KIMAP2; 0031 0032 class FetchJobTest: public QObject 0033 { 0034 Q_OBJECT 0035 0036 private: 0037 QStringList m_signals; 0038 0039 QMap<qint64, qint64> m_uids; 0040 QMap<qint64, qint64> m_sizes; 0041 QMap<qint64, KIMAP2::MessageFlags> m_flags; 0042 QMap<qint64, KIMAP2::MessagePtr> m_messages; 0043 QMap<qint64, KIMAP2::MessageParts> m_parts; 0044 QMap<qint64, KIMAP2::MessageAttributes> m_attrs; 0045 0046 public Q_SLOTS: 0047 void onResultReceived(const FetchJob::Result &result) 0048 { 0049 qDebug() << "Received result " << result.sequenceNumber; 0050 m_signals << QStringLiteral("headersReceived"); 0051 m_uids.insert(result.sequenceNumber, result.uid); 0052 m_sizes.insert(result.sequenceNumber, result.size); 0053 m_flags.insert(result.sequenceNumber, result.flags); 0054 m_messages.insert(result.sequenceNumber, result.message); 0055 if (!result.attributes.isEmpty()) { 0056 m_attrs.insert(result.sequenceNumber, result.attributes); 0057 } 0058 m_parts.insert(result.sequenceNumber, result.parts); 0059 } 0060 0061 private Q_SLOTS: 0062 0063 void testFetch_data() 0064 { 0065 qRegisterMetaType<KIMAP2::FetchJob::FetchScope>(); 0066 0067 QTest::addColumn<bool>("uidBased"); 0068 QTest::addColumn< KIMAP2::ImapSet >("set"); 0069 QTest::addColumn<int>("expectedMessageCount"); 0070 QTest::addColumn< QList<QByteArray> >("scenario"); 0071 QTest::addColumn<KIMAP2::FetchJob::FetchScope>("scope"); 0072 0073 KIMAP2::FetchJob::FetchScope scope; 0074 scope.mode = KIMAP2::FetchJob::FetchScope::Flags; 0075 scope.changedSince = 123456789; 0076 0077 QList<QByteArray> scenario; 0078 scenario << FakeServer::preauth() 0079 << "C: A000001 FETCH 1:4 (FLAGS UID) (CHANGEDSINCE 123456789)" 0080 << "S: * 1 FETCH ( FLAGS () UID 1 )" 0081 << "S: * 2 FETCH ( FLAGS () UID 2 )" 0082 << "S: * 3 FETCH ( FLAGS () UID 3 )" 0083 << "S: * 4 FETCH ( FLAGS () UID 4 )" 0084 << "S: A000001 OK fetch done"; 0085 0086 QTest::newRow("messages have empty flags (with changedsince)") << false << KIMAP2::ImapSet(1, 4) << 4 0087 << scenario << scope; 0088 0089 scenario.clear(); 0090 scope.changedSince = 0; 0091 scenario << FakeServer::preauth() 0092 << "C: A000001 FETCH 1:4 (FLAGS UID)" 0093 << "S: * 1 FETCH ( FLAGS () UID 1 )" 0094 << "S: * 2 FETCH ( FLAGS () UID 2 )" 0095 << "S: * 3 FETCH ( FLAGS () UID 3 )" 0096 << "S: * 4 FETCH ( FLAGS () UID 4 )" 0097 << "S: A000001 OK fetch done"; 0098 0099 QTest::newRow("messages have empty flags") << false << KIMAP2::ImapSet(1, 4) << 4 0100 << scenario << scope; 0101 0102 scenario.clear(); 0103 // kill the connection part-way through a list, with carriage returns at end 0104 // BUG 253619 0105 // this should fail, but it shouldn't crash 0106 scenario << FakeServer::preauth() 0107 << "C: A000001 FETCH 11 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID)" 0108 << "S: * 11 FETCH (RFC822.SIZE 770 INTERNALDATE \"11-Oct-2010 03:33:50 +0100\" BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {246}" 0109 << "S: From: John Smith <jonathanr.smith@foobarbaz.com>\r\nTo: \"amagicemailaddress@foobarbazbarfoo.com\"\r\n\t<amagicemailaddress@foobarbazbarfoo.com>\r\nDate: Mon, 11 Oct 2010 03:34:48 +0100\r\nSubject: unsubscribe\r\nMessage-ID: <ASDFFDSASDFFDS@foobarbaz.com>\r\n\r\n" 0110 << "X"; 0111 scope.mode = KIMAP2::FetchJob::FetchScope::Headers; 0112 QTest::newRow("connection drop") << false << KIMAP2::ImapSet(11, 11) << 1 << scenario << scope; 0113 0114 scenario.clear(); 0115 // Important bit here if "([127.0.0.1])" which used to crash the stream parser 0116 scenario << FakeServer::preauth() 0117 << "C: A000001 FETCH 11 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID)" 0118 << "S: * 11 FETCH (RFC822.SIZE 770 INTERNALDATE \"11-Oct-2010 03:33:50 +0100\" BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {246}" 0119 << "S: ([127.0.0.1])\r\nDate: Mon, 11 Oct 2010 03:34:48 +0100\r\nSubject: unsubscribe\r\nMessage-ID: <ASDFFDSASDFFDS@foobarbaz.com>\r\n\r\n" 0120 << "X"; 0121 scope.mode = KIMAP2::FetchJob::FetchScope::Headers; 0122 QTest::newRow("buffer overwrite") << false << KIMAP2::ImapSet(11, 11) << 1 << scenario << scope; 0123 0124 scenario.clear(); 0125 // We're assuming a buffer overwrite here which made us miss the opening parenthesis 0126 // for the properties list 0127 scenario << FakeServer::preauth() 0128 << "C: A000001 FETCH 11 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] FLAGS UID)" 0129 << "S: * 11 FETCH {10}doh!\r\n\r\n\r\n)\r\n" 0130 << "X"; 0131 scope.mode = KIMAP2::FetchJob::FetchScope::Headers; 0132 QTest::newRow("buffer overwrite 2") << false << KIMAP2::ImapSet(11, 11) << 1 << scenario << scope; 0133 0134 scenario.clear(); 0135 scenario << FakeServer::preauth() 0136 << "C: A000001 FETCH 11 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER] FLAGS UID) (CHANGEDSINCE 123456789)" 0137 << "S: * 11 FETCH (UID 123 RFC822.SIZE 770 INTERNALDATE \"11-Oct-2010 03:33:50 +0100\" BODY[HEADER] {245}" 0138 << "S: From: John Smith <jonathanr.smith@foobarbaz.com>\r\nTo: \"amagicemailaddress@foobarbazbarfoo.com\"\r\n\t<amagicemailaddress@foobarbazbarfoo.com>\r\nDate: Mon, 11 Oct 2010 03:34:48 +0100\r\nSubject: unsubscribe\r\nMessage-ID: <ASDFFDSASDFFDS@foobarbaz.com>\r\n\r\n FLAGS ())" 0139 << "S: A000001 OK fetch done"; 0140 scope.mode = KIMAP2::FetchJob::FetchScope::FullHeaders; 0141 scope.changedSince = 123456789; 0142 QTest::newRow("fetch full headers") << false << KIMAP2::ImapSet(11, 11) << 1 0143 << scenario << scope; 0144 0145 scenario.clear(); 0146 const auto payloadSize = 32000; 0147 QByteArray payload; 0148 payload.fill('c', payloadSize); 0149 scenario << FakeServer::preauth() 0150 << "C: A000001 FETCH 11 (RFC822.SIZE INTERNALDATE BODY.PEEK[HEADER] FLAGS UID) (CHANGEDSINCE 123456789)" 0151 << QString("S: * 11 FETCH (UID 123 RFC822.SIZE 770 INTERNALDATE \"11-Oct-2010 03:33:50 +0100\" BODY[HEADER] {%1}").arg(payloadSize).toLatin1() 0152 << "S: " + payload + " FLAGS ())" 0153 << "S: A000001 OK fetch done"; 0154 scope.mode = KIMAP2::FetchJob::FetchScope::FullHeaders; 0155 scope.changedSince = 123456789; 0156 QTest::newRow("fetch large payload") << false << KIMAP2::ImapSet(11, 11) << 1 0157 << scenario << scope; 0158 } 0159 0160 void testFetch() 0161 { 0162 QFETCH(bool, uidBased); 0163 QFETCH(KIMAP2::ImapSet, set); 0164 QFETCH(int, expectedMessageCount); 0165 QFETCH(QList<QByteArray>, scenario); 0166 QFETCH(KIMAP2::FetchJob::FetchScope, scope); 0167 0168 FakeServer fakeServer; 0169 fakeServer.setScenario(scenario); 0170 fakeServer.startAndWait(); 0171 0172 KIMAP2::Session session(QStringLiteral("127.0.0.1"), 5989); 0173 0174 KIMAP2::FetchJob *job = new KIMAP2::FetchJob(&session); 0175 job->setUidBased(uidBased); 0176 job->setSequenceSet(set); 0177 job->setScope(scope); 0178 0179 connect(job, &FetchJob::resultReceived, this, &FetchJobTest::onResultReceived); 0180 0181 bool result = job->exec(); 0182 QEXPECT_FAIL("connection drop", "Expected failure on connection drop", Continue); 0183 QEXPECT_FAIL("buffer overwrite", "Expected failure on confused list", Continue); 0184 QEXPECT_FAIL("buffer overwrite 2", "Expected beginning of message missing", Continue); 0185 QVERIFY(result); 0186 if (result) { 0187 QVERIFY(m_signals.count() > 0); 0188 QCOMPARE(m_uids.count(), expectedMessageCount); 0189 } 0190 0191 QVERIFY(fakeServer.isAllScenarioDone()); 0192 fakeServer.quit(); 0193 0194 m_signals.clear(); 0195 m_uids.clear(); 0196 m_sizes.clear(); 0197 m_flags.clear(); 0198 m_messages.clear(); 0199 m_parts.clear(); 0200 m_attrs.clear(); 0201 } 0202 0203 void testFetchStructure() 0204 { 0205 QList<QByteArray> scenario; 0206 scenario << FakeServer::preauth() 0207 << "C: A000001 FETCH 1:2 (BODYSTRUCTURE UID)" 0208 << "S: * 1 FETCH (UID 10 BODYSTRUCTURE (\"TEXT\" \"PLAIN\" (\"CHARSET\" \"ISO-8859-1\") NIL NIL \"7BIT\" 5 1 NIL NIL NIL))" 0209 << "S: * 2 FETCH (UID 20 BODYSTRUCTURE ((((\"TEXT\" \"PLAIN\" (\"CHARSET\" \"ISO-8859-1\") NIL NIL \"7BIT\" 72 4 NIL NIL NIL)(\"TEXT\" \"HTML\" (\"CHARSET\" \"ISO-8859-1\") NIL NIL \"QUOTED-PRINTABLE\" 281 5 NIL NIL NIL) \"ALTERNATIVE\" (\"BOUNDARY\" \"0001\") NIL NIL)(\"IMAGE\" \"GIF\" (\"NAME\" \"B56.gif\") \"<B56@goomoji.gmail>\" NIL \"BASE64\" 528 NIL NIL NIL) \"RELATED\" (\"BOUNDARY\" \"0002\") NIL NIL)(\"IMAGE\" \"JPEG\" (\"NAME\" \"photo.jpg\") NIL NIL \"BASE64\" 53338 NIL (\"ATTACHMENT\" (\"FILENAME\" \"photo.jpg\")) NIL) \"MIXED\" (\"BOUNDARY\" \"0003\") NIL NIL))" 0210 << "S: A000001 OK fetch done"; 0211 0212 KIMAP2::FetchJob::FetchScope scope; 0213 scope.mode = KIMAP2::FetchJob::FetchScope::Structure; 0214 0215 FakeServer fakeServer; 0216 fakeServer.setScenario(scenario); 0217 fakeServer.startAndWait(); 0218 0219 KIMAP2::Session session(QStringLiteral("127.0.0.1"), 5989); 0220 0221 KIMAP2::FetchJob *job = new KIMAP2::FetchJob(&session); 0222 job->setUidBased(false); 0223 job->setSequenceSet(KIMAP2::ImapSet(1, 2)); 0224 job->setScope(scope); 0225 0226 connect(job, &FetchJob::resultReceived, this, &FetchJobTest::onResultReceived); 0227 0228 bool result = job->exec(); 0229 QVERIFY(result); 0230 QVERIFY(m_signals.count() > 0); 0231 QCOMPARE(m_uids.count(), 2); 0232 QCOMPARE(m_messages.count(), 2); 0233 QVERIFY(m_messages[1]); 0234 QCOMPARE(m_messages[1]->attachments().count(), 0); 0235 QVERIFY(m_messages[2]); 0236 QCOMPARE(m_messages[2]->attachments().count(), 1); 0237 QCOMPARE(m_messages[2]->contents().size(), 2); 0238 QCOMPARE(m_messages[2]->contents()[0]->contents().size(), 2); 0239 QCOMPARE(m_messages[2]->attachments().at(0)->contentDisposition()->filename(), QStringLiteral("photo.jpg")); 0240 0241 fakeServer.quit(); 0242 0243 m_signals.clear(); 0244 m_uids.clear(); 0245 m_sizes.clear(); 0246 m_flags.clear(); 0247 m_messages.clear(); 0248 m_parts.clear(); 0249 m_attrs.clear(); 0250 } 0251 0252 void testFetchParts() 0253 { 0254 QList<QByteArray> scenario; 0255 scenario << FakeServer::preauth() 0256 << "C: A000001 FETCH 2 (BODY.PEEK[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] BODY.PEEK[1.1.1.MIME] BODY.PEEK[1.1.1] FLAGS UID)" 0257 << "S: * 2 FETCH (UID 20 FLAGS (\\Seen) BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)] {154}\r\nFrom: Joe Smith <smith@example.com>\r\nDate: Wed, 2 Mar 2011 11:33:24 +0700\r\nMessage-ID: <1234@example.com>\r\nSubject: hello\r\nTo: Jane <jane@example.com>\r\n\r\n BODY[1.1.1] {28}\r\nHi Jane, nice to meet you!\r\n BODY[1.1.1.MIME] {48}\r\nContent-Type: text/plain; charset=ISO-8859-1\r\n\r\n)\r\n" 0258 << "S: A000001 OK fetch done"; 0259 0260 KIMAP2::FetchJob::FetchScope scope; 0261 scope.mode = KIMAP2::FetchJob::FetchScope::HeaderAndContent; 0262 scope.parts.clear(); 0263 scope.parts.append("1.1.1"); 0264 0265 FakeServer fakeServer; 0266 fakeServer.setScenario(scenario); 0267 fakeServer.startAndWait(); 0268 0269 KIMAP2::Session session(QStringLiteral("127.0.0.1"), 5989); 0270 0271 KIMAP2::FetchJob *job = new KIMAP2::FetchJob(&session); 0272 job->setUidBased(false); 0273 job->setSequenceSet(KIMAP2::ImapSet(2, 2)); 0274 job->setScope(scope); 0275 0276 connect(job, &FetchJob::resultReceived, this, &FetchJobTest::onResultReceived); 0277 0278 bool result = job->exec(); 0279 0280 QVERIFY(result); 0281 QVERIFY(m_signals.count() > 0); 0282 QCOMPARE(m_uids.count(), 1); 0283 QCOMPARE(m_parts.count(), 1); 0284 QCOMPARE(m_attrs.count(), 0); 0285 0286 // Check that we received the message header 0287 QCOMPARE(m_messages[2]->messageID()->identifier(), QByteArray("1234@example.com")); 0288 0289 // Check that we recieved the flags 0290 QMap<qint64, KIMAP2::MessageFlags> expectedFlags; 0291 expectedFlags.insert(2, KIMAP2::MessageFlags() << "\\Seen"); 0292 QCOMPARE(m_flags, expectedFlags); 0293 0294 // Check that we didn't received the full message body, since we only requested a specific part 0295 QCOMPARE(m_messages[2]->decodedText().length(), 0); 0296 QCOMPARE(m_messages[2]->attachments().count(), 0); 0297 0298 // Check that we received the part we requested 0299 QByteArray partId = m_parts[2].keys().first(); 0300 QString text = m_parts[2].value(partId)->decodedText(true, true); 0301 QCOMPARE(partId, QByteArray("1.1.1")); 0302 QCOMPARE(text, QStringLiteral("Hi Jane, nice to meet you!")) ; 0303 0304 fakeServer.quit(); 0305 0306 m_signals.clear(); 0307 m_uids.clear(); 0308 m_sizes.clear(); 0309 m_flags.clear(); 0310 m_messages.clear(); 0311 m_parts.clear(); 0312 m_attrs.clear(); 0313 } 0314 0315 }; 0316 0317 QTEST_GUILESS_MAIN(FetchJobTest) 0318 0319 #include "fetchjobtest.moc"