File indexing completed on 2024-05-12 05:17:20
0001 /* 0002 Copyright (C) 2013 Christian Mollekopf <mollekopf@kolabsys.com> 0003 0004 This program is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This program is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 General Public License for more details. 0013 0014 You should have received a copy of the GNU General Public License 0015 along with this program; if not, write to the Free Software 0016 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0017 */ 0018 0019 #include <qtest.h> 0020 0021 #include "imapstreamparser.h" 0022 #include <message_p.h> 0023 0024 #include <QtTest> 0025 0026 using namespace KIMAP2; 0027 0028 class StreamParserTest: public QObject 0029 { 0030 Q_OBJECT 0031 0032 QByteArray part1; 0033 QByteArray part2; 0034 QByteArray part3; 0035 QByteArray part4; 0036 QByteArray completeMessage; 0037 QList<QByteArray> expectedList; 0038 0039 void printResponse(const Message &response) 0040 { 0041 qDebug() << "content: " << response.content.size(); 0042 for (const auto &c : response.content) { 0043 if (c.type() == Message::Part::List) { 0044 qDebug() << c.toList(); 0045 } else { 0046 qDebug() << c.toString(); 0047 } 0048 } 0049 qDebug() << "response: " << response.responseCode.size(); 0050 for (const auto &c : response.responseCode) { 0051 if (c.type() == Message::Part::List) { 0052 qDebug() << c.toList(); 0053 } else { 0054 qDebug() << c.toString(); 0055 } 0056 } 0057 } 0058 0059 private Q_SLOTS: 0060 0061 void init() 0062 { 0063 0064 // part1 = "* 230 FETCH (FLAGS (\\Recent \\Seen) UID 230 INTERNALDATE \" 1-Nov-2013 13:31:17 +0100\" RFC822.SIZE 37)\r\n"; 0065 // completeMessage = part1; 0066 part1 = "* 230 FETCH (FLAGS (\\Recent \\Seen) UID 230 INTERNALDATE \" 1-Nov-2013 13:31:17 +0100\" RFC822.SIZE 37 BODY[] {37}\r"; 0067 part2 = "\nDate: Fri, 01 Nov 2013 12:31:13 +0000"; 0068 part3 = "body\n"; 0069 part4 = ")\r\n"; 0070 completeMessage = part1 + part2 + part3 + part4; 0071 0072 expectedList.clear(); 0073 expectedList << "FLAGS"; 0074 expectedList << "(\\Recent \\Seen)"; 0075 expectedList << "UID"; 0076 expectedList << "230"; 0077 expectedList << "INTERNALDATE"; 0078 expectedList << " 1-Nov-2013 13:31:17 +0100"; 0079 expectedList << "RFC822.SIZE"; 0080 expectedList << "37"; 0081 expectedList << "BODY[]"; 0082 expectedList << "Date: Fri, 01 Nov 2013 12:31:13 +0000"; 0083 expectedList << "body\n"; 0084 } 0085 0086 void testParse() 0087 { 0088 QByteArray buffer; 0089 QBuffer socket(&buffer); 0090 socket.open(QBuffer::WriteOnly); 0091 QVERIFY(socket.write(completeMessage) != -1); 0092 0093 QBuffer readSocket(&buffer); 0094 readSocket.open(QBuffer::ReadOnly); 0095 ImapStreamParser parser(&readSocket); 0096 0097 QVERIFY(parser.availableDataSize() != 0); 0098 bool gotResponse = false; 0099 Message message; 0100 parser.onResponseReceived([this, &gotResponse, &message](const Message &response) { 0101 gotResponse = true; 0102 printResponse(response); 0103 message = response; 0104 }); 0105 parser.parseStream(); 0106 QVERIFY(gotResponse); 0107 QCOMPARE(message.content.last().toList(), expectedList); 0108 QVERIFY(parser.availableDataSize() == 0); 0109 QVERIFY(!parser.error()); 0110 } 0111 0112 void testParseInPieces() 0113 { 0114 QByteArray buffer; 0115 QBuffer socket(&buffer); 0116 socket.open(QBuffer::WriteOnly); 0117 0118 QBuffer readSocket(&buffer); 0119 readSocket.open(QBuffer::ReadOnly); 0120 ImapStreamParser parser(&readSocket); 0121 0122 bool gotResponse = false; 0123 Message message; 0124 parser.onResponseReceived([this, &gotResponse, &message](const Message &response) { 0125 gotResponse = true; 0126 printResponse(response); 0127 message = response; 0128 }); 0129 parser.parseStream(); 0130 QVERIFY(!gotResponse); 0131 QVERIFY(socket.write(part1) != -1); 0132 parser.parseStream(); 0133 QVERIFY(!gotResponse); 0134 QVERIFY(socket.write(part2) != -1); 0135 parser.parseStream(); 0136 QVERIFY(!gotResponse); 0137 QVERIFY(socket.write(part3) != -1); 0138 parser.parseStream(); 0139 QVERIFY(!gotResponse); 0140 QVERIFY(socket.write(part4) != -1); 0141 parser.parseStream(); 0142 0143 QVERIFY(gotResponse); 0144 QCOMPARE(message.content.last().toList(), expectedList); 0145 QVERIFY(parser.availableDataSize() == 0); 0146 QVERIFY(!parser.error()); 0147 } 0148 0149 //Try parsing angled brackets in a list (which we treat as string). 0150 void testParse2() 0151 { 0152 QByteArray buffer; 0153 QBuffer socket(&buffer); 0154 socket.open(QBuffer::WriteOnly); 0155 QVERIFY(socket.write("* 230 FETCH (BODY[HEADER FOOBAR (FOO BAR)])\r\n") != -1); 0156 0157 QBuffer readSocket(&buffer); 0158 readSocket.open(QBuffer::ReadOnly); 0159 ImapStreamParser parser(&readSocket); 0160 0161 QList<QByteArray> expectedList; 0162 expectedList << "BODY[HEADER FOOBAR (FOO BAR)]"; 0163 0164 QVERIFY(parser.availableDataSize() != 0); 0165 bool gotResponse = false; 0166 Message message; 0167 parser.onResponseReceived([this, &gotResponse, &message](const Message &response) { 0168 gotResponse = true; 0169 printResponse(response); 0170 message = response; 0171 }); 0172 parser.parseStream(); 0173 QVERIFY(gotResponse); 0174 QCOMPARE(message.content.last().toList(), expectedList); 0175 QVERIFY(parser.availableDataSize() == 0); 0176 QVERIFY(!parser.error()); 0177 } 0178 0179 void testParse3() 0180 { 0181 QByteArray buffer; 0182 QBuffer socket(&buffer); 0183 socket.open(QBuffer::WriteOnly); 0184 QVERIFY(socket.write("* 1 FETCH (UID 10 BODYSTRUCTURE (\"TEXT\" \"PLAIN\" (\"CHARSET\" \"ISO-8859-1\") NIL NIL \"7BIT\" 5 1 NIL NIL NIL))\r\n") != -1); 0185 0186 QBuffer readSocket(&buffer); 0187 readSocket.open(QBuffer::ReadOnly); 0188 ImapStreamParser parser(&readSocket); 0189 0190 QList<QByteArray> expectedList; 0191 expectedList << "UID"; 0192 expectedList << "10"; 0193 expectedList << "BODYSTRUCTURE"; 0194 expectedList << "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"ISO-8859-1\") NIL NIL \"7BIT\" 5 1 NIL NIL NIL)"; 0195 0196 QVERIFY(parser.availableDataSize() != 0); 0197 bool gotResponse = false; 0198 Message message; 0199 parser.onResponseReceived([this, &gotResponse, &message](const Message &response) { 0200 gotResponse = true; 0201 printResponse(response); 0202 message = response; 0203 }); 0204 parser.parseStream(); 0205 QVERIFY(gotResponse); 0206 QCOMPARE(message.content.last().toList(), expectedList); 0207 QVERIFY(parser.availableDataSize() == 0); 0208 QVERIFY(!parser.error()); 0209 } 0210 0211 void testParse4() 0212 { 0213 QByteArray buffer; 0214 QBuffer socket(&buffer); 0215 socket.open(QBuffer::WriteOnly); 0216 QVERIFY(socket.write("* 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") != -1); 0217 0218 QBuffer readSocket(&buffer); 0219 readSocket.open(QBuffer::ReadOnly); 0220 ImapStreamParser parser(&readSocket); 0221 0222 QList<QByteArray> expectedList; 0223 expectedList << "UID"; 0224 expectedList << "20"; 0225 expectedList << "FLAGS" << "(\\Seen)" << "BODY[HEADER.FIELDS (TO FROM MESSAGE-ID REFERENCES IN-REPLY-TO SUBJECT DATE)]"; 0226 expectedList << "From: 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"; 0227 expectedList << "BODY[1.1.1]"; 0228 expectedList << "Hi Jane, nice to meet you!\r\n"; 0229 expectedList << "BODY[1.1.1.MIME]"; 0230 expectedList << "Content-Type: text/plain; charset=ISO-8859-1\r\n\r\n"; 0231 0232 QVERIFY(parser.availableDataSize() != 0); 0233 bool gotResponse = false; 0234 Message message; 0235 parser.onResponseReceived([this, &gotResponse, &message](const Message &response) { 0236 gotResponse = true; 0237 printResponse(response); 0238 message = response; 0239 }); 0240 parser.parseStream(); 0241 QVERIFY(gotResponse); 0242 QCOMPARE(message.content.last().toList(), expectedList); 0243 QVERIFY(parser.availableDataSize() == 0); 0244 QVERIFY(!parser.error()); 0245 } 0246 0247 void testParseEmptyFlags() 0248 { 0249 QByteArray buffer; 0250 QBuffer socket(&buffer); 0251 socket.open(QBuffer::WriteOnly); 0252 QVERIFY(socket.write("* 1 FETCH ( FLAGS () UID 1 )\r\n") != -1); 0253 0254 QBuffer readSocket(&buffer); 0255 readSocket.open(QBuffer::ReadOnly); 0256 ImapStreamParser parser(&readSocket); 0257 0258 QList<QByteArray> expectedList; 0259 expectedList << "FLAGS"; 0260 expectedList << "()"; 0261 expectedList << "UID"; 0262 expectedList << "1"; 0263 0264 QVERIFY(parser.availableDataSize() != 0); 0265 bool gotResponse = false; 0266 Message message; 0267 parser.onResponseReceived([this, &gotResponse, &message](const Message &response) { 0268 gotResponse = true; 0269 printResponse(response); 0270 message = response; 0271 }); 0272 parser.parseStream(); 0273 QVERIFY(gotResponse); 0274 QCOMPARE(message.content.last().toList(), expectedList); 0275 QVERIFY(parser.availableDataSize() == 0); 0276 } 0277 0278 void testResponseCode() 0279 { 0280 const auto payloadSize = 32000; 0281 QByteArray payload; 0282 payload.fill('c', payloadSize); 0283 QByteArray buffer; 0284 QBuffer socket(&buffer); 0285 socket.open(QBuffer::WriteOnly); 0286 QVERIFY(socket.write("A000001 OK [READ-WRITE] SELECT completed\r\n") != -1); 0287 0288 QBuffer readSocket(&buffer); 0289 readSocket.open(QBuffer::ReadOnly); 0290 ImapStreamParser parser(&readSocket); 0291 0292 QVERIFY(parser.availableDataSize() != 0); 0293 bool gotResponse = false; 0294 Message message; 0295 parser.onResponseReceived([this, &gotResponse, &message](const Message &response) { 0296 gotResponse = true; 0297 printResponse(response); 0298 message = response; 0299 }); 0300 parser.parseStream(); 0301 QVERIFY(gotResponse); 0302 QCOMPARE(message.content.at(0).toString(), QByteArray("A000001")); 0303 QCOMPARE(message.content.at(1).toString(), QByteArray("OK")); 0304 QCOMPARE(message.content.at(2).toString(), QByteArray("SELECT")); 0305 QCOMPARE(message.content.at(3).toString(), QByteArray("completed")); 0306 QCOMPARE(message.responseCode.at(0).toString(), QByteArray("READ-WRITE")); 0307 QVERIFY(parser.availableDataSize() == 0); 0308 QVERIFY(!parser.error()); 0309 } 0310 0311 void testPermanentFlags() 0312 { 0313 QByteArray buffer; 0314 QBuffer socket(&buffer); 0315 socket.open(QBuffer::WriteOnly); 0316 QVERIFY(socket.write("* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited\r\n") != -1); 0317 0318 QBuffer readSocket(&buffer); 0319 readSocket.open(QBuffer::ReadOnly); 0320 ImapStreamParser parser(&readSocket); 0321 0322 QVERIFY(parser.availableDataSize() != 0); 0323 bool gotResponse = false; 0324 Message message; 0325 parser.onResponseReceived([this, &gotResponse, &message](const Message &response) { 0326 gotResponse = true; 0327 printResponse(response); 0328 message = response; 0329 }); 0330 parser.parseStream(); 0331 QVERIFY(gotResponse); 0332 QCOMPARE(message.content.at(0).toString(), QByteArray("*")); 0333 QCOMPARE(message.content.at(1).toString(), QByteArray("OK")); 0334 QCOMPARE(message.content.at(2).toString(), QByteArray("Limited")); 0335 QCOMPARE(message.responseCode.at(0).toString(), QByteArray("PERMANENTFLAGS")); 0336 QCOMPARE(message.responseCode.at(1).toList(), QList<QByteArray>() << "\\Deleted" << "\\Seen" << "\\*"); 0337 QVERIFY(parser.availableDataSize() == 0); 0338 QVERIFY(!parser.error()); 0339 } 0340 0341 void testParseLargeLiteral() 0342 { 0343 const auto payloadSize = 32000; 0344 QByteArray payload; 0345 payload.fill('c', payloadSize); 0346 const auto data = QString("* 11 FETCH (UID 123 BODY[HEADER] {%1}\r\n").arg(payloadSize).toLatin1() + payload + " FLAGS ())\r\n"; 0347 QByteArray buffer; 0348 QBuffer socket(&buffer); 0349 socket.open(QBuffer::WriteOnly); 0350 QVERIFY(socket.write(data) != -1); 0351 0352 QBuffer readSocket(&buffer); 0353 readSocket.open(QBuffer::ReadOnly); 0354 ImapStreamParser parser(&readSocket); 0355 0356 QList<QByteArray> expectedList; 0357 expectedList << "UID"; 0358 expectedList << "123"; 0359 expectedList << "BODY[HEADER]"; 0360 expectedList << payload; 0361 expectedList << "FLAGS"; 0362 expectedList << "()"; 0363 0364 QVERIFY(parser.availableDataSize() != 0); 0365 bool gotResponse = false; 0366 Message message; 0367 parser.onResponseReceived([this, &gotResponse, &message](const Message &response) { 0368 gotResponse = true; 0369 printResponse(response); 0370 message = response; 0371 }); 0372 parser.parseStream(); 0373 QVERIFY(gotResponse); 0374 QCOMPARE(message.content.last().toList(), expectedList); 0375 QVERIFY(parser.availableDataSize() == 0); 0376 QVERIFY(!parser.error()); 0377 } 0378 0379 void testRecursiveParse() 0380 { 0381 QByteArray buffer; 0382 QBuffer socket(&buffer); 0383 socket.open(QBuffer::WriteOnly); 0384 QVERIFY(socket.write("* 230 FOO\r\n") != -1); 0385 0386 QBuffer readSocket(&buffer); 0387 readSocket.open(QBuffer::ReadOnly); 0388 ImapStreamParser parser(&readSocket); 0389 0390 parser.onResponseReceived([&](const Message &) { 0391 parser.parseStream(); 0392 }); 0393 parser.parseStream(); 0394 QVERIFY(parser.availableDataSize() == 0); 0395 QVERIFY(!parser.error()); 0396 } 0397 0398 }; 0399 0400 QTEST_GUILESS_MAIN(StreamParserTest) 0401 0402 #include "streamparsertest.moc"