Warning, file /pim/trojita/tests/Imap/test_Imap_Parser_parse.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net>
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 <QBuffer>
0024 #include <QFile>
0025 #include <QTest>
0026 #include "Imap/Parser/Message.h"
0027 #include "Streams/FakeSocket.h"
0028 
0029 #include "test_Imap_Parser_parse.h"
0030 
0031 Q_DECLARE_METATYPE(QSharedPointer<Imap::Responses::AbstractResponse>)
0032 Q_DECLARE_METATYPE(Imap::Responses::State)
0033 Q_DECLARE_METATYPE(Imap::Sequence)
0034 
0035 void ImapParserParseTest::initTestCase()
0036 {
0037     array.reset( new QByteArray() );
0038     Streams::Socket* sock(new Streams::FakeSocket(Imap::CONN_STATE_CONNECTED_PRETLS_PRECAPS));
0039     parser = new Imap::Parser( this, sock, 666 );
0040 }
0041 
0042 void ImapParserParseTest::cleanupTestCase()
0043 {
0044     delete parser;
0045     parser = 0;
0046     QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
0047 }
0048 
0049 /** @short Test tagged response parsing */
0050 void ImapParserParseTest::testParseTagged()
0051 {
0052     QFETCH( QByteArray, line );
0053     QFETCH( QSharedPointer<Imap::Responses::AbstractResponse>, response );
0054 
0055     Q_ASSERT( response );
0056     QCOMPARE( *(parser->parseTagged( line )), *response );
0057 }
0058 
0059 void ImapParserParseTest::testParseTagged_data()
0060 {
0061     using namespace Imap::Responses;
0062 
0063     QTest::addColumn<QByteArray>("line");
0064     QTest::addColumn<QSharedPointer<AbstractResponse> >("response");
0065 
0066     QSharedPointer<AbstractData> voidData( new RespData<void>() );
0067     QSharedPointer<AbstractData> emptyList(
0068             new RespData<QStringList>( QStringList() ) );
0069 
0070     QTest::newRow("tagged-ok-simple")
0071         << QByteArray("y01 OK Everything works, man!\r\n")
0072         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Everything works, man!"), NONE, voidData ) );
0073 
0074     QTest::newRow("tagged-no-simple")
0075         << QByteArray("12345 NO Nope, something is broken\r\n")
0076         << QSharedPointer<AbstractResponse>( new State("12345", NO, QStringLiteral("Nope, something is broken"), NONE, voidData ) );
0077 
0078     QTest::newRow("tagged-bad-simple")
0079         << QByteArray("ahoj BaD WTF?\r\n")
0080         << QSharedPointer<AbstractResponse>( new State("ahoj", BAD, QStringLiteral("WTF?"), NONE, voidData ) );
0081 
0082     QTest::newRow("tagged-ok-alert")
0083         << QByteArray("y01 oK [ALERT] Server on fire\r\n")
0084         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Server on fire"), ALERT, voidData ) );
0085     QTest::newRow("tagged-no-alert")
0086         << QByteArray("1337 no [ALeRT] Server on fire\r\n")
0087         << QSharedPointer<AbstractResponse>( new State("1337", NO, QStringLiteral("Server on fire"), ALERT, voidData ) );
0088 
0089     QTest::newRow("tagged-ok-capability")
0090         << QByteArray("y01 OK [CAPaBILITY blurdybloop IMAP4rev1 WTF] Capabilities updated\r\n")
0091         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Capabilities updated"), CAPABILITIES,
0092                         QSharedPointer<AbstractData>(
0093                             new RespData<QStringList>( QStringList() << QStringLiteral("blurdybloop") << QStringLiteral("IMAP4rev1") << QStringLiteral("WTF")) ) ) );
0094 
0095     QTest::newRow("tagged-ok-parse")
0096         << QByteArray("y01 OK [PArSE] Parse error. What do you feed me with?\r\n")
0097         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Parse error. What do you feed me with?"),
0098                     PARSE, voidData) );
0099     QTest::newRow("tagged-ok-permanentflags-empty")
0100         << QByteArray("y01 OK [PERMANENTfLAGS] Behold, the flags!\r\n")
0101         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Behold, the flags!"), PERMANENTFLAGS, emptyList) );
0102     QTest::newRow("tagged-ok-permanentflags-flags")
0103         << QByteArray("y01 OK [PErMANENTFLAGS (\\Foo \\Bar SmrT)] Behold, the flags!\r\n")
0104         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Behold, the flags!"), PERMANENTFLAGS,
0105                     QSharedPointer<AbstractData>( new RespData<QStringList>(
0106                             QStringList() << QStringLiteral("\\Foo") << QStringLiteral("\\Bar") << QStringLiteral("SmrT") ))) );
0107     QTest::newRow("tagged-ok-permanentflags-flags-not-enclosed")
0108         << QByteArray("y01 OK [PErMANENTFLAGS \\Foo \\Bar SmrT] Behold, the flags!\r\n")
0109         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Behold, the flags!"), PERMANENTFLAGS,
0110                     QSharedPointer<AbstractData>( new RespData<QStringList>(
0111                             QStringList() << QStringLiteral("\\Foo") << QStringLiteral("\\Bar") << QStringLiteral("SmrT") ))) );
0112     QTest::newRow("tagged-ok-badcharset-empty")
0113         << QByteArray("y01 OK [BadCharset] foo\r\n")
0114         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("foo"), BADCHARSET, emptyList) );
0115     QTest::newRow("tagged-ok-badcharset-something")
0116         << QByteArray("y01 OK [BADCHARSET (utf8)] wrong charset\r\n")
0117         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("wrong charset"), BADCHARSET,
0118                     QSharedPointer<AbstractData>( new RespData<QStringList>( QStringList() << QStringLiteral("utf8") ))) );
0119     QTest::newRow("tagged-ok-badcharset-not-enclosed")
0120         << QByteArray("y01 OK [badcharset a ba cc] Behold, the charset!\r\n")
0121         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Behold, the charset!"), BADCHARSET,
0122                     QSharedPointer<AbstractData>( new RespData<QStringList>(
0123                             QStringList() << QStringLiteral("a") << QStringLiteral("ba") << QStringLiteral("cc") ))) );
0124     QTest::newRow("tagged-ok-readonly")
0125         << QByteArray("333 OK [ReAD-ONLY] No writing for you\r\n")
0126         << QSharedPointer<AbstractResponse>( new State("333", OK, QStringLiteral("No writing for you"), READ_ONLY, voidData));
0127     QTest::newRow("tagged-ok-readwrite")
0128         << QByteArray("y01 OK [REaD-WRITE] Write!!!\r\n")
0129         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Write!!!"), READ_WRITE, voidData));
0130     QTest::newRow("tagged-ok-trycreate")
0131         << QByteArray("y01 OK [TryCreate] ...would be better :)\r\n")
0132         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("...would be better :)"), TRYCREATE, voidData));
0133     QTest::newRow("tagged-ok-uidnext")
0134         << QByteArray("y01 OK [uidNext 5] Next UID\r\n")
0135         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("Next UID"), UIDNEXT,
0136                     QSharedPointer<AbstractData>( new RespData<uint>( 5 ) )));
0137     QTest::newRow("tagged-ok-uidvalidity")
0138         << QByteArray("y01 OK [UIDVALIDITY 17] UIDs valid\r\n")
0139         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("UIDs valid"), UIDVALIDITY,
0140                     QSharedPointer<AbstractData>( new RespData<uint>( 17 ) )));
0141     QTest::newRow("tagged-ok-unseen")
0142         << QByteArray("y01 OK [unSeen 666] I need my glasses\r\n")
0143         << QSharedPointer<AbstractResponse>( new State("y01", OK, QStringLiteral("I need my glasses"), UNSEEN,
0144                     QSharedPointer<AbstractData>( new RespData<uint>( 666 ) )));
0145 
0146     QTest::newRow("appenduid-simple")
0147             << QByteArray("A003 OK [APPENDUID 38505 3955] APPEND completed\r\n")
0148             << QSharedPointer<AbstractResponse>(new State("A003", OK, QStringLiteral("APPEND completed"), APPENDUID,
0149                                                           QSharedPointer<AbstractData>(
0150                                                               new RespData<QPair<uint,Imap::Sequence> >(
0151                                                                   qMakePair(38505u, Imap::Sequence(3955)))
0152                                                               )));
0153     /*
0154     QTest::newRow("appenduid-seq")
0155             << QByteArray("A003 OK [APPENDUID 38505 3955,333666] APPEND completed\r\n")
0156             << QSharedPointer<AbstractResponse>(new State("A003", OK, "APPEND completed", APPENDUID,
0157                                                           QSharedPointer<AbstractData>(
0158                                                               new RespData<QPair<uint,Imap::Sequence> >(
0159                                                                   qMakePair(38505u, Imap::Sequence(3955)))
0160                                                               )));
0161 
0162     QTest::newRow("copyuid-simple")
0163             << QByteArray("A004 OK [COPYUID 38505 304 3956] Done\r\n")
0164             << QSharedPointer<AbstractResponse>(new State("A004", OK, "Done", COPYUID,
0165                                                           QSharedPointer<AbstractData>(
0166                                                               new RespData<QPair<uint,QPair<Imap::Sequence, Imap::Sequence> > >(
0167                                                                   qMakePair(38505u,
0168                                                                             qMakePair(Imap::Sequence(304), Imap::Sequence(3956))
0169                                                                             ))
0170                                                               )));
0171 
0172 
0173     QTest::newRow("copyuid-sequence")
0174             << QByteArray("A004 OK [COPYUID 38505 304,319:320 3956:3958] Done\r\n")
0175             << QSharedPointer<AbstractResponse>(new State("A003", OK, "Done", APPENDUID,
0176                                                           QSharedPointer<AbstractData>(
0177                                                               new RespData<QPair<uint,Imap::Sequence> >(
0178                                                                   qMakePair(38505u, Imap::Sequence(3955)))
0179                                                               )));
0180     */
0181 }
0182 
0183 /** @short Test untagged response parsing */
0184 void ImapParserParseTest::testParseUntagged()
0185 {
0186     QFETCH( QByteArray, line );
0187     QFETCH( QSharedPointer<Imap::Responses::AbstractResponse>, response );
0188 
0189     Q_ASSERT( response );
0190     QSharedPointer<Imap::Responses::AbstractResponse> r = parser->parseUntagged( line );
0191     if ( Imap::Responses::Fetch* fetchResult = dynamic_cast<Imap::Responses::Fetch*>( r.data() ) ) {
0192         fetchResult->data.remove( "x-trojita-bodystructure" );
0193     }
0194 #if 0// qDebug()'s internal buffer is too small to be useful here, that's why QCOMPARE's normal dumping is not enough
0195     if ( *r != *response ) {
0196         QTextStream s( stderr );
0197         s << "\nPARSED:\n" << *r;
0198         s << "\nEXPETCED:\n";
0199         s << *response;
0200     }
0201 #endif
0202     QCOMPARE( *r, *response );
0203 }
0204 
0205 void ImapParserParseTest::testParseUntagged_data()
0206 {
0207     using namespace Imap::Responses;
0208     using namespace Imap::Message;
0209     using Imap::Responses::Fetch; // needed for gcc-3.4
0210 
0211     QTest::addColumn<QByteArray>("line");
0212     QTest::addColumn<QSharedPointer<AbstractResponse> >("response");
0213 
0214     QSharedPointer<AbstractData> voidData( new RespData<void>() );
0215     QSharedPointer<AbstractData> emptyList(
0216             new RespData<QStringList>( QStringList() ) );
0217 
0218     QTest::newRow("untagged-ok-simple")
0219         << QByteArray("* OK Everything works, man!\r\n")
0220         << QSharedPointer<AbstractResponse>( new State(QByteArray(), OK, QStringLiteral("Everything works, man!"), NONE, voidData ) );
0221 
0222     QTest::newRow("untagged-no-simple")
0223         << QByteArray("* NO Nope, something is broken\r\n")
0224         << QSharedPointer<AbstractResponse>( new State(QByteArray(), NO, QStringLiteral("Nope, something is broken"), NONE, voidData ) );
0225     QTest::newRow("untagged-ok-uidvalidity")
0226         << QByteArray("* OK [UIDVALIDITY 17] UIDs valid\r\n")
0227         << QSharedPointer<AbstractResponse>( new State(QByteArray(), OK, QStringLiteral("UIDs valid"), UIDVALIDITY,
0228                     QSharedPointer<AbstractData>( new RespData<uint>( 17 ) )));
0229     QTest::newRow("untagged-bye")
0230         << QByteArray("* BYE go away\r\n")
0231         << QSharedPointer<AbstractResponse>( new State(QByteArray(), BYE, QStringLiteral("go away"), NONE, voidData ) );
0232     QTest::newRow("untagged-bye-empty")
0233         << QByteArray("* BYE\r\n")
0234         << QSharedPointer<AbstractResponse>( new State(QByteArray(), BYE, QString(), NONE, voidData ) );
0235     QTest::newRow("untagged-no-somerespcode-empty")
0236         << QByteArray("* NO [ALERT]\r\n")
0237         << QSharedPointer<AbstractResponse>( new State(QByteArray(), NO, QString(), ALERT, voidData ) );
0238 
0239     QTest::newRow("untagged-expunge")
0240         << QByteArray("* 1337 Expunge\r\n")
0241         << QSharedPointer<AbstractResponse>( new NumberResponse( EXPUNGE, 1337 ) );
0242     QTest::newRow("untagged-exists")
0243         << QByteArray("* 3 exIsts\r\n")
0244         << QSharedPointer<AbstractResponse>( new NumberResponse( EXISTS, 3 ) );
0245     QTest::newRow("untagged-recent")
0246         << QByteArray("* 666 recenT\r\n")
0247         << QSharedPointer<AbstractResponse>( new NumberResponse( RECENT, 666 ) );
0248 
0249     QTest::newRow("untagged-capability")
0250         << QByteArray("* CAPABILITY fooBar IMAP4rev1 blah\r\n")
0251         << QSharedPointer<AbstractResponse>( new Capability( QStringList() << QStringLiteral("fooBar") << QStringLiteral("IMAP4rev1") << QStringLiteral("blah") ) );
0252 
0253     QTest::newRow("untagged-list")
0254         << QByteArray("* LIST (\\Noselect) \".\" \"\"\r\n")
0255         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList() << QStringLiteral("\\Noselect"), QStringLiteral("."), QLatin1String(""), QMap<QByteArray,QVariant>()) );
0256     QTest::newRow("untagged-lsub")
0257         << QByteArray("* LSUB (\\Noselect) \".\" \"\"\r\n")
0258         << QSharedPointer<AbstractResponse>( new List( LSUB, QStringList() << QStringLiteral("\\Noselect"), QStringLiteral("."), QLatin1String(""), QMap<QByteArray,QVariant>() ) );
0259     QTest::newRow("untagged-list-moreflags")
0260         << QByteArray("* LIST (\\Noselect Blesmrt) \".\" \"\"\r\n")
0261         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList() << QStringLiteral("\\Noselect") << QStringLiteral("Blesmrt"), QStringLiteral("."), QLatin1String(""), QMap<QByteArray,QVariant>() ) );
0262     QTest::newRow("untagged-list-mailbox")
0263         << QByteArray("* LIST () \".\" \"someName\"\r\n")
0264         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList(), QStringLiteral("."), QStringLiteral("someName"), QMap<QByteArray,QVariant>() ) );
0265     QTest::newRow("untagged-list-mailbox-atom")
0266         << QByteArray("* LIST () \".\" someName\r\n")
0267         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList(), QStringLiteral("."), QStringLiteral("someName"), QMap<QByteArray,QVariant>() ) );
0268     QTest::newRow("untagged-list-separator-nil")
0269         << QByteArray("* LIST () NiL someName\r\n")
0270         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList(), QByteArray(), QStringLiteral("someName"), QMap<QByteArray,QVariant>() ) );
0271     QTest::newRow("untagged-list-mailbox-quote")
0272         << QByteArray("* LIST () \".\" \"some\\\"Name\"\r\n")
0273         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList(), QStringLiteral("."), QStringLiteral("some\"Name"), QMap<QByteArray,QVariant>() ) );
0274     QTest::newRow("untagged-list-unicode")
0275         << QByteArray("* LIST () \"/\" \"~Peter/mail/&U,BTFw-/&ZeVnLIqe-\"\r\n")
0276         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList(), QStringLiteral("/"), QStringLiteral("~Peter/mail/台北/日本語"), QMap<QByteArray,QVariant>() ) );
0277     QTest::newRow("untagged-list-inbox-atom")
0278         << QByteArray("* LIST () \"/\" inBox\r\n")
0279         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList(), QStringLiteral("/"), QStringLiteral("INBOX"), QMap<QByteArray,QVariant>()) );
0280     QTest::newRow("untagged-list-inbox-quoted")
0281         << QByteArray("* LIST () \"/\" \"inBox\"\r\n")
0282         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList(), QStringLiteral("/"), QStringLiteral("INBOX"), QMap<QByteArray,QVariant>()) );
0283     QTest::newRow("untagged-list-inbox-literal")
0284             << QByteArray("* LIST () \"/\" {5}\r\ninBox\r\n")
0285         << QSharedPointer<AbstractResponse>( new List( LIST, QStringList(), QStringLiteral("/"), QStringLiteral("INBOX"), QMap<QByteArray,QVariant>()) );
0286     QTest::newRow("untagged-list-numeric-mailbox")
0287         << QByteArray("* LIST () \"/\" 666\r\n")
0288         << QSharedPointer<AbstractResponse>(new List(LIST, QStringList(), QStringLiteral("/"), QStringLiteral("666"), QMap<QByteArray,QVariant>()));
0289     QTest::newRow("untagged-list-numeric-mixed-mailbox")
0290         << QByteArray("* LIST () \"/\" 666x333\r\n")
0291         << QSharedPointer<AbstractResponse>(new List(LIST, QStringList(), QStringLiteral("/"), QStringLiteral("666x333"), QMap<QByteArray,QVariant>()));
0292     // https://bugs.kde.org/show_bug.cgi?id=334456
0293     QTest::newRow("untagged-list-groupwise-bug-334456")
0294         << QByteArray("* LIST (\\Unmarked) \"/\" \"Calendar/GroupWise.5-Mehrfachbenutzer \\(Standard\\).MultiUser Control\"\r\n")
0295         << QSharedPointer<AbstractResponse>(new List(LIST, QStringList() << QStringLiteral("\\Unmarked"), QStringLiteral("/"),
0296                                                      QStringLiteral("Calendar/GroupWise.5-Mehrfachbenutzer (Standard).MultiUser Control"),
0297                                                      QMap<QByteArray,QVariant>()));
0298 
0299     QMap<QByteArray,QVariant> listExtData;
0300     listExtData["CHILDINFO"] = QStringList() << QStringLiteral("SUBSCRIBED");
0301     QTest::newRow("untagged-list-ext-childinfo")
0302         << QByteArray("* LIST () \"/\" \"Foo\" (\"CHILDINFO\" (\"SUBSCRIBED\"))\r\n")
0303         << QSharedPointer<AbstractResponse>(new List( LIST, QStringList(), QStringLiteral("/"), QStringLiteral("Foo"), listExtData));
0304     listExtData["OLDNAME"] = QStringList() << QStringLiteral("blesmrt");
0305     QTest::newRow("untagged-list-ext-childinfo-2")
0306         << QByteArray("* LIST () \"/\" \"Foo\" (\"CHILDINFO\" (\"SUBSCRIBED\") \"OLDNAME\" (\"blesmrt\"))\r\n")
0307         << QSharedPointer<AbstractResponse>(new List( LIST, QStringList(), QStringLiteral("/"), QStringLiteral("Foo"), listExtData));
0308 
0309     QTest::newRow("untagged-flags")
0310         << QByteArray("* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n")
0311         << QSharedPointer<AbstractResponse>(
0312                 new Flags( QStringList() << QStringLiteral("\\Answered") << QStringLiteral("\\Flagged") <<
0313                     QStringLiteral("\\Deleted") << QStringLiteral("\\Seen") << QStringLiteral("\\Draft") ) );
0314     QTest::newRow("untagged-flags-numeric")
0315         << QByteArray("* FLAGS (333 666x333)\r\n")
0316         << QSharedPointer<AbstractResponse>(new Flags(QStringList() << QStringLiteral("333") << QStringList(QStringLiteral("666x333"))));
0317     QTest::newRow("search-empty")
0318         << QByteArray("* SEARCH\r\n")
0319         << QSharedPointer<AbstractResponse>(new Search(Imap::Uids()));
0320     QTest::newRow("search-messages")
0321         << QByteArray("* SEARCH 1 33 666\r\n")
0322         << QSharedPointer<AbstractResponse>(new Search(Imap::Uids() << 1 << 33 << 666));
0323 
0324     ESearch::ListData_t esearchData;
0325     QTest::newRow("esearch-empty")
0326         << QByteArray("* ESEARCH\r\n")
0327         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0328 
0329     QTest::newRow("esearch-empty-tag")
0330         << QByteArray("* ESEARCH (TAG x)\r\n")
0331         << QSharedPointer<AbstractResponse>(new ESearch("x", ESearch::SEQUENCE, esearchData));
0332 
0333     QTest::newRow("esearch-empty-uid")
0334         << QByteArray("* ESEARCH UiD\r\n")
0335         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::UIDS, esearchData));
0336 
0337     QTest::newRow("esearch-empty-uid-tag")
0338         << QByteArray("* ESEARCH (TAG \"1\") UiD\r\n")
0339         << QSharedPointer<AbstractResponse>(new ESearch("1", ESearch::UIDS, esearchData));
0340 
0341     esearchData.push_back(qMakePair<>(QByteArray("BLAH"), Imap::Uids() << 10));
0342     QTest::newRow("esearch-one-number")
0343         << QByteArray("* ESEARCH BLaH 10\r\n")
0344         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0345 
0346     QTest::newRow("esearch-uid-one-number")
0347         << QByteArray("* ESEArCH UiD BLaH 10\r\n")
0348         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::UIDS, esearchData));
0349 
0350     QTest::newRow("esearch-tag-one-number")
0351         << QByteArray("* ESEArCH (TaG x) BLaH 10\r\n")
0352         << QSharedPointer<AbstractResponse>(new ESearch("x", ESearch::SEQUENCE, esearchData));
0353 
0354     QTest::newRow("esearch-uid-tag-one-number")
0355         << QByteArray("* ESEArCH (TaG x) Uid BLaH 10\r\n")
0356         << QSharedPointer<AbstractResponse>(new ESearch("x", ESearch::UIDS, esearchData));
0357 
0358     esearchData.push_front(qMakePair<>(QByteArray("FOO"), Imap::Uids() << 666));
0359     esearchData.push_front(qMakePair<>(QByteArray("FOO"), Imap::Uids() << 333));
0360     QTest::newRow("esearch-two-numbers")
0361         << QByteArray("* ESEARCH fOO 333 foo 666   BLaH 10\r\n")
0362         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0363 
0364     esearchData.clear();
0365     esearchData.push_back(qMakePair<>(QByteArray("FOO"), Imap::Uids() << 333));
0366     esearchData.push_back(qMakePair<>(QByteArray("BLAH"), Imap::Uids() << 10));
0367     QTest::newRow("esearch-uid-two-numbers")
0368         << QByteArray("* ESEArCH UiD foo    333 BLaH  10\r\n")
0369         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::UIDS, esearchData));
0370 
0371     QTest::newRow("esearch-tag-two-numbers")
0372         << QByteArray("* ESEArCH (TaG x)    foo 333 BLaH 10  \r\n")
0373         << QSharedPointer<AbstractResponse>(new ESearch("x", ESearch::SEQUENCE, esearchData));
0374 
0375     QTest::newRow("esearch-uid-tag-two-numbers")
0376         << QByteArray("* ESEArCH    (TaG   x)   Uid  foo 333 BLaH 10\r\n")
0377         << QSharedPointer<AbstractResponse>(new ESearch("x", ESearch::UIDS, esearchData));
0378 
0379     esearchData.clear();
0380     esearchData.push_back(qMakePair<>(QByteArray("BLAH"), Imap::Uids() << 10 << 11 << 13 << 14 << 15 << 16 << 17));
0381     QTest::newRow("esearch-one-list-1")
0382         << QByteArray("* ESEARCH BLaH 10,11,13:17\r\n")
0383         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0384 
0385     esearchData.clear();
0386     esearchData.push_back(qMakePair<>(QByteArray("BLAH"), Imap::Uids() << 1 << 2));
0387     QTest::newRow("esearch-one-list-2")
0388         << QByteArray("* ESEARCH BLaH 1:2\r\n")
0389         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0390 
0391     QTest::newRow("esearch-one-list-3")
0392         << QByteArray("* ESEARCH BLaH    1,2\r\n")
0393         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0394 
0395     esearchData.clear();
0396     esearchData.push_back(qMakePair<>(QByteArray("BLAH"), Imap::Uids() << 1 << 2 << 3 << 4 << 5));
0397     QTest::newRow("esearch-one-list-4")
0398         << QByteArray("* ESEARCH BLaH 1,2:4,5\r\n")
0399         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0400 
0401     QTest::newRow("esearch-one-list-extra-space-at-end")
0402         << QByteArray("* ESEARCH BLaH 1,2:4,5   \r\n")
0403         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0404 
0405     esearchData.push_back(qMakePair<>(QByteArray("FOO"), Imap::Uids() << 6));
0406     QTest::newRow("esearch-mixed-1")
0407         << QByteArray("* ESEARCH BLaH 1,2:4,5 FOO 6\r\n")
0408         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0409 
0410     std::swap(esearchData[0], esearchData[1]);
0411     QTest::newRow("esearch-mixed-2")
0412         << QByteArray("* ESEARCH FOO 6 BLaH 1,2:4,5\r\n")
0413         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0414 
0415     esearchData.push_back(qMakePair<>(QByteArray("BAZ"), Imap::Uids() << 33));
0416     QTest::newRow("esearch-mixed-3")
0417         << QByteArray("* ESEARCH FOO 6   BLaH 1,2:4,5   baz 33  \r\n")
0418         << QSharedPointer<AbstractResponse>(new ESearch(QByteArray(), ESearch::SEQUENCE, esearchData));
0419 
0420     ESearch::IncrementalContextData_t incrementalEsearchData;
0421     incrementalEsearchData.push_back(ESearch::ContextIncrementalItem(ESearch::ContextIncrementalItem::ADDTO, 1, Imap::Uids() << 2733));
0422     incrementalEsearchData.push_back(ESearch::ContextIncrementalItem(ESearch::ContextIncrementalItem::ADDTO, 1, Imap::Uids() << 2731 << 2732));
0423     QTest::newRow("esearch-context-sort-1")
0424         << QByteArray("* ESEARCH (TAG \"C01\") UID ADDTO (1 2733) ADDTO (1 2731:2732)\r\n")
0425         << QSharedPointer<AbstractResponse>(new ESearch("C01", ESearch::UIDS, incrementalEsearchData));
0426 
0427     QTest::newRow("esearch-context-sort-2")
0428         << QByteArray("* ESEARCH (TAG \"C01\") UID ADDTO (1 2733 1 2731:2732)\r\n")
0429         << QSharedPointer<AbstractResponse>(new ESearch("C01", ESearch::UIDS, incrementalEsearchData));
0430 
0431     incrementalEsearchData.clear();
0432     incrementalEsearchData.push_back(ESearch::ContextIncrementalItem(ESearch::ContextIncrementalItem::ADDTO, 1, Imap::Uids() << 2733));
0433     incrementalEsearchData.push_back(ESearch::ContextIncrementalItem(ESearch::ContextIncrementalItem::ADDTO, 1, Imap::Uids() << 2732));
0434     incrementalEsearchData.push_back(ESearch::ContextIncrementalItem(ESearch::ContextIncrementalItem::ADDTO, 1, Imap::Uids() << 2731));
0435     QTest::newRow("esearch-context-sort-3")
0436         << QByteArray("* ESEARCH (TAG \"C01\") UID ADDTO (1 2733 1 2732 1 2731)\r\n")
0437         << QSharedPointer<AbstractResponse>(new ESearch("C01", ESearch::UIDS, incrementalEsearchData));
0438 
0439     incrementalEsearchData.clear();
0440     incrementalEsearchData.push_back(ESearch::ContextIncrementalItem(ESearch::ContextIncrementalItem::ADDTO, 1, Imap::Uids() << 2731 << 2732 << 2733));
0441     QTest::newRow("esearch-context-sort-4")
0442         << QByteArray("* ESEARCH (TAG \"C01\") UID ADDTO (1 2731:2733)\r\n")
0443         << QSharedPointer<AbstractResponse>(new ESearch("C01", ESearch::UIDS, incrementalEsearchData));
0444 
0445     incrementalEsearchData.clear();
0446     incrementalEsearchData.push_back(ESearch::ContextIncrementalItem(ESearch::ContextIncrementalItem::REMOVEFROM, 0, Imap::Uids() << 32768));
0447     QTest::newRow("esearch-context-sort-5")
0448         << QByteArray("* ESEARCH (TAG \"B01\") UID REMOVEFROM (0 32768)\r\n")
0449         << QSharedPointer<AbstractResponse>(new ESearch("B01", ESearch::UIDS, incrementalEsearchData));
0450 
0451     Status::stateDataType states;
0452     states[Status::MESSAGES] = 231;
0453     states[Status::UIDNEXT] = 44292;
0454     QTest::newRow("status-1")
0455         << QByteArray("* STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292)\r\n")
0456         << QSharedPointer<AbstractResponse>( new Status( QStringLiteral("blurdybloop"), states ) );
0457     QTest::newRow("status-numeric-only")
0458         << QByteArray("* STATUS 333 (MESSAGES 231 UIDNEXT 44292)\r\n")
0459         << QSharedPointer<AbstractResponse>(new Status(QStringLiteral("333"), states));
0460     QTest::newRow("status-numeric-mixed")
0461         << QByteArray("* STATUS 333x666 (MESSAGES 231 UIDNEXT 44292)\r\n")
0462         << QSharedPointer<AbstractResponse>(new Status(QStringLiteral("333x666"), states));
0463     states[Status::UIDVALIDITY] = 1337;
0464     states[Status::RECENT] = 3234567890u;
0465     QTest::newRow("status-2")
0466         << QByteArray("* STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292 UIDVALIDITY 1337 RECENT 3234567890)\r\n")
0467         << QSharedPointer<AbstractResponse>( new Status( QStringLiteral("blurdybloop"), states ) );
0468 
0469     // notice the trailing whitespace
0470     QTest::newRow("status-exchange-botched-1")
0471         << QByteArray("* STATUS blurdybloop (MESSAGES 231 UIDNEXT 44292 UIDVALIDITY 1337 RECENT 3234567890) \r\n")
0472         << QSharedPointer<AbstractResponse>( new Status( QStringLiteral("blurdybloop"), states ) );
0473 
0474     states.clear();
0475     states[Status::MESSAGES] = 113;
0476     QTest::newRow("status-extra-whitespace-around-atoms")
0477             << QByteArray("* STATUS blurdybloop ( MESSAGES 113 )\r\n")
0478             << QSharedPointer<AbstractResponse>( new Status( QStringLiteral("blurdybloop"), states ) );
0479 
0480     // https://bugs.kde.org/show_bug.cgi?id=365149
0481     states.clear();
0482     states[Status::MESSAGES] = 702;
0483     QTest::newRow("untagged-extra-space-before-status")
0484             << QByteArray("*  STATUS \"INBOX\" (MESSAGES 702)\r\n")
0485             << QSharedPointer<AbstractResponse>(new Status(QStringLiteral("INBOX"), states));
0486 
0487 
0488     QTest::newRow("namespace-1")
0489         << QByteArray("* Namespace nil NIL nil\r\n")
0490         << QSharedPointer<AbstractResponse>( new Namespace( QList<NamespaceData>(),
0491                     QList<NamespaceData>(), QList<NamespaceData>() ) );
0492 
0493     QTest::newRow("namespace-2")
0494         << QByteArray("* Namespace () NIL nil\r\n")
0495         << QSharedPointer<AbstractResponse>( new Namespace( QList<NamespaceData>(),
0496                     QList<NamespaceData>(), QList<NamespaceData>() ) );
0497 
0498     QTest::newRow("namespace-3")
0499         << QByteArray("* Namespace ((\"\" \"/\")) NIL nil\r\n")
0500         << QSharedPointer<AbstractResponse>( new Namespace( QList<NamespaceData>() << NamespaceData( QLatin1String(""), QStringLiteral("/") ),
0501                     QList<NamespaceData>(), QList<NamespaceData>() ) );
0502 
0503     QTest::newRow("namespace-4")
0504         << QByteArray("* Namespace ((\"prefix\" \".\")(\"\" \"/\")) ((\"blesmrt\" \"trojita\")) ((\"foo\" \"bar\"))\r\n")
0505         << QSharedPointer<AbstractResponse>( new Namespace(
0506                     QList<NamespaceData>() << NamespaceData( QStringLiteral("prefix"), QStringLiteral(".") ) << NamespaceData( QLatin1String(""), QStringLiteral("/") ),
0507                     QList<NamespaceData>() << NamespaceData( QStringLiteral("blesmrt"), QStringLiteral("trojita") ),
0508                     QList<NamespaceData>() << NamespaceData( QStringLiteral("foo"), QStringLiteral("bar") ) ) );
0509 
0510     QTest::newRow("namespace-5")
0511         << QByteArray("* NAMESPACE ((\"\" \"/\")(\"#mhinbox\" NIL)(\"#mh/\" \"/\")) ((\"~\" \"/\")) "
0512                 "((\"#shared/\" \"/\")(\"#ftp/\" \"/\")(\"#news.\" \".\")(\"#public/\" \"/\"))\r\n")
0513         << QSharedPointer<AbstractResponse>( new Namespace(
0514                     QList<NamespaceData>() << NamespaceData( QLatin1String(""), QStringLiteral("/") ) << NamespaceData( QStringLiteral("#mhinbox"), QByteArray() )
0515                         << NamespaceData( QStringLiteral("#mh/"), QStringLiteral("/") ),
0516                     QList<NamespaceData>() << NamespaceData( QStringLiteral("~"), QStringLiteral("/") ),
0517                     QList<NamespaceData>() << NamespaceData( QStringLiteral("#shared/"), QStringLiteral("/") ) << NamespaceData( QStringLiteral("#ftp/"), QStringLiteral("/") )
0518                         << NamespaceData( QStringLiteral("#news."), QStringLiteral(".") ) << NamespaceData( QStringLiteral("#public/"), QStringLiteral("/") )
0519                     ) );
0520 
0521     QTest::newRow("sort-1")
0522         << QByteArray("* SORT") << QSharedPointer<AbstractResponse>(new Sort(Imap::Uids()));
0523     QTest::newRow("sort-2")
0524         << QByteArray("* SORT ") << QSharedPointer<AbstractResponse>(new Sort(Imap::Uids()));
0525     QTest::newRow("sort-3")
0526         << QByteArray("* SORT 13 1 6 5 7 9 10 11 12 4 3 2 8\r\n")
0527         << QSharedPointer<AbstractResponse>(new Sort(Imap::Uids() << 13 << 1 << 6 << 5 << 7 << 9 << 10 << 11 << 12 << 4 << 3 << 2 << 8));
0528 
0529     ThreadingNode node2(2), node3(3), node6(6), node4(4), node23(23), node44(44), node7(7), node96(96);
0530     QVector<ThreadingNode> rootNodes;
0531     node4.children << node23;
0532     node7.children << node96;
0533     node44.children << node7;
0534     node6.children << node4 << node44;
0535     node3.children << node6;
0536     rootNodes << node2 << node3;
0537     QTest::newRow("thread-1")
0538         << QByteArray("* THREAD (2)(3 6 (4 23)(44 7 96))\r\n")
0539         << QSharedPointer<AbstractResponse>( new Thread( rootNodes ) );
0540 
0541     rootNodes.clear();
0542     ThreadingNode node203(3), node205(5), anonymousNode201;
0543     anonymousNode201.children << node203 << node205;
0544     rootNodes << anonymousNode201;
0545     QTest::newRow("thread-2")
0546         << QByteArray("* THREAD ((3)(5))\r\n")
0547         << QSharedPointer<AbstractResponse>( new Thread( rootNodes ) );
0548 
0549     rootNodes.clear();
0550     ThreadingNode node301(1), node302(2), node303(3);
0551     rootNodes << node301 << node302 << node303;
0552     QTest::newRow("thread-3")
0553         << QByteArray("* THREAD (1)(2)(3)\r\n")
0554         << QSharedPointer<AbstractResponse>( new Thread( rootNodes ) );
0555 
0556     rootNodes.clear();
0557     ThreadingNode node401(1), node402(2), node403(3);
0558     node401.children << node402 << node403;
0559     rootNodes << node401;
0560     QTest::newRow("thread-4")
0561         << QByteArray("* THREAD (1(2)(3))\r\n")
0562         << QSharedPointer<AbstractResponse>( new Thread( rootNodes ) );
0563 
0564     rootNodes.clear();
0565     ThreadingNode node502(2), node503(3), node506(6), node504(4), node523(23),
0566         node544(44), node507(7), node596(96), node513(13), node566(66), anon501, anon502;
0567     node504.children << node523;
0568     node507.children << node596;
0569     node544.children << node507;
0570     anon502.children << node566;
0571     anon501.children << anon502;
0572     node506.children << node504 << node544 << node513 << anon501;
0573     node503.children << node506;
0574     rootNodes << node502 << node503;
0575     QTest::newRow("thread-5")
0576         << QByteArray("* THREAD (2)(3 6 (4 23)(44 7 96) (13) (((66))))\r\n")
0577         << QSharedPointer<AbstractResponse>( new Thread( rootNodes ) );
0578 
0579     rootNodes.clear();
0580     ThreadingNode node608(8), node602(2), node603(3), node604(4), node607(7), node609(9),
0581         node610(10), node611(11), node612(12), node605(5), node601(1), node606(6), node613(13);
0582     node603.children << node604;
0583     node602.children << node603;
0584     rootNodes << node608 << node602 << node607 << node609 << node610;
0585     rootNodes << ThreadingNode(0, QVector<ThreadingNode>() << node611 << node612 );
0586     rootNodes << node605;
0587     rootNodes << ThreadingNode(0, QVector<ThreadingNode>() << node601 << node606 );
0588     rootNodes << node613;
0589     QTest::newRow("thread-6")
0590         << QByteArray("* THREAD (8)(2 3 4)(7)(9)(10)((11)(12))(5)((1)(6))(13)\r\n")
0591         << QSharedPointer<AbstractResponse>( new Thread( rootNodes ) );
0592 
0593     esearchData.clear();
0594     ESearch::IncrementalThreadingData_t esearchThreading;
0595     esearchThreading.push_back(ESearch::IncrementalThreadingItem_t(666, rootNodes));
0596     QTest::newRow("esearch-incthread-single")
0597         << QByteArray("* ESEARCH (TAG \"x\") UID INCTHREAD 666 (8)(2 3 4)(7)(9)(10)((11)(12))(5)((1)(6))(13)\r\n")
0598         << QSharedPointer<AbstractResponse>(new ESearch("x", ESearch::UIDS, esearchThreading));
0599     rootNodes.clear();
0600     rootNodes << ThreadingNode(123, QVector<ThreadingNode>());
0601     esearchThreading.push_back(ESearch::IncrementalThreadingItem_t(333, rootNodes));
0602     QTest::newRow("esearch-incthread-two")
0603         << QByteArray("* ESEARCH (TAG \"x\") UID INCTHREAD 666 (8)(2 3 4)(7)(9)(10)((11)(12))(5)((1)(6))(13) "
0604                       "INCTHREAD 333 (123)\r\n")
0605         << QSharedPointer<AbstractResponse>(new ESearch("x", ESearch::UIDS, esearchThreading));
0606 
0607     Fetch::dataType fetchData;
0608 
0609     QTest::newRow("fetch-empty")
0610         << QByteArray("* 66 FETCH ()\r\n")
0611         << QSharedPointer<AbstractResponse>( new Fetch( 66, fetchData ) );
0612 
0613     fetchData.clear();
0614     fetchData[ "RFC822.SIZE" ] = QSharedPointer<AbstractData>( new RespData<quint64>( 1337 ) );
0615     QTest::newRow("fetch-rfc822-size")
0616         << QByteArray("* 123 FETCH (rfc822.size 1337)\r\n")
0617         << QSharedPointer<AbstractResponse>( new Fetch( 123, fetchData ) );
0618 
0619     fetchData.clear();
0620     fetchData[ "RFC822.SIZE" ] = QSharedPointer<AbstractData>( new RespData<quint64>( 1337 ) );
0621     fetchData[ "UID" ] = QSharedPointer<AbstractData>( new RespData<uint>( 666 ) );
0622     QTest::newRow("fetch-rfc822-size-uid")
0623         << QByteArray("* 123 FETCH (uID 666 rfc822.size 1337)\r\n")
0624         << QSharedPointer<AbstractResponse>( new Fetch( 123, fetchData ) );
0625     QTest::newRow("fetch-rfc822-size-uid-swapped")
0626         << QByteArray("* 123 FETCH (rfc822.size 1337 uId 666)\r\n")
0627         << QSharedPointer<AbstractResponse>( new Fetch( 123, fetchData ) );
0628 
0629     fetchData.clear();
0630     fetchData[ "RFC822.HEADER" ] = QSharedPointer<AbstractData>( new RespData<QByteArray>( "123456789012" ) );
0631     QTest::newRow("fetch-rfc822-header")
0632         << QByteArray("* 123 FETCH (rfc822.header {12}\r\n123456789012)\r\n")
0633         << QSharedPointer<AbstractResponse>( new Fetch( 123, fetchData ) );
0634 
0635     fetchData.clear();
0636     fetchData[ "RFC822.TEXT" ] = QSharedPointer<AbstractData>( new RespData<QByteArray>( "abcdEf" ) );
0637     QTest::newRow("fetch-rfc822-text")
0638         << QByteArray("* 123 FETCH (rfc822.tExt abcdEf)\r\n")
0639         << QSharedPointer<AbstractResponse>( new Fetch( 123, fetchData ) );
0640 
0641     fetchData.clear();
0642     fetchData[ "INTERNALDATE" ] = QSharedPointer<AbstractData>(
0643             new RespData<QDateTime>( QDateTime( QDate(2007, 3, 7), QTime( 14, 3, 32 ), Qt::UTC ) ) );
0644     QTest::newRow("fetch-rfc822-internaldate")
0645         << QByteArray("* 123 FETCH (InternalDate \"07-Mar-2007 15:03:32 +0100\")\r\n")
0646         << QSharedPointer<AbstractResponse>( new Fetch( 123, fetchData ) );
0647 
0648     fetchData.clear();
0649     fetchData[ "INTERNALDATE" ] = QSharedPointer<AbstractData>(
0650             new RespData<QDateTime>( QDateTime( QDate(1981, 4, 6), QTime( 18, 33, 32 ), Qt::UTC ) ) );
0651     QTest::newRow("fetch-rfc822-internaldate-shorter")
0652         << QByteArray("* 123 FETCH (InternalDate \"6-Apr-1981 12:03:32 -0630\")\r\n")
0653         << QSharedPointer<AbstractResponse>( new Fetch( 123, fetchData ) );
0654 
0655     fetchData.clear();
0656     QDateTime date( QDate( 1996, 7, 17 ), QTime( 9, 23, 25 ), Qt::UTC );
0657     QString subject( QStringLiteral("IMAP4rev1 WG mtg summary and minutes"));
0658     QList<MailAddress> from, sender, replyTo, to, cc, bcc;
0659     from.append( MailAddress( QStringLiteral("Terry Gray"), QByteArray(), QStringLiteral("gray"), QStringLiteral("cac.washington.edu")) );
0660     sender = replyTo = from;
0661     to.append( MailAddress( QByteArray(), QByteArray(), QStringLiteral("imap"), QStringLiteral("cac.washington.edu")) );
0662     cc.append( MailAddress( QByteArray(), QByteArray(), QStringLiteral("minutes"), QStringLiteral("CNRI.Reston.VA.US")) );
0663     cc.append( MailAddress( QStringLiteral("John Klensin"), QByteArray(), QStringLiteral("KLENSIN"), QStringLiteral("MIT.EDU")) );
0664     QByteArray messageId( "B27397-0100000@cac.washington.edu" );
0665     fetchData[ "ENVELOPE" ] = QSharedPointer<AbstractData>(
0666             new RespData<Envelope>(
0667                 Envelope( date, subject, from, sender, replyTo, to, cc, bcc, QList<QByteArray>(), messageId )
0668                 ) );
0669     QTest::newRow("fetch-envelope")
0670         << QByteArray( "* 12 FETCH (ENVELOPE (\"Wed, 17 Jul 1996 02:23:25 -0700 (PDT)\" "
0671             "\"IMAP4rev1 WG mtg summary and minutes\" "
0672             "((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) "
0673             "((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) "
0674             "((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) "
0675             "((NIL NIL \"imap\" \"cac.washington.edu\")) "
0676             "((NIL NIL \"minutes\" \"CNRI.Reston.VA.US\") "
0677             "(\"John Klensin\" NIL \"KLENSIN\" \"MIT.EDU\")) NIL NIL "
0678             "\"<B27397-0100000@cac.washington.edu>\"))\r\n" )
0679         << QSharedPointer<AbstractResponse>( new Fetch( 12, fetchData ) );
0680 
0681     fetchData.clear();
0682     fetchData[ "ENVELOPE" ] = QSharedPointer<AbstractData>(
0683             new RespData<Envelope>(
0684                 Envelope( QDateTime(), subject, from, sender, replyTo, to, cc, bcc, QList<QByteArray>(), messageId )
0685                 ) );
0686     QTest::newRow("fetch-envelope-nildate")
0687         << QByteArray( "* 13 FETCH (ENVELOPE (NIL "
0688             "\"IMAP4rev1 WG mtg summary and minutes\" "
0689             "((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) "
0690             "((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) "
0691             "((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) "
0692             "((NIL NIL \"imap\" \"cac.washington.edu\")) "
0693             "((NIL NIL \"minutes\" \"CNRI.Reston.VA.US\") "
0694             "(\"John Klensin\" NIL \"KLENSIN\" \"MIT.EDU\")) NIL NIL "
0695             "\"<B27397-0100000@cac.washington.edu>\"))\r\n" )
0696         << QSharedPointer<AbstractResponse>( new Fetch( 13, fetchData ) );
0697 
0698     // FIXME: more unit tests for ENVELOPE and BODY[
0699 
0700     fetchData.clear();
0701     AbstractMessage::bodyFldParam_t bodyFldParam;
0702     AbstractMessage::bodyFldDsp_t bodyFldDsp;
0703     bodyFldParam[ "CHARSET" ] = "UTF-8";
0704     bodyFldParam[ "FORMAT" ] = "flowed";
0705     fetchData[ "BODYSTRUCTURE" ] = QSharedPointer<AbstractData>(
0706             new TextMessage( "text", "plain", bodyFldParam, QByteArray(), QByteArray(),
0707                 "8bit", 362, QByteArray(), bodyFldDsp, QList<QByteArray>(),
0708                 QByteArray(), QVariant(), 15 )
0709             );
0710     QTest::newRow("fetch-bodystructure-plain") <<
0711         QByteArray( "* 3 FETCH (BODYSTRUCTURE (\"text\" \"plain\" (\"chaRset\" \"UTF-8\" \"format\" \"flowed\") NIL NIL \"8bit\" 362 15 NIL NIL NIL))\r\n" ) <<
0712         QSharedPointer<AbstractResponse>( new Fetch( 3, fetchData ) );
0713 
0714 
0715     fetchData.clear();
0716     QList<QSharedPointer<AbstractMessage> > msgList;
0717     // 1.1
0718     bodyFldParam.clear();
0719     bodyFldDsp = AbstractMessage::bodyFldDsp_t();
0720     bodyFldParam[ "CHARSET" ] = "US-ASCII";
0721     bodyFldParam[ "DELSP" ] = "yes";
0722     bodyFldParam[ "FORMAT" ] = "flowed";
0723     msgList.append( QSharedPointer<AbstractMessage>(
0724                 new TextMessage( "text", "plain", bodyFldParam, QByteArray(),
0725                     QByteArray(), "7bit", 990, QByteArray(), bodyFldDsp,
0726                     QList<QByteArray>(), QByteArray(), QVariant(), 27 )
0727                 ) );
0728     // 1.2
0729     bodyFldParam.clear();
0730     bodyFldParam[ "X-MAC-TYPE" ] = "70674453";
0731     bodyFldParam[ "NAME" ] = "PGP.sig";
0732     bodyFldDsp = AbstractMessage::bodyFldDsp_t();
0733     bodyFldDsp.first = "inline";
0734     bodyFldDsp.second[ "FILENAME" ] = "PGP.sig";
0735     msgList.append( QSharedPointer<AbstractMessage>(
0736                 new TextMessage( "application", "pgp-signature", bodyFldParam,
0737                     QByteArray(), "This is a digitally signed message part",
0738                     "7bit", 193, QByteArray(), bodyFldDsp, QList<QByteArray>(),
0739                     QByteArray(), QVariant(), 0 )
0740                 ) );
0741     // 1
0742     bodyFldParam.clear();
0743     bodyFldParam[ "PROTOCOL" ] = "application/pgp-signature";
0744     bodyFldParam[ "MICALG" ] = "pgp-sha1";
0745     bodyFldParam[ "BOUNDARY" ] = "Apple-Mail-10--856231115";
0746     bodyFldDsp = AbstractMessage::bodyFldDsp_t();
0747     fetchData[ "BODYSTRUCTURE" ] = QSharedPointer<AbstractData>(
0748             new MultiMessage( msgList, "signed", bodyFldParam, bodyFldDsp, QList<QByteArray>(), QByteArray(), QVariant() )
0749             );
0750     QTest::newRow("fetch-bodystructure-signed") <<
0751         QByteArray("* 1 FETCH (BODYSTRUCTURE ((\"text\" \"plain\" (\"charset\" \"US-ASCII\" \"delsp\" \"yes\" \"format\" \"flowed\") NIL NIL \"7bit\" 990 27 NIL NIL NIL)(\"application\" \"pgp-signature\" (\"x-mac-type\" \"70674453\" \"name\" \"PGP.sig\") NIL \"This is a digitally signed message part\" \"7bit\" 193 NIL (\"inline\" (\"filename\" \"PGP.sig\")) NIL) \"signed\" (\"protocol\" \"application/pgp-signature\" \"micalg\" \"pgp-sha1\" \"boundary\" \"Apple-Mail-10--856231115\") NIL NIL))\r\n") <<
0752         QSharedPointer<AbstractResponse>( new Fetch( 1, fetchData ) );
0753 
0754     fetchData.clear();
0755     bodyFldParam.clear();
0756     bodyFldParam[ "CHARSET" ] = "UTF-8";
0757     bodyFldParam[ "FORMAT" ] = "flowed";
0758     fetchData[ "BODYSTRUCTURE" ] = QSharedPointer<AbstractData>(
0759             new TextMessage( "text", "plain", bodyFldParam, QByteArray(), QByteArray(),
0760                              "quoted-printable", 0, QByteArray(), bodyFldDsp, QList<QByteArray>(), QByteArray(), QVariant(), 0 ) );
0761     QTest::newRow("fetch-exchange-screwup-1") <<
0762             QByteArray("* 61 FETCH "
0763                        "(BODYSTRUCTURE (\"text\" \"plain\" (\"charset\" \"UTF-8\" \"format\" \"flowed\") "
0764                        "NIL NIL \"quoted-printable\" -1 -1 NIL NIL NIL NIL))\r\n") <<
0765             QSharedPointer<AbstractResponse>( new Fetch( 61, fetchData ) );
0766 
0767     msgList.clear();
0768     bodyFldParam.clear();
0769     bodyFldDsp = AbstractMessage::bodyFldDsp_t();
0770     bodyFldParam["CHARSET"] = "utf-8";
0771     msgList.append(QSharedPointer<AbstractMessage>(new TextMessage("text", "plain", bodyFldParam,
0772                                                                    QByteArray(), QByteArray(), "quoted-printable", 333, QByteArray(),
0773                                                                    bodyFldDsp, QList<QByteArray>(), QByteArray(), QVariant(), 10)));
0774     msgList.append(QSharedPointer<AbstractMessage>(new TextMessage("text", "html", bodyFldParam,
0775                                                                    QByteArray(), QByteArray(), "7bit", 666, QByteArray(),
0776                                                                    bodyFldDsp, QList<QByteArray>(), QByteArray(), QVariant(), 20)));
0777     fetchData.clear();
0778     bodyFldParam.clear();
0779     bodyFldParam["BOUNDARY"] = "=_1234";
0780     fetchData["BODYSTRUCTURE"] = QSharedPointer<AbstractData>(new MultiMessage(msgList, "alternative", bodyFldParam, bodyFldDsp, QList<QByteArray>(), QByteArray(), QVariant()));
0781     // Exchange sends utter crap inside the body-fld-dsp, unfortunately.
0782     // This was reported thhrough an encrypted mail in a followup to https://bugs.kde.org/show_bug.cgi?id=334056,
0783     // but the problem is unrelted to the original topic of that bug.
0784     QTest::newRow("fetch-exchange-screwup-bodystructure-body-fld-dsp")
0785             << QByteArray("* 1 FETCH (BODYSTRUCTURE ("
0786                           "(\"text\" \"plain\" (\"charset\" \"utf-8\") NIL NIL \"quoted-printable\" 333 10 NIL NIL NIL NIL)"
0787                           "(\"text\" \"html\" (\"charset\" \"utf-8\") NIL NIL \"7bit\" 666 20 NIL NIL NIL NIL) "
0788                           "\"alternative\" (\"boundary\" \"=_1234\") \"random@exchange!crap!goes!here!\" NIL))\r\n")
0789             << QSharedPointer<AbstractResponse>(new Fetch(1, fetchData));
0790 
0791     fetchData.clear();
0792     bodyFldParam.clear();
0793     bodyFldDsp = AbstractMessage::bodyFldDsp_t();
0794     bodyFldDsp.first = "inline";
0795     bodyFldParam["CHARSET"] = "utf-8";
0796     fetchData["BODYSTRUCTURE"] = QSharedPointer<AbstractData>(
0797                 new TextMessage("text", "html", bodyFldParam, QByteArray(), QByteArray(), "quoted-printable", 4848,
0798                                 QByteArray(), bodyFldDsp, QList<QByteArray>(), QByteArray(), QVariant(), 25));
0799     QTest::newRow("fetch-body-fld-dsp-inline-citadel")
0800             << QByteArray("* 12 FETCH (BODYSTRUCTURE (\"text\" \"html\" (\"CHARSET\" \"utf-8\") NIL NIL \"quoted-printable\" 4848 25 NIL (\"inline\") NIL))\r\n")
0801             << QSharedPointer<AbstractResponse>(new Fetch(12, fetchData));
0802 
0803     // GMail and its flawed representation of a nested message/rfc822
0804     fetchData.clear();
0805     from.clear(); from << MailAddress(QStringLiteral("somebody"), QString(), QStringLiteral("info"), QStringLiteral("example.com"));
0806     sender = replyTo = from;
0807     to.clear(); to << MailAddress(QStringLiteral("destination"), QString(), QStringLiteral("foobar"), QStringLiteral("gmail.com"));
0808     cc.clear();
0809     bcc.clear();
0810     fetchData["ENVELOPE"] = QSharedPointer<AbstractData>(
0811             new RespData<Envelope>(
0812                     Envelope( QDateTime(QDate(2011, 1, 11), QTime(9, 21, 42), Qt::UTC), QStringLiteral("blablabla"), from, sender, replyTo, to, cc, bcc, QList<QByteArray>(), QByteArray() )
0813                     ));
0814     fetchData["UID"] = QSharedPointer<AbstractData>(new RespData<uint>(8803));
0815     fetchData["RFC822.SIZE"] = QSharedPointer<AbstractData>(new RespData<quint64>(56144));
0816 
0817     msgList.clear();
0818     bodyFldParam.clear();
0819     bodyFldParam["CHARSET"] = "iso-8859-2";
0820     msgList.append(QSharedPointer<AbstractMessage>(
0821             new TextMessage("text", "plain", bodyFldParam, QByteArray(), QByteArray(), "QUOTED-PRINTABLE", 52, QByteArray(),
0822                             AbstractMessage::bodyFldDsp_t(), QList<QByteArray>(), QByteArray(), QVariant(), 2)));
0823     msgList.append(QSharedPointer<AbstractMessage>(
0824             new TextMessage("text", "html", bodyFldParam, QByteArray(), QByteArray(), "QUOTED-PRINTABLE", 1739, QByteArray(),
0825                             AbstractMessage::bodyFldDsp_t(), QList<QByteArray>(), QByteArray(), QVariant(), 66)));
0826     bodyFldParam.clear();
0827     bodyFldParam["BOUNDARY"] = "----=_NextPart_001_0078_01CBB179.57530990";
0828     msgList = QList<QSharedPointer<AbstractMessage> >() << QSharedPointer<AbstractMessage>(
0829             new MultiMessage( msgList, "alternative", bodyFldParam, AbstractMessage::bodyFldDsp_t(), QList<QByteArray>(), QByteArray(), QVariant() ) );
0830     msgList << QSharedPointer<AbstractMessage>(
0831             new MsgMessage("message", "rfc822", AbstractMessage::bodyFldParam_t(), QByteArray(), QByteArray(), "7BIT", 836,
0832                            QByteArray(), AbstractMessage::bodyFldDsp_t(), QList<QByteArray>(), QByteArray(), QVariant(),
0833                            Envelope(), QSharedPointer<AbstractMessage>(
0834                                    new BasicMessage("attachment", QByteArray(), AbstractMessage::bodyFldParam_t(), QByteArray(), QByteArray(),
0835                                                     QByteArray(), 0, QByteArray(), AbstractMessage::bodyFldDsp_t(), QList<QByteArray>(), QByteArray(), QVariant())
0836                                    ), 0 ));
0837     msgList << QSharedPointer<AbstractMessage>(
0838             new MsgMessage("message", "rfc822", AbstractMessage::bodyFldParam_t(), QByteArray(), QByteArray(), "7BIT", 50785,
0839                            QByteArray(), AbstractMessage::bodyFldDsp_t(), QList<QByteArray>(), QByteArray(), QVariant(),
0840                            Envelope(), QSharedPointer<AbstractMessage>(
0841                                    new BasicMessage("attachment", QByteArray(), AbstractMessage::bodyFldParam_t(), QByteArray(), QByteArray(),
0842                                                     QByteArray(), 0, QByteArray(), AbstractMessage::bodyFldDsp_t(), QList<QByteArray>(), QByteArray(), QVariant())
0843                                    ), 0 ));
0844     bodyFldParam.clear();
0845     bodyFldParam["BOUNDARY"] = "----=_NextPart_000_0077_01CBB179.57530990";
0846     bodyFldDsp = AbstractMessage::bodyFldDsp_t();
0847     fetchData["BODYSTRUCTURE"] = QSharedPointer<AbstractData>(
0848             new MultiMessage( msgList, "mixed", bodyFldParam, bodyFldDsp, QList<QByteArray>(), QByteArray(), QVariant()));
0849     QTest::newRow("fetch-envelope-blupix-gmail")
0850             << QByteArray("* 6116 FETCH (UID 8803 RFC822.SIZE 56144 ENVELOPE (\"Tue, 11 Jan 2011 10:21:42 +0100\" "
0851                           "\"blablabla\" ((\"somebody\" NIL \"info\" \"example.com\")) "
0852                           "((\"somebody\" NIL \"info\" \"example.com\")) "
0853                           "((\"somebody\" NIL \"info\" \"example.com\")) "
0854                           "((\"destination\" NIL \"foobar\" \"gmail.com\")) "
0855                           "NIL NIL NIL \"\") "
0856                           "BODYSTRUCTURE (((\"TEXT\" \"PLAIN\" (\"CHARSET\" \"iso-8859-2\") NIL NIL "
0857                           "\"QUOTED-PRINTABLE\" 52 2 NIL NIL NIL)(\"TEXT\" \"HTML\" (\"CHARSET\" \"iso-8859-2\") "
0858                           "NIL NIL \"QUOTED-PRINTABLE\" 1739 66 NIL NIL NIL) \"ALTERNATIVE\" "
0859                           "(\"BOUNDARY\" \"----=_NextPart_001_0078_01CBB179.57530990\") NIL NIL)"
0860                           "(\"MESSAGE\" \"RFC822\" NIL NIL NIL \"7BIT\" 836 NIL (\"ATTACHMENT\" NIL) NIL)"
0861                           "(\"MESSAGE\" \"RFC822\" NIL NIL NIL \"7BIT\" 50785 NIL (\"ATTACHMENT\" NIL) NIL) "
0862                           "\"MIXED\" (\"BOUNDARY\" \"----=_NextPart_000_0077_01CBB179.57530990\") NIL NIL))\r\n")
0863             << QSharedPointer<AbstractResponse>( new Fetch( 6116, fetchData ) );
0864 
0865     msgList.clear();
0866     bodyFldParam.clear();
0867     bodyFldParam["CHARSET"] = "UTF-8";
0868     msgList << QSharedPointer<AbstractMessage>(new TextMessage("text", "plain", bodyFldParam, {}, {}, "QUOTED-PRINTABLE", 388, {}, {}, {}, {}, {}, 12));
0869     msgList << QSharedPointer<AbstractMessage>(new TextMessage("text", "html", bodyFldParam, {}, {}, "QUOTED-PRINTABLE", 1576, {}, {}, {}, {}, {}, 46));
0870     bodyFldParam.clear();
0871     bodyFldParam["BOUNDARY"] = "001a113fbc1264f3cd0551bfbcf8";
0872     msgList = QList<QSharedPointer<AbstractMessage>>() << QSharedPointer<AbstractMessage>(
0873         new MultiMessage(msgList, "alternative", bodyFldParam, {}, {}, {}, {}));
0874     bodyFldParam.clear();
0875     bodyFldParam["NAME"] = "icon.png";
0876     bodyFldDsp = AbstractMessage::bodyFldDsp_t();
0877     bodyFldDsp.first = "ATTACHMENT";
0878     bodyFldDsp.second["FILENAME"] = "icon.png";
0879     msgList << QSharedPointer<AbstractMessage>(new BasicMessage("image", "png", bodyFldParam, {}, {}, "BASE64", 7864, {}, bodyFldDsp, {}, {}, {}));
0880     bodyFldParam.clear();
0881     bodyFldParam["BOUNDARY"] = "001a113fbc1264f3bd0551bfbcf7";
0882     msgList = QList<QSharedPointer<AbstractMessage>>() << QSharedPointer<AbstractMessage>(
0883         new MultiMessage(msgList, "related", bodyFldParam, {}, {}, {}, {}));
0884     msgList << QSharedPointer<AbstractMessage>(new BasicMessage("message", "delivery-status", {}, {}, {}, "7BIT", 571, {}, {}, {}, {}, {}));
0885     bodyFldParam.clear();
0886     bodyFldParam["x-trojita-original-mime-type"] = "message/rfc822";
0887     msgList << QSharedPointer<AbstractMessage>(
0888                    new BasicMessage("application", "x-trojita-malformed-part-from-imap-response", bodyFldParam, {}, {},
0889                                     "QUOTED-PRINTABLE", 6109, {}, {}, {}, {}, {}));
0890     bodyFldParam.clear();
0891     bodyFldParam["BOUNDARY"] = "001a113fbc1264e9640551bfbcf6";
0892     bodyFldParam["REPORT-TYPE"] = "delivery-status";
0893     fetchData.clear();
0894     fetchData["BODYSTRUCTURE"] = QSharedPointer<AbstractData>(
0895                 new MultiMessage(msgList, "report", bodyFldParam, {}, {}, {}, {}));
0896     QTest::newRow("2018-gmail-message-rfc822-not-list")
0897             << QByteArray("* 380 FETCH (BODYSTRUCTURE ("
0898                     // A first part of multipart/report: a human-readable version. So far so good.
0899                     "("
0900                       "("
0901                         "(\"TEXT\" \"PLAIN\" (\"CHARSET\" \"UTF-8\") NIL NIL \"QUOTED-PRINTABLE\" 388 12 NIL NIL NIL)"
0902                         "(\"TEXT\" \"HTML\" (\"CHARSET\" \"UTF-8\") NIL NIL \"QUOTED-PRINTABLE\" 1576 46 NIL NIL NIL)"
0903                         " \"ALTERNATIVE\" (\"BOUNDARY\" \"001a113fbc1264f3cd0551bfbcf8\") NIL NIL"
0904                       ")"
0905                       "(\"IMAGE\" \"PNG\" (\"NAME\" \"icon.png\") \"\" NIL \"BASE64\" 7864 NIL (\"ATTACHMENT\" (\"FILENAME\" \"icon.png\")) NIL)"
0906                       " \"RELATED\" (\"BOUNDARY\" \"001a113fbc1264f3bd0551bfbcf7\") NIL NIL"
0907                     ")"
0908                     // The second part: a machine-readable report. OK.
0909                     "(\"MESSAGE\" \"DELIVERY-STATUS\" NIL NIL NIL \"7BIT\" 571 NIL NIL NIL)"
0910                     // Third part: this has a bug, it says that it's a message/rfc822, but it is not because it has a NIL envelope and a NIL body.
0911                     "(\"MESSAGE\" \"RFC822\" NIL NIL NIL \"QUOTED-PRINTABLE\" 6109 NIL NIL NIL)"
0912                     // the top-level multipart/report
0913                     " \"REPORT\" (\"BOUNDARY\" \"001a113fbc1264e9640551bfbcf6\" \"REPORT-TYPE\" \"delivery-status\") NIL NIL))\r\n")
0914             << QSharedPointer<AbstractResponse>(new Fetch(380, fetchData));
0915 
0916     fetchData.clear();
0917     fetchData["UID"] = QSharedPointer<AbstractData>(new RespData<uint>(42463));
0918     fetchData["FLAGS"] = QSharedPointer<AbstractData>(new RespData<QStringList>(QStringList() << QStringLiteral("\\Seen")));
0919     fetchData["MODSEQ"] = QSharedPointer<AbstractData>(new RespData<quint64>(quint64(45278)));
0920     QTest::newRow("fetch-flags-modseq")
0921             << QByteArray("* 11235 FETCH (UID 42463 MODSEQ (45278) FLAGS (\\Seen))\r\n")
0922             << QSharedPointer<AbstractResponse>(new Fetch(11235, fetchData));
0923 
0924     fetchData.clear();
0925     fetchData["FLAGS"] = QSharedPointer<AbstractData>(
0926                 new RespData<QStringList>(QStringList() << QStringLiteral("\\Seen") << QStringLiteral("\\Answered")));
0927     QTest::newRow("fetch-two-flags")
0928             << QByteArray("* 2 FETCH (FLAGS (\\Seen \\Answered))\r\n")
0929             << QSharedPointer<AbstractResponse>(new Fetch(2, fetchData));
0930 
0931     fetchData.clear();
0932     fetchData["UID"] = QSharedPointer<AbstractData>(new RespData<uint>(81));
0933     fetchData["BODY[HEADER.FIELDS (MESSAGE-ID IN-REPLY-TO REFERENCES DATE)]"] = QSharedPointer<AbstractData>(
0934                 new RespData<QByteArray>("01234567\r\n"));
0935     QTest::newRow("fetch-header-fields-1")
0936             << QByteArray("* 81 FETCH (UID 81 BODY[HEADER.FIELDS (MESSAGE-ID In-REPLY-TO REFERENCES DATE)] {10}\r\n01234567\r\n)\r\n")
0937             << QSharedPointer<AbstractResponse>(new Fetch(81, fetchData));
0938 
0939     fetchData.clear();
0940     fetchData["UID"] = QSharedPointer<AbstractData>(new RespData<uint>(81));
0941     fetchData["BODY[HEADER.FIELDS (MESSAGE-ID)]"] = QSharedPointer<AbstractData>(
0942                 new RespData<QByteArray>("01234567\r\n"));
0943     QTest::newRow("fetch-header-fields-2")
0944             << QByteArray("* 81 FETCH (UID 81 BODY[HEADER.FIELDS (MESSAgE-Id)]{10}\r\n01234567\r\n)\r\n")
0945             << QSharedPointer<AbstractResponse>(new Fetch(81, fetchData));
0946 
0947     QTest::newRow("id-nil")
0948             << QByteArray("* ID nIl\r\n")
0949             << QSharedPointer<AbstractResponse>(new Id(QMap<QByteArray,QByteArray>()));
0950 
0951     QTest::newRow("id-empty")
0952             << QByteArray("* ID ()\r\n")
0953             << QSharedPointer<AbstractResponse>(new Id(QMap<QByteArray,QByteArray>()));
0954 
0955     QMap<QByteArray,QByteArray> sampleId;
0956     sampleId["foo "] = "bar";
0957     QTest::newRow("id-something")
0958             << QByteArray("* ID (\"foo \" \"bar\")\r\n")
0959             << QSharedPointer<AbstractResponse>(new Id(sampleId));
0960 
0961     sampleId.clear();
0962     sampleId["v1"] = "333";
0963     sampleId["v2"] = "666x333";
0964     QTest::newRow("id-numeric-bits")
0965             << QByteArray("* ID (\"v1\" 333 \"v2\" 666x333)\r\n")
0966             << QSharedPointer<AbstractResponse>(new Id(sampleId));
0967 
0968     QTest::newRow("enabled-empty")
0969             << QByteArray("* ENABLED\r\n")
0970             << QSharedPointer<AbstractResponse>(new Enabled(QList<QByteArray>()));
0971 
0972     QTest::newRow("enabled-empty-space")
0973             << QByteArray("* ENABLED \r\n")
0974             << QSharedPointer<AbstractResponse>(new Enabled(QList<QByteArray>()));
0975 
0976     QTest::newRow("enabled-condstore")
0977             << QByteArray("* ENABLED CONDSToRE\r\n")
0978             << QSharedPointer<AbstractResponse>(new Enabled(QList<QByteArray>() << "CONDSToRE"));
0979 
0980     QTest::newRow("enabled-condstore-qresync")
0981             << QByteArray("* ENABLED CONDSToRE qresync\r\n")
0982             << QSharedPointer<AbstractResponse>(new Enabled(QList<QByteArray>() << "CONDSToRE" << "qresync"));
0983 
0984     QTest::newRow("vanished-one")
0985             << QByteArray("* VANIShED 1\r\n")
0986             << QSharedPointer<AbstractResponse>(new Vanished(Vanished::NOT_EARLIER, Imap::Uids() << 1));
0987 
0988     QTest::newRow("vanished-earlier-one")
0989             << QByteArray("* VANIShED (EARlIER) 1\r\n")
0990             << QSharedPointer<AbstractResponse>(new Vanished(Vanished::EARLIER, Imap::Uids() << 1));
0991 
0992     QTest::newRow("vanished-earlier-set")
0993             << QByteArray("* VANISHED (EARLIER) 300:303,405,411\r\n")
0994             << QSharedPointer<AbstractResponse>(new Vanished(Vanished::EARLIER, Imap::Uids() << 300 << 301 << 302 << 303 << 405 << 411));
0995 
0996     QTest::newRow("genurlauth-1")
0997             << QByteArray("* GENURLAUTH \"imap://joe@example.com/INBOX/;uid=20/;section=1.2;urlauth=submit+fred:internal:91354a473744909de610943775f92038\"\r\n")
0998             << QSharedPointer<AbstractResponse>(new GenUrlAuth(QStringLiteral("imap://joe@example.com/INBOX/;uid=20/;section=1.2;urlauth=submit+fred:internal:91354a473744909de610943775f92038")));
0999 
1000     QTest::newRow("genurlauth-2")
1001             << QByteArray("* GENURLAUTH meh\r\n")
1002             << QSharedPointer<AbstractResponse>(new GenUrlAuth(QStringLiteral("meh")));
1003 
1004     QSharedPointer<AbstractData> modSeqData(new RespData<quint64>(5875136264581852368ULL));
1005     QTest::newRow("highestmodseq-64bit")
1006             << QByteArray("* OK [HIGHESTMODSEQ 5875136264581852368] x\r\n")
1007             << QSharedPointer<AbstractResponse>(new State("", OK, QStringLiteral("x"), HIGHESTMODSEQ, modSeqData));
1008 
1009     fetchData.clear();
1010     fetchData["UID"] = QSharedPointer<AbstractData>(new RespData<uint>(123));
1011     fetchData["MODSEQ"] = QSharedPointer<AbstractData>(new RespData<quint64>(5875136264581852368ULL));
1012     QTest::newRow("fetch-highestmodseq-64bit")
1013             << QByteArray("* 33 FETCH (UID 123 MODSEQ (5875136264581852368))\r\n")
1014             << QSharedPointer<AbstractResponse>(new Fetch(33, fetchData));
1015 
1016     fetchData.clear();
1017     from.clear();
1018     sender = replyTo = to = cc = bcc = from;
1019     fetchData["ENVELOPE"] = QSharedPointer<AbstractData>(new RespData<Envelope>(
1020             Envelope(QDateTime(), QByteArray(), from, sender, replyTo, to, cc, bcc, QList<QByteArray>(),
1021                      QByteArray("CAPunWhCDfYrqx_Px-072QAjZbog2DFz9O=48WnCxaxOov-VNVQ@mail.gmail.com"))));
1022     QTest::newRow("aox-messageid-spacing")
1023             << QByteArray("* 666 FETCH (ENVELOPE (NIL NIL NIL NIL NIL NIL NIL NIL NIL {70}\r\n"
1024                           "\n <CAPunWhCDfYrqx_Px-072QAjZbog2DFz9O=48WnCxaxOov-VNVQ@mail.gmail.com>))\r\n")
1025             << QSharedPointer<AbstractResponse>(new Fetch(666, fetchData));
1026 }
1027 
1028 /** @short Test that parsing this garbage doesn't result in an expceiton
1029 
1030 In a erfect world with unlimited resources, it would be nice to verify the result of the parsing. However, I'm trying to feed
1031 real-world data to the test cases and this might mean having to construct tens of lines using code which is just tedious to create.
1032 This is better than nothing.
1033 */
1034 void ImapParserParseTest::testParseFetchGarbageWithoutExceptions()
1035 {
1036     QFETCH(QByteArray, line);
1037     try {
1038         QSharedPointer<Imap::Responses::AbstractResponse> r = parser->parseUntagged(line);
1039         QVERIFY(r.dynamicCast<Imap::Responses::Fetch>());
1040     } catch (const Imap::ParseError &err) {
1041         qDebug() << err.what();
1042         QFAIL("Parsing resulted in an exception");
1043     }
1044 }
1045 
1046 void ImapParserParseTest::testParseFetchGarbageWithoutExceptions_data()
1047 {
1048     QTest::addColumn<QByteArray>("line");
1049 
1050     QTest::newRow("fetch-seznam.cz-quirk-minus-two-as-uint")
1051         << QByteArray("* 997 FETCH (UID 8636 ENVELOPE (NIL \"Merkantilismus\" ((\"\" NIL \"tony.szturc\" \"seznam.cz\")) "
1052                       "((\"\" NIL \"tony.szturc\" \"seznam.cz\")) ((\"\" NIL \"tony.szturc\" \"seznam.cz\")) "
1053                       "((\"\" NIL \"xxx\" \"seznam.cz\")) NIL NIL NIL \"\") INTERNALDATE \"15-Jan-2013 12:17:06 +0000\" "
1054                       "BODYSTRUCTURE ((\"text\" \"plain\" (\"charset\" \"us- ascii\") NIL NIL \"7bit\" -2 1 NIL NIL NIL)"
1055                       "(\"application\" \"msword\" (\"name\" "
1056                       "\"=?utf-8?B?NC10cmFkaWNuaV9ob3Nwb2RhcnNrZV9teXNsZW5pLG5vdm92ZWtlX2Vrb25vbWlja2VfdGVvcmllLmRvY2==?=\") "
1057                       "NIL NIL \"base64\" 51146 NIL NIL NIL)"
1058                       "(\"application\" \"msword\" (\"name\" \"=?utf-8?B?NS1ob3Nwb2RhcnNreXZ5dm9qLmRvY2==?=\") NIL NIL "
1059                       "\"base64\" 205984 NIL NIL NIL)"
1060                       "(\"application\" \"vnd.ms-powerpoint\" (\"name\" \"=?utf-8?B?Mi5faG9zcG9kYXJza3lfdnl2b2pfY2Vza3ljaF96ZW1pLnBwdH==?=\") "
1061                       "NIL NIL \"base64\" 3986596 NIL NIL NIL) \"mixed\" NIL NIL NIL) RFC822.SIZE 4245505)\r\n");
1062 
1063     // Test data from http://thread.gmane.org/gmane.mail.squirrelmail.user/26433
1064     QTest::newRow("random-squirrelmail-report")
1065             << QByteArray("* 1 FETCH (BODYSTRUCTURE "
1066                           "(((\"text\" \"plain\" (\"charset\" \"US-ASCII\") NIL NIL \"7bit\" 2 1 NIL NIL "
1067                           "NIL)(\"text\" \"html\" (\"charset\" \"US-ASCII\") NIL NIL \"quoted-printable\" 486 "
1068                           "10 NIL NIL NIL) \"alternative\" (\"boundary\" "
1069                           "\"-----------------------------1131387468\") NIL NIL)(\"message\" \"rfc822\" NIL "
1070                           "NIL NIL NIL 191761 (\"Thu, 3 Nov 2005 14:19:49 EST\" \"Fwd: Fw: Fw:\" ((NIL "
1071                           "NIL \"Ernestgerp12\" \"aol.com\")) NIL NIL ((NIL NIL \"BagLady1933\" "
1072                           "\"aol.com\")(NIL NIL \"BOBPEGRUKS\" \"cs.com\")(NIL NIL \"bren804\" "
1073                           "\"webtv.net\")(NIL NIL \"khgsk\" \"comcast.net\")(NIL NIL \"KOKOGOSIK\" "
1074                           "\"COMCAST.NET\")(NIL NIL \"MAG2215\" \"aol.com\")(NIL NIL \"Mamabenc\" "
1075                           "\"aol.com\")(NIL NIL \"Ifix2th\" \"aol.com\")(NIL NIL \"Ooosal\" \"aol.com\")(NIL "
1076                           "NIL \"rtalalaj\" \"comcast.net\")) NIL NIL NIL \"e3.1fdeccbc.309bbcd5 <at> aol.com\") "
1077                           "(((\"text\" \"plain\" (\"charset\" \"US-ASCII\") NIL NIL \"7bit\" 2 1 NIL NIL "
1078                           "NIL)(\"text\" \"html\" (\"charset\" \"US-ASCII\") NIL NIL \"quoted-printable\" 486 "
1079                           "10 NIL NIL NIL) \"alternative\" (\"boundary\" "
1080                           "\"-----------------------------1131045588\") NIL NIL)(\"message\" \"rfc822\" NIL "
1081                           "NIL NIL NIL 190153 NIL (((\"text\" \"plain\" (\"charset\" \"iso-8859-1\") NIL NIL "
1082                           "\"quoted-printable\" 6584 293 NIL NIL NIL)(\"text\" \"html\" (\"charset\" "
1083                           "\"iso-8859-1\") NIL NIL \"quoted-printable\" 61190 948 NIL NIL NIL) "
1084                           "\"alternative\" (\"boundary\" \"----=_NextPart_001_004C_01C5E044.F95592A0\") NIL "
1085                           "NIL)(\"image\" \"gif\" (\"name\" \"ATT0000712221112121.gif\") NIL NIL \"base64\" "
1086                           "68496 NIL NIL NIL)(\"image\" \"gif\" (\"name\" \"ATT0001023332223232.gif\") NIL "
1087                           "NIL \"base64\" 20038 NIL NIL NIL)(\"image\" \"gif\" (\"name\" "
1088                           "\"image001334343.gif\") NIL NIL \"base64\" 10834 NIL NIL NIL)(\"image\" \"gif\" "
1089                           "(\"name\" \"ATT0001646665545455.gif\") NIL NIL \"base64\" 7146 NIL NIL "
1090                           "NIL)(\"audio\" \"mid\" (\"name\" \"ATT0001951116651516.mid\") NIL NIL \"base64\" "
1091                           "12938 NIL NIL NIL) \"related\" (\"boundary\" "
1092                           "\"----=_NextPart_000_004B_01C5E044.F95592A0\" \"type\" "
1093                           "\"multipart/alternative\") NIL NIL) NIL NIL (\"INLINE\" NIL) NIL) \"mixed\" "
1094                           "(\"boundary\" \"part2_d7.319e0427.309bbcd5_boundary\") NIL NIL) NIL NIL "
1095                           "(\"INLINE\" NIL) NIL) \"mixed\" (\"boundary\" "
1096                           "\"part1_d7.319e0427.30a0f44c_boundary\") NIL NIL)"
1097                           " )\r\n");
1098 
1099     QTest::newRow("davmail-by-ashp")
1100             << QByteArray("* 1125 FETCH (BODYSTRUCTURE ((\"TEXT\" \"HTML\" (\"CHARSET\" \"ISO-8859-1\") NIL NIL \"QUOTED-PRINTABLE\" 562 7)"
1101                           "(\"APPLICATION\" \"OCTET-STREAM\" (\"NAME\" \"zzz.xml\") NIL "
1102                           "\"ZZZ.XML\" \"BASE64\" NIL NIL) \"MIXED\"))\r\n");
1103 }
1104 
1105 void ImapParserParseTest::benchmark()
1106 {
1107     QByteArray line1 = "* 1 FETCH (BODYSTRUCTURE ((\"text\" \"plain\" "
1108         "(\"charset\" \"US-ASCII\" \"delsp\" \"yes\" \"format\" \"flowed\") "
1109         "NIL NIL \"7bit\" 990 27 NIL NIL NIL)"
1110         "(\"application\" \"pgp-signature\" (\"x-mac-type\" \"70674453\" \"name\" \"PGP.sig\") NIL "
1111         "\"This is a digitally signed message part\" \"7bit\" 193 NIL (\"inline\" "
1112         "(\"filename\" \"PGP.sig\")) NIL) \"signed\" (\"protocol\" "
1113         "\"application/pgp-signature\" \"micalg\" \"pgp-sha1\" \"boundary\" "
1114         "\"Apple-Mail-10--856231115\") NIL NIL))\r\n";
1115     QByteArray line2 = "* 13 FETCH (ENVELOPE (NIL "
1116             "\"IMAP4rev1 WG mtg summary and minutes\" "
1117             "((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) "
1118             "((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) "
1119             "((\"Terry Gray\" NIL \"gray\" \"cac.washington.edu\")) "
1120             "((NIL NIL \"imap\" \"cac.washington.edu\")) "
1121             "((NIL NIL \"minutes\" \"CNRI.Reston.VA.US\") "
1122             "(\"John Klensin\" NIL \"KLENSIN\" \"MIT.EDU\")) NIL NIL "
1123             "\"<B27397-0100000@cac.washington.edu>\"))\r\n";
1124     QByteArray line3 = "* 123 FETCH (InternalDate \"6-Apr-1981 12:03:32 -0630\")\r\n";
1125 
1126     QBENCHMARK {
1127         parser->processLine( line1 );
1128         parser->processLine( line2 );
1129         parser->processLine( line3 );
1130 
1131         while (parser->hasResponse())
1132             parser->getResponse();
1133     }
1134 }
1135 
1136 void ImapParserParseTest::benchmarkInitialChat()
1137 {
1138     QByteArray line4 = "* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE STARTTLS AUTH=PLAIN] Dovecot ready.\r\n";
1139     QByteArray line5 = "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE STARTTLS AUTH=PLAIN\r\n";
1140     QByteArray line6 = "1 OK Pre-login capabilities listed, post-login capabilities have more.\r\n";
1141     QByteArray line7 = "* CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY "
1142         "THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 "
1143         "CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS SPECIAL-USE\r\n";
1144     QByteArray line8 = "2 OK Logged in\r\n";
1145     QByteArray line9 = "3 OK Capability completed.\r\n";
1146     QByteArray line10 = "* ENABLED QRESYNC\r\n";
1147     QByteArray line11 = "4 OK Enabled.\r\n";
1148     QByteArray line12 = "* NAMESPACE ((\"\" \".\")) NIL NIL\r\n";
1149     QByteArray line13 = "5 OK Namespace completed.\r\n";
1150 
1151     QBENCHMARK {
1152         parser->processLine(line4);
1153         parser->processLine(line5);
1154         parser->processLine(line6);
1155         parser->processLine(line7);
1156         parser->processLine(line8);
1157         parser->processLine(line9);
1158         parser->processLine(line10);
1159         parser->processLine(line11);
1160         parser->processLine(line12);
1161         parser->processLine(line13);
1162         while (parser->hasResponse())
1163             parser->getResponse();
1164     }
1165 }
1166 
1167 void ImapParserParseTest::testSequences()
1168 {
1169     QFETCH( Imap::Sequence, sequence );
1170     QFETCH( QByteArray, muster );
1171     QCOMPARE( sequence.toByteArray(), muster );
1172 }
1173 
1174 void ImapParserParseTest::testSequences_data()
1175 {
1176     QTest::addColumn<Imap::Sequence>("sequence");
1177     QTest::addColumn<QByteArray>("muster");
1178 
1179     QTest::newRow("sequence-one") <<
1180             Imap::Sequence( 33 ) << QByteArray("33");
1181 
1182     QTest::newRow("sequence-unlimited") <<
1183             Imap::Sequence::startingAt(666) << QByteArray("666:*");
1184 
1185     QTest::newRow("sequence-range") <<
1186             Imap::Sequence( 333, 666 ) << QByteArray("333:666");
1187 
1188     QTest::newRow("sequence-distinct") <<
1189             Imap::Sequence( 20 ).add( 10 ).add( 30 ) << QByteArray("10,20,30");
1190 
1191     QTest::newRow("sequence-collapsed-2") <<
1192             Imap::Sequence( 10 ).add( 11 ) << QByteArray("10:11");
1193 
1194     QTest::newRow("sequence-collapsed-3") <<
1195             Imap::Sequence( 10 ).add( 11 ).add( 12 ) << QByteArray("10:12");
1196 
1197     QTest::newRow("sequence-head-and-collapsed") <<
1198             Imap::Sequence( 3 ).add( 31 ).add( 32 ).add( 33 ) << QByteArray("3,31:33");
1199 
1200     QTest::newRow("sequence-collapsed-and-tail") <<
1201             Imap::Sequence( 666 ).add( 31 ).add( 32 ).add( 33 ) << QByteArray("31:33,666");
1202 
1203     QTest::newRow("sequence-head-collapsed-tail") <<
1204             Imap::Sequence( 666 ).add( 31 ).add( 32 ).add( 1 ).add( 33 ) << QByteArray("1,31:33,666");
1205 
1206     QTest::newRow("sequence-same") <<
1207             Imap::Sequence( 2 ).add( 2 ) << QByteArray("2");
1208 
1209     QTest::newRow("sequence-multiple-consequent") <<
1210             Imap::Sequence( 2 ).add( 3 ).add( 4 ).add( 6 ).add( 7 ) << QByteArray("2:4,6:7");
1211 
1212     QTest::newRow("sequence-complex") <<
1213             Imap::Sequence( 2 ).add( 3 ).add( 4 ).add( 6 ).add( 7 ).add( 1 )
1214             .add( 100 ).add( 101 ).add( 102 ).add( 99 ).add( 666 ).add( 333 ).add( 666 ) <<
1215             QByteArray("1:4,6:7,99:102,333,666");
1216 
1217     QTest::newRow("sequence-from-list-1") <<
1218             Imap::Sequence::fromVector(Imap::Uids() << 2 << 3 << 4 << 6 << 7 << 1 << 100 << 101 << 102 << 99 << 666 << 333 << 666) <<
1219             QByteArray("1:4,6:7,99:102,333,666");
1220 }
1221 
1222 /** @short Test responses which fail to parse */
1223 void ImapParserParseTest::testThrow()
1224 {
1225     QFETCH(QByteArray, line);
1226     QFETCH(QString, exceptionClass);
1227     QFETCH(QString, error);
1228 
1229     try {
1230         QSharedPointer<Imap::Responses::AbstractResponse> ptr = parser->parseUntagged(line);
1231         QVERIFY2(!ptr, "should have thrown");
1232     } catch (Imap::ImapException &e) {
1233         QCOMPARE(QString::fromUtf8(e.exceptionClass().c_str()), exceptionClass);
1234         QCOMPARE(QString::fromUtf8(e.msg().c_str()), error);
1235     }
1236 }
1237 
1238 /** @short Test data for testThrow() */
1239 void ImapParserParseTest::testThrow_data()
1240 {
1241     QTest::addColumn<QByteArray>("line");
1242     QTest::addColumn<QString>("exceptionClass");
1243     QTest::addColumn<QString>("error");
1244 
1245     QTest::newRow("garbage")
1246             << QByteArray("blah")
1247             << QStringLiteral("UnrecognizedResponseKind")
1248             << QStringLiteral("AH"); // uppercase of "blah" after cutting the two leading characters
1249 
1250     QTest::newRow("garbage-well-formed")
1251             << QByteArray("* blah\r\n")
1252             << QStringLiteral("UnrecognizedResponseKind")
1253             << QStringLiteral("BLAH");
1254 
1255     QTest::newRow("expunge-number-at-the-end")
1256             << QByteArray("* expunge 666\r\n") << QStringLiteral("UnexpectedHere") << QStringLiteral("Malformed response: the number should go first");
1257 
1258     QTest::newRow("thread-non-numbers")
1259             << QByteArray("* THREAD (ahoj)\r\n") << QStringLiteral("UnexpectedHere") << QStringLiteral("THREAD response: cannot parse \"ahoj\" as an unsigned integer");
1260 }
1261 
1262 QTEST_GUILESS_MAIN( ImapParserParseTest )
1263 
1264 namespace QTest {
1265 
1266 template<> char * toString( const Imap::Responses::AbstractResponse& resp )
1267 {
1268     QByteArray buf;
1269     QTextStream stream( &buf );
1270     stream << resp;
1271     stream.flush();
1272     return qstrdup( buf.data() );
1273 }
1274 
1275 }