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"