File indexing completed on 2024-05-05 09:36:08

0001 /*
0002     SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "headertest.h"
0008 
0009 #include <QTest>
0010 
0011 #include <kmime_headers.h>
0012 
0013 using namespace KMime;
0014 using namespace KMime::Headers;
0015 using namespace KMime::Headers::Generics;
0016 
0017 // the following test cases are taken from KDE mailinglists, bug reports, RFC 2045,
0018 // RFC 2183 and RFC 2822, Appendix A
0019 
0020 QTEST_MAIN(HeaderTest)
0021 
0022 void HeaderTest::testIdentHeader()
0023 {
0024     // empty header
0025     auto h = new Headers::Generics::Ident();
0026     QVERIFY(h->isEmpty());
0027 
0028     // parse single identifier
0029     h->from7BitString(QByteArray("<1162746587.784559.5038.nullmailer@svn.kde.org>"));
0030     QCOMPARE(h->identifiers().count(), 1);
0031     QCOMPARE(h->identifiers().first(), QByteArray("1162746587.784559.5038.nullmailer@svn.kde.org"));
0032     QCOMPARE(h->asUnicodeString(), QString::fromLatin1("<1162746587.784559.5038.nullmailer@svn.kde.org>"));
0033     QVERIFY(!h->isEmpty());
0034 
0035     // clearing a header
0036     h->clear();
0037     QVERIFY(h->isEmpty());
0038     QVERIFY(h->identifiers().isEmpty());
0039     delete h;
0040 
0041     // parse multiple identifiers
0042     h = new Headers::Generics::Ident();
0043     h->from7BitString(QByteArray("<1234@local.machine.example> <3456@example.net>"));
0044     QCOMPARE(h->identifiers().count(), 2);
0045     auto ids = h->identifiers();
0046     QCOMPARE(ids.takeFirst(), QByteArray("1234@local.machine.example"));
0047     QCOMPARE(ids.first(), QByteArray("3456@example.net"));
0048     delete h;
0049 
0050     // parse multiple identifiers with folded headers
0051     h = new Headers::Generics::Ident();
0052     h->from7BitString(QByteArray("<1234@local.machine.example>\n  <3456@example.net>"));
0053     QCOMPARE(h->identifiers().count(), 2);
0054     ids = h->identifiers();
0055     QCOMPARE(ids.takeFirst(), QByteArray("1234@local.machine.example"));
0056     QCOMPARE(ids.first(), QByteArray("3456@example.net"));
0057 
0058     // appending of new identifiers (with and without angle-brackets)
0059     h->appendIdentifier("<abcd.1234@local.machine.tld>");
0060     h->appendIdentifier("78910@example.net");
0061     QCOMPARE(h->identifiers().count(), 4);
0062 
0063     // assemble the final header
0064     QCOMPARE(h->as7BitString(false), QByteArray("<1234@local.machine.example> <3456@example.net> <abcd.1234@local.machine.tld> <78910@example.net>"));
0065     delete h;
0066 
0067     // parsing of ident with literal domain
0068     h = new Headers::Generics::Ident();
0069     const QByteArray ident = QByteArray("<O55F3Y9E5MmKFwBN@[127.0.0.1]>");
0070     h->appendIdentifier(ident);
0071     QEXPECT_FAIL("", "Parsing strips square brackets.", Continue);
0072     QCOMPARE(h->as7BitString(false), QByteArray(ident));
0073     delete h;
0074 }
0075 
0076 void HeaderTest::testAddressListHeader()
0077 {
0078     // empty header
0079     auto h = new Headers::Generics::AddressList();
0080     QVERIFY(h->isEmpty());
0081 
0082     // parse single simple address
0083     h->from7BitString("joe@where.test");
0084     QVERIFY(!h->isEmpty());
0085     QCOMPARE(h->addresses().count(), 1);
0086     QCOMPARE(h->addresses().first(), QByteArray("joe@where.test"));
0087     QCOMPARE(h->displayNames().count(), 1);
0088     QCOMPARE(h->displayNames().first(), QLatin1String("joe@where.test"));
0089     QCOMPARE(h->displayString(), QLatin1String("joe@where.test"));
0090     QCOMPARE(h->asUnicodeString(), QLatin1String("joe@where.test"));
0091 
0092     // clearing a header
0093     h->clear();
0094     QVERIFY(h->isEmpty());
0095     delete h;
0096 
0097     // parsing and re-assembling a single address with display name
0098     h = new Headers::Generics::AddressList();
0099     h->from7BitString("Pete <pete@silly.example>");
0100     QCOMPARE(h->addresses().count(), 1);
0101     QCOMPARE(h->addresses().first(), QByteArray("pete@silly.example"));
0102     QCOMPARE(h->displayNames().first(), QLatin1String("Pete"));
0103     QCOMPARE(h->displayString(), QLatin1String("Pete"));
0104     QCOMPARE(h->asUnicodeString(), QLatin1String("Pete <pete@silly.example>"));
0105     QCOMPARE(h->as7BitString(false), QByteArray("Pete <pete@silly.example>"));
0106     delete h;
0107 
0108     // parsing a single address with legacy comment style display name
0109     h = new Headers::Generics::AddressList();
0110     h->from7BitString("jdoe@machine.example (John Doe)");
0111     QCOMPARE(h->addresses().count(), 1);
0112     QCOMPARE(h->addresses().first(), QByteArray("jdoe@machine.example"));
0113     QCOMPARE(h->displayNames().first(), QLatin1String("John Doe"));
0114     QCOMPARE(h->asUnicodeString(), QLatin1String("John Doe <jdoe@machine.example>"));
0115     delete h;
0116 
0117     // parsing and re-assembling list of different addresses
0118     h = new Headers::Generics::AddressList();
0119     h->from7BitString("Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>");
0120     QCOMPARE(h->addresses().count(), 3);
0121     QStringList names = h->displayNames();
0122     QCOMPARE(names.takeFirst(), QLatin1String("Mary Smith"));
0123     QCOMPARE(names.takeFirst(), QLatin1String("jdoe@example.org"));
0124     QCOMPARE(names.takeFirst(), QLatin1String("Who?"));
0125     QCOMPARE(h->displayString(), QLatin1String("Mary Smith, jdoe@example.org, Who?"));
0126     QCOMPARE(h->as7BitString(false), QByteArray("Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>"));
0127     delete h;
0128 
0129     // same again with some interesting quoting
0130     h = new Headers::Generics::AddressList();
0131     h->from7BitString(R"("Joe Q. Public" <john.q.public@example.com>, <boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>)");
0132     QCOMPARE(h->addresses().count(), 3);
0133     names = h->displayNames();
0134     QCOMPARE(names.takeFirst(), QLatin1String("Joe Q. Public"));
0135     QCOMPARE(names.takeFirst(), QLatin1String("boss@nil.test"));
0136     QCOMPARE(names.takeFirst(), QLatin1String("Giant; \"Big\" Box"));
0137     QCOMPARE(h->as7BitString(false), QByteArray("\"Joe Q. Public\" <john.q.public@example.com>, boss@nil.test, \"Giant; \\\"Big\\\" Box\" <sysservices@example.net>"));
0138     delete h;
0139 
0140     // a display name with non-latin1 content
0141     h = new Headers::Generics::AddressList();
0142     h->from7BitString("Ingo =?iso-8859-15?q?Kl=F6cker?= <kloecker@kde.org>");
0143     QCOMPARE(h->addresses().count(), 1);
0144     QCOMPARE(h->addresses().first(), QByteArray("kloecker@kde.org"));
0145     QCOMPARE(h->displayNames().first(), QString::fromUtf8("Ingo Klöcker"));
0146     QCOMPARE(h->asUnicodeString(), QString::fromUtf8("Ingo Klöcker <kloecker@kde.org>"));
0147     QCOMPARE(h->as7BitString(false), QByteArray("Ingo =?ISO-8859-1?Q?Kl=F6cker?= <kloecker@kde.org>"));
0148     delete h;
0149 
0150 
0151     // a display name with non-latin1 content in both name components
0152     h = new Headers::Generics::AddressList();
0153     const QString testAddress = QString::fromUtf8("Ingö Klöcker <kloecker@kde.org>");
0154     h->fromUnicodeString(testAddress, "utf-8");
0155     QCOMPARE(h->asUnicodeString(), testAddress);
0156     delete h;
0157 
0158     {
0159         // a display name with non-latin1 content in both name components
0160         h = new Headers::Generics::AddressList();
0161         const QString testAddress = QString::fromUtf8("\"Rüedi-Huser, Thomas\" <test@test.org>");
0162         h->fromUnicodeString(testAddress, "utf-8");
0163         QEXPECT_FAIL("", "AddressList::prettyAddresses() does not quote the mailbox correctly", Continue);
0164         QCOMPARE(h->asUnicodeString(), testAddress);
0165         delete h;
0166     }
0167 
0168     // again, this time legacy style
0169     h = new Headers::Generics::AddressList();
0170     h->from7BitString("kloecker@kde.org (Ingo =?iso-8859-15?q?Kl=F6cker?=)");
0171     QCOMPARE(h->addresses().count(), 1);
0172     QCOMPARE(h->addresses().first(), QByteArray("kloecker@kde.org"));
0173     QCOMPARE(h->displayNames().first(), QString::fromUtf8("Ingo Klöcker"));
0174     delete h;
0175 
0176     // parsing a empty group
0177     h = new Headers::Generics::AddressList();
0178     h->from7BitString("Undisclosed recipients:;");
0179     QCOMPARE(h->addresses().count(), 0);
0180     delete h;
0181 
0182     // parsing and re-assembling a address list with a group
0183     h = new Headers::Generics::AddressList();
0184     h->from7BitString("A Group:Chris Jones <c@a.test>,joe@where.test,John <jdoe@one.test>;");
0185     QCOMPARE(h->addresses().count(), 3);
0186     names = h->displayNames();
0187     QCOMPARE(names.takeFirst(), QLatin1String("Chris Jones"));
0188     QCOMPARE(names.takeFirst(), QLatin1String("joe@where.test"));
0189     QCOMPARE(names.takeFirst(), QLatin1String("John"));
0190     QCOMPARE(h->as7BitString(false), QByteArray("Chris Jones <c@a.test>, joe@where.test, John <jdoe@one.test>"));
0191     delete h;
0192 
0193     // modifying a header
0194     h = new Headers::Generics::AddressList();
0195     h->from7BitString("John <jdoe@one.test>");
0196     h->addAddress("<kloecker@kde.org>", QString::fromUtf8("Ingo Klöcker"));
0197     h->addAddress("c@a.test");
0198     QCOMPARE(h->addresses().count(), 3);
0199     QCOMPARE(h->asUnicodeString(), QString::fromUtf8("John <jdoe@one.test>, Ingo Klöcker <kloecker@kde.org>, c@a.test"));
0200     QCOMPARE(h->as7BitString(false), QByteArray("John <jdoe@one.test>, Ingo =?ISO-8859-1?Q?Kl=F6cker?= <kloecker@kde.org>, c@a.test"));
0201     delete h;
0202 
0203     // parsing from utf-8
0204     h = new Headers::Generics::AddressList();
0205     h->fromUnicodeString(QString::fromUtf8("Ingo Klöcker <kloecker@kde.org>"), "utf-8");
0206     QCOMPARE(h->addresses().count(), 1);
0207     QCOMPARE(h->addresses().first(), QByteArray("kloecker@kde.org"));
0208     QCOMPARE(h->displayNames().first(), QString::fromUtf8("Ingo Klöcker"));
0209     delete h;
0210 
0211     // based on bug #137033, a header broken in various ways: ';' as list separator,
0212     // unquoted '.' in display name
0213     h = new Headers::Generics::AddressList();
0214     h->from7BitString("Vice@censored.serverkompetenz.net,\n    President@mail2.censored.net;\"Int\\\\\\\\\\\\\\\\\\\\'l\" Lotto Commission. <censored@yahoo.fr>");
0215     QCOMPARE(h->addresses().count(), 3);
0216     names = h->displayNames();
0217     QCOMPARE(names.takeFirst(), QLatin1String("Vice@censored.serverkompetenz.net"));
0218     QCOMPARE(names.takeFirst(), QLatin1String("President@mail2.censored.net"));
0219     // there is an wrong ' ' after the name, but since the header is completely
0220     // broken we can be happy it parses at all...
0221     QCOMPARE(names.takeFirst(), QLatin1String("Int\\\\\\\\\\'l Lotto Commission. "));
0222     auto addrs = h->addresses();
0223     QCOMPARE(addrs.takeFirst(), QByteArray("Vice@censored.serverkompetenz.net"));
0224     QCOMPARE(addrs.takeFirst(), QByteArray("President@mail2.censored.net"));
0225     QCOMPARE(addrs.takeFirst(), QByteArray("censored@yahoo.fr"));
0226     delete h;
0227 
0228     // based on bug #102010, a display name containing '<'
0229     h = new Headers::Generics::AddressList();
0230     h->from7BitString("\"|<onrad\" <censored@censored.dy>");
0231     QCOMPARE(h->addresses().count(), 1);
0232     QCOMPARE(h->addresses().first(), QByteArray("censored@censored.dy"));
0233     QCOMPARE(h->displayNames().first(), QLatin1String("|<onrad"));
0234     QCOMPARE(h->as7BitString(false), QByteArray("\"|<onrad\" <censored@censored.dy>"));
0235     delete h;
0236 
0237     // based on bug #93790 (legacy display name with nested comments)
0238     h = new Headers::Generics::AddressList();
0239     h->from7BitString("first.name@domain.tld (first name (nickname))");
0240     QCOMPARE(h->displayNames().count(), 1);
0241     QCOMPARE(h->displayNames().first(), QLatin1String("first name (nickname)"));
0242     QCOMPARE(h->as7BitString(false), QByteArray("\"first name (nickname)\" <first.name@domain.tld>"));
0243     delete h;
0244 
0245     // rfc 2047 encoding in quoted name (it is not allowed there as per the RFC, but it happens)
0246     // some software == current KMail (v1.12.90) ...
0247     h = new Headers::Generics::AddressList();
0248     h->from7BitString(QByteArray("\"Ingo =?iso-8859-15?q?Kl=F6cker?=\" <kloecker@kde.org>"));
0249     QCOMPARE(h->mailboxes().count(), 1);
0250     QCOMPARE(h->asUnicodeString(), QString::fromUtf8("Ingo Klöcker <kloecker@kde.org>"));
0251     delete h;
0252 
0253     // corner case of almost-rfc2047 encoded string in quoted string but not
0254     h = new Headers::Generics::AddressList();
0255     h->from7BitString("\"Some =Use ?r\" <user@example.com>");
0256     QCOMPARE(h->mailboxes().count(), 1);
0257     QCOMPARE(h->as7BitString(false), QByteArray("\"Some =Use ?r\" <user@example.com>"));
0258     delete h;
0259 
0260     // corner case of almost-rfc2047 encoded string in quoted string but not
0261     h = new Headers::Generics::AddressList();
0262     h->from7BitString("\"Some ?=U=?se =?r\" <user@example.com>");
0263     QCOMPARE(h->mailboxes().count(), 1);
0264     QCOMPARE(h->as7BitString(false), QByteArray("\"Some ?=U=?se =?r\" <user@example.com>"));
0265     delete h;
0266 
0267     // based on bug #139477, trailing '.' in domain name (RFC 3696, section 2 - https://tools.ietf.org/html/rfc3696#page-4)
0268     h = new Headers::Generics::AddressList();
0269     h->from7BitString("joe@where.test.");
0270     QVERIFY(!h->isEmpty());
0271     QCOMPARE(h->addresses().count(), 1);
0272     QCOMPARE(h->addresses().first(), QByteArray("joe@where.test."));
0273     QCOMPARE(h->displayNames().count(), 1);
0274     QCOMPARE(h->displayNames().first(), QLatin1String("joe@where.test."));
0275     QCOMPARE(h->asUnicodeString(), QLatin1String("joe@where.test."));
0276     delete h;
0277 
0278     h = new Headers::Generics::AddressList();
0279     h->from7BitString("Mary Smith <mary@x.test>, jdoe@example.org., Who? <one@y.test>");
0280     QCOMPARE(h->addresses().count(), 3);
0281     names = h->displayNames();
0282     QCOMPARE(names.takeFirst(), QLatin1String("Mary Smith"));
0283     QCOMPARE(names.takeFirst(), QLatin1String("jdoe@example.org."));
0284     QCOMPARE(names.takeFirst(), QLatin1String("Who?"));
0285     QCOMPARE(h->as7BitString(false), QByteArray("Mary Smith <mary@x.test>, jdoe@example.org., Who? <one@y.test>"));
0286     delete h;
0287 
0288     //Bug 421251
0289     // a display name with non-latin1 content
0290     h = new Headers::Generics::AddressList();
0291     h->from7BitString("=?iso-8859-1?Q?=22I=F1igo_Salvador_Azurmendi=22?= <xalba@clientes.euskaltel.es>");
0292     QCOMPARE(h->addresses().count(), 1);
0293     QCOMPARE(h->addresses().first(), QByteArray("xalba@clientes.euskaltel.es"));
0294     QCOMPARE(h->displayNames().first(), QString::fromUtf8("I\u00F1igo Salvador Azurmendi"));
0295     QCOMPARE(h->asUnicodeString(), QString::fromUtf8("I\u00F1igo Salvador Azurmendi <xalba@clientes.euskaltel.es>"));
0296     QCOMPARE(h->as7BitString(false), QByteArray("=?ISO-8859-1?Q?I=F1igo?= Salvador Azurmendi <xalba@clientes.euskaltel.es>"));
0297     delete h;
0298 }
0299 
0300 void HeaderTest::testMailboxListHeader()
0301 {
0302     // empty header
0303     auto h = new Headers::Generics::MailboxList();
0304     QVERIFY(h->isEmpty());
0305 
0306     // parse single simple address
0307     h->from7BitString("joe_smith@where.test");
0308     QVERIFY(!h->isEmpty());
0309     QCOMPARE(h->mailboxes().count(), 1);
0310     QCOMPARE(h->addresses().count(), 1);
0311     QCOMPARE(h->addresses().first(), QByteArray("joe_smith@where.test"));
0312     QCOMPARE(h->displayNames().count(), 1);
0313     QCOMPARE(h->displayNames().first(), QLatin1String("joe_smith@where.test"));
0314     QCOMPARE(h->displayString(), QLatin1String("joe_smith@where.test"));
0315     QCOMPARE(h->asUnicodeString(), QLatin1String("joe_smith@where.test"));
0316 
0317     // https://bugzilla.novell.com/show_bug.cgi?id=421057 (but apparently this was not the cause of the bug)
0318     h->from7BitString("fr...@ce.sco (Francesco)");
0319     QVERIFY(!h->isEmpty());
0320     QCOMPARE(h->mailboxes().count(), 1);
0321     QCOMPARE(h->displayString(), QLatin1String("Francesco"));
0322     QCOMPARE(h->asUnicodeString(), QLatin1String("Francesco <fr...@ce.sco>"));
0323 
0324     delete h;
0325 }
0326 
0327 void HeaderTest::testSingleMailboxHeader()
0328 {
0329     // empty header
0330     auto h = new Headers::Generics::SingleMailbox();
0331     QVERIFY(h->isEmpty());
0332 
0333     // parse single simple address
0334     h->from7BitString("joe_smith@where.test");
0335     QVERIFY(!h->isEmpty());
0336     QCOMPARE(h->addresses().count(), 1);
0337     QCOMPARE(h->addresses().first(), QByteArray("joe_smith@where.test"));
0338     QCOMPARE(h->displayNames().count(), 1);
0339     QCOMPARE(h->displayNames().first(), QLatin1String("joe_smith@where.test"));
0340     QCOMPARE(h->asUnicodeString(), QLatin1String("joe_smith@where.test"));
0341 
0342     // parse single simple address with display name
0343     h->from7BitString("John Smith <joe_smith@where.test>");
0344     QVERIFY(!h->isEmpty());
0345     QCOMPARE(h->addresses().count(), 1);
0346     QCOMPARE(h->addresses().first(), QByteArray("joe_smith@where.test"));
0347     QCOMPARE(h->displayNames().count(), 1);
0348     QCOMPARE(h->displayNames().first(), QLatin1String("John Smith"));
0349     QCOMPARE(h->asUnicodeString(), QLatin1String("John Smith <joe_smith@where.test>"));
0350     QCOMPARE(h->mailboxes().first().prettyAddress(Types::Mailbox::QuoteAlways),
0351              QLatin1String("\"John Smith\" <joe_smith@where.test>"));
0352 
0353     // parse quoted display name with \ in it
0354     h->from7BitString(R"("Lastname\, Firstname" <firstname.lastname@example.com>)");
0355     QVERIFY(!h->isEmpty());
0356     QCOMPARE(h->addresses().count(), 1);
0357     QCOMPARE(h->addresses().first(), QByteArray("firstname.lastname@example.com"));
0358     QCOMPARE(h->displayNames().count(), 1);
0359     QCOMPARE(h->displayNames().first(), QLatin1String("Lastname, Firstname"));
0360     QCOMPARE(h->asUnicodeString().toLatin1().data(),
0361              "Lastname, Firstname <firstname.lastname@example.com>");
0362     QCOMPARE(h->mailboxes().first().prettyAddress().toLatin1().data(),
0363              "Lastname, Firstname <firstname.lastname@example.com>");
0364     QCOMPARE(h->mailboxes().first().prettyAddress(Types::Mailbox::QuoteWhenNecessary).toLatin1().data(),
0365              "\"Lastname, Firstname\" <firstname.lastname@example.com>");
0366 
0367     // parse quoted display name with " in it
0368     h->from7BitString(R"("John \"the guru\" Smith" <john.smith@mail.domain>)");
0369     QVERIFY(!h->isEmpty());
0370     QCOMPARE(h->addresses().count(), 1);
0371     QCOMPARE(h->addresses().first().data(), "john.smith@mail.domain");
0372     QCOMPARE(h->displayNames().first().toLatin1().data(), "John \"the guru\" Smith");
0373     QCOMPARE(h->mailboxes().first().prettyAddress(Types::Mailbox::QuoteWhenNecessary).toLatin1().data(),
0374              "\"John \\\"the guru\\\" Smith\" <john.smith@mail.domain>");
0375     QCOMPARE(h->as7BitString(false).data(),
0376              "\"John \\\"the guru\\\" Smith\" <john.smith@mail.domain>");
0377 
0378     // The following tests are for broken clients that by accident add quotes inside of encoded words that enclose the
0379     // display name. We strip away those quotes, which is not strictly correct, but much nicer.
0380     h->from7BitString("=?iso-8859-1?Q?=22Andre_Woebbeking=22?= <woebbeking@example.com>");
0381     QVERIFY(!h->isEmpty());
0382     QCOMPARE(h->addresses().count(), 1);
0383     QCOMPARE(h->mailboxes().first().name().toLatin1().data(), "Andre Woebbeking");
0384     h->from7BitString("=?iso-8859-1?Q?=22Andre_=22Mr._Tall=22_Woebbeking=22?= <woebbeking@example.com>");
0385     QVERIFY(!h->isEmpty());
0386     QCOMPARE(h->addresses().count(), 1);
0387     QCOMPARE(h->mailboxes().first().name().toLatin1().data(), "Andre \"Mr. Tall\" Woebbeking");
0388     h->from7BitString("=?iso-8859-1?Q?=22Andre_=22?= =?iso-8859-1?Q?Mr._Tall?= =?iso-8859-1?Q?=22_Woebbeking=22?= <woebbeking@example.com>");
0389     QVERIFY(!h->isEmpty());
0390     QCOMPARE(h->addresses().count(), 1);
0391     QCOMPARE(h->mailboxes().first().name().toLatin1().data(), "Andre \"Mr. Tall\" Woebbeking");
0392 
0393     delete h;
0394 }
0395 
0396 void HeaderTest::testMailCopiesToHeader()
0397 {
0398     Headers::MailCopiesTo *h;
0399 
0400     // empty header
0401     h = new Headers::MailCopiesTo();
0402     QVERIFY(h->isEmpty());
0403     QVERIFY(!h->alwaysCopy());
0404     QVERIFY(!h->neverCopy());
0405 
0406     // set to always copy to poster
0407     h->setAlwaysCopy();
0408     QVERIFY(!h->isEmpty());
0409     QVERIFY(h->alwaysCopy());
0410     QVERIFY(!h->neverCopy());
0411     QCOMPARE(h->as7BitString(), QByteArray("Mail-Copies-To: poster"));
0412 
0413     // set to never copy
0414     h->setNeverCopy();
0415     QVERIFY(!h->isEmpty());
0416     QVERIFY(!h->alwaysCopy());
0417     QVERIFY(h->neverCopy());
0418     QCOMPARE(h->as7BitString(), QByteArray("Mail-Copies-To: nobody"));
0419 
0420     // clear header
0421     h->clear();
0422     QVERIFY(h->isEmpty());
0423     delete h;
0424 
0425     // parse copy to poster
0426     h = new MailCopiesTo;
0427     h->from7BitString("always");
0428     QVERIFY(h->addresses().isEmpty());
0429     QVERIFY(!h->isEmpty());
0430     QVERIFY(h->alwaysCopy());
0431     delete h;
0432 
0433     h = new MailCopiesTo;
0434     h->from7BitString("poster");
0435     QVERIFY(h->addresses().isEmpty());
0436     QVERIFY(!h->isEmpty());
0437     QVERIFY(h->alwaysCopy());
0438     delete h;
0439 
0440     // parse never copy
0441     h = new MailCopiesTo;
0442     h->from7BitString("never");
0443     QVERIFY(h->addresses().isEmpty());
0444     QVERIFY(!h->isEmpty());
0445     QVERIFY(h->neverCopy());
0446     delete h;
0447 
0448     h = new MailCopiesTo;
0449     h->from7BitString("nobody");
0450     QVERIFY(h->addresses().isEmpty());
0451     QVERIFY(!h->isEmpty());
0452     QVERIFY(h->neverCopy());
0453     delete h;
0454 
0455     // parsing is case-insensitive
0456     h = new MailCopiesTo;
0457     h->from7BitString("AlWays");
0458     QVERIFY(h->alwaysCopy());
0459     delete h;
0460 
0461     // parse address
0462     h = new MailCopiesTo;
0463     h->from7BitString("vkrause@kde.org");
0464     QVERIFY(!h->addresses().isEmpty());
0465     QVERIFY(h->alwaysCopy());
0466     QVERIFY(!h->neverCopy());
0467     QCOMPARE(h->as7BitString(), QByteArray("Mail-Copies-To: vkrause@kde.org"));
0468     delete h;
0469 }
0470 
0471 void HeaderTest::testParametrizedHeader()
0472 {
0473     Parametrized *h;
0474 
0475     // empty header
0476     h = new Parametrized();
0477     QVERIFY(h->isEmpty());
0478     QVERIFY(!h->hasParameter(QLatin1String("foo")));
0479 
0480     // add a parameter
0481     h->setParameter(QStringLiteral("filename"), QStringLiteral("bla.jpg"));
0482     QVERIFY(!h->isEmpty());
0483     QVERIFY(h->hasParameter(QLatin1String("filename")));
0484     QVERIFY(h->hasParameter(QLatin1String("FiLeNaMe")));
0485     QVERIFY(!h->hasParameter(QLatin1String("bla.jpg")));
0486     QCOMPARE(h->parameter(QLatin1String("filename")), QLatin1String("bla.jpg"));
0487     QCOMPARE(h->as7BitString(false), QByteArray("filename=\"bla.jpg\""));
0488 
0489     // clear again
0490     h->clear();
0491     QVERIFY(h->isEmpty());
0492     delete h;
0493 
0494     // parse a parameter list
0495     h = new Parametrized;
0496     h->from7BitString("filename=genome.jpeg;\n modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"");
0497     QCOMPARE(h->parameter(QLatin1String("filename")), QLatin1String("genome.jpeg"));
0498     QCOMPARE(h->parameter(QLatin1String("modification-date")), QLatin1String("Wed, 12 Feb 1997 16:29:51 -0500"));
0499     QCOMPARE(h->as7BitString(false), QByteArray("filename=\"genome.jpeg\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\""));
0500     delete h;
0501 
0502     // quoting of whitespaces in parameter value
0503     h = new Parametrized();
0504     h->setParameter(QLatin1String("boundary"), QLatin1String("simple boundary"));
0505     QCOMPARE(h->as7BitString(false), QByteArray("boundary=\"simple boundary\""));
0506     delete h;
0507 
0508     // TODO: test RFC 2047 encoded values
0509     // TODO: test case-insensitive key-names
0510 }
0511 
0512 void HeaderTest::testContentDispositionHeader()
0513 {
0514     ContentDisposition *h;
0515 
0516     // empty header
0517     h = new ContentDisposition();
0518     QVERIFY(h->isEmpty());
0519 
0520     // set some values
0521     h->setFilename(QLatin1String("test.jpg"));
0522     QVERIFY(h->isEmpty());
0523     QVERIFY(h->as7BitString(false).isEmpty());
0524     h->setDisposition(CDattachment);
0525     QVERIFY(!h->isEmpty());
0526     QCOMPARE(h->as7BitString(false), QByteArray("attachment; filename=\"test.jpg\""));
0527     delete h;
0528 
0529     // parse parameter-less header
0530     h = new ContentDisposition;
0531     h->from7BitString("inline");
0532     QCOMPARE(h->disposition(), CDinline);
0533     QVERIFY(h->filename().isEmpty());
0534     QCOMPARE(h->as7BitString(true), QByteArray("Content-Disposition: inline"));
0535     delete h;
0536 
0537     // parse header with parameter
0538     h = new ContentDisposition;
0539     h->from7BitString("attachment; filename=genome.jpeg;\n modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";");
0540     QCOMPARE(h->disposition(), CDattachment);
0541     QCOMPARE(h->filename(), QLatin1String("genome.jpeg"));
0542     delete h;
0543 
0544     // TODO: test for case-insensitive disposition value
0545 
0546     // Bug 362650
0547     h = new ContentDisposition;
0548     h->from7BitString("attachment;\n"
0549      "filename*0*=UTF-8''%D0%AD%D1%82%D0%BE%D0%92%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD;"
0550      "filename*1*=%D0%B8%D0%B5%D0%A1%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D1%8B%D0%BC;"
0551      "filename*2*=%D0%98%D0%BC%D0%B5%D0%BC%D0%A4%D0%B0%D0%B9%D0%BB%D0%B0%D0%A1;"
0552      "filename*3*=%D0%BE%D0%B2%D1%81%D0%B5%D0%BC%D0%91%D0%B5%D0%B7%D0%9F%D1%80;"
0553      "filename*4*=%D0%BE%D0%B1%D0%B5%D0%BB%D0%BE%D0%B2%D0%98%D0%95%D1%89%D1%91;"
0554      "filename*5*=%D0%A0%D0%B0%D0%B7%D0%AD%D1%82%D0%BE%D0%92%D0%BB%D0%BE%D0%B6;"
0555      "filename*6*=%D0%B5%D0%BD%D0%B8%D0%B5%D0%A1%D0%94%D0%BB%D0%B8%D0%BD%D0%BD;"
0556      "filename*7*=%D1%8B%D0%BC%D0%98%D0%BC%D0%B5%D0%BC%D0%A4%D0%B0%D0%B9%D0%BB;"
0557      "filename*8*=%D0%B0%D0%A1%D0%BE%D0%B2%D1%81%D0%B5%D0%BC%D0%91%D0%B5%D0%B7;"
0558      "filename*9*=%D0%9F%D1%80%D0%BE%D0%B1%D0%B5%D0%BB%D0%BE%D0%B2%2E%74%78%74");
0559     QCOMPARE(h->disposition(), CDattachment);
0560     QCOMPARE(h->filename(), QString::fromUtf8("ЭтоВложениеСДлиннымИмемФайлаСовсемБезПробеловИЕщёРазЭтоВложениеСДлиннымИмемФайлаСовсемБезПробелов.txt"));
0561     delete h;
0562 
0563     h = new ContentDisposition;
0564     h->from7BitString("attachment; filename*=UTF-8''%D0%AD%D1%82%D0%BE%D0%92%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5%D0%A1%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D1%8B%D0%BC%D0%98%D0%BC%D0%B5%D0%BC%D0%A4%D0%B0%D0%B9%D0%BB%D0%B0%D0%A1%D0%BE%D0%B2%D1%81%D0%B5%D0%BC%D0%91%D0%B5%D0%B7%D0%9F%D1%80%D0%BE%D0%B1%D0%B5%D0%BB%D0%BE%D0%B2%D0%98%D0%95%D1%89%D1%91%D0%A0%D0%B0%D0%B7%D0%AD%D1%82%D0%BE%D0%92%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5%D0%A1%D0%94%D0%BB%D0%B8%D0%BD%D0%BD%D1%8B%D0%BC%D0%98%D0%BC%D0%B5%D0%BC%D0%A4%D0%B0%D0%B9%D0%BB%D0%B0%D0%A1%D0%BE%D0%B2%D1%81%D0%B5%D0%BC%D0%91%D0%B5%D0%B7%D0%9F%D1%80%D0%BE%D0%B1%D0%B5%D0%BB%D0%BE%D0%B2%2Etxt");
0565     QCOMPARE(h->disposition(), CDattachment);
0566     QCOMPARE(h->filename(), QString::fromUtf8("ЭтоВложениеСДлиннымИмемФайлаСовсемБезПробеловИЕщёРазЭтоВложениеСДлиннымИмемФайлаСовсемБезПробелов.txt"));
0567     delete h;
0568 }
0569 
0570 void HeaderTest::testContentTypeHeader()
0571 {
0572     ContentType *h;
0573 
0574     //Bug 362650 (test is without space => ok)
0575     h = new ContentType;
0576     h->from7BitString("text/plain;\n name=\"=?UTF-8?B?0K3RgtC+0JLQu9C+0LbQtdC90LjQtdCh0JTQu9C40L3QvdGL0LzQmNC8?="
0577                       "=?UTF-8?B?0LXQvNCk0LDQudC70LDQodC+0LLRgdC10LzQkdC10LfQn9GA0L7QsdC1?="
0578                       "=?UTF-8?B?0LvQvtCy0JjQldGJ0ZHQoNCw0LfQrdGC0L7QktC70L7QttC10L3QuNC1?="
0579                       "=?UTF-8?B?0KHQlNC70LjQvdC90YvQvNCY0LzQtdC80KTQsNC50LvQsNCh0L7QstGB?="
0580                       "=?UTF-8?B?0LXQvNCR0LXQt9Cf0YDQvtCx0LXQu9C+0LIudHh0?=\"");
0581     QCOMPARE(h->name(), QString::fromUtf8("ЭтоВложениеСДлиннымИмемФайлаСовсемБезПробеловИЕщёРазЭтоВложениеСДлиннымИмемФайлаСовсемБезПробелов.txt"));
0582     delete h;
0583     h = new ContentType;
0584     h->from7BitString("text/plain;\n name=\"=?UTF-8?B?0K3RgtC+0JLQu9C+0LbQtdC90LjQtdCh0JTQu9C40L3QvdGL0LzQmNC8?="
0585                       " =?UTF-8?B?0LXQvNCk0LDQudC70LDQodC+0LLRgdC10LzQkdC10LfQn9GA0L7QsdC1?="
0586                       " =?UTF-8?B?0LvQvtCy0JjQldGJ0ZHQoNCw0LfQrdGC0L7QktC70L7QttC10L3QuNC1?="
0587                       " =?UTF-8?B?0KHQlNC70LjQvdC90YvQvNCY0LzQtdC80KTQsNC50LvQsNCh0L7QstGB?="
0588                       " =?UTF-8?B?0LXQvNCR0LXQt9Cf0YDQvtCx0LXQu9C+0LIudHh0?=\"");
0589     QCOMPARE(h->name(), QString::fromUtf8("ЭтоВложениеСДлиннымИмемФайлаСовсемБезПробеловИЕщёРазЭтоВложениеСДлиннымИмемФайлаСовсемБезПробелов.txt"));
0590     delete h;
0591 
0592     // empty header
0593     h = new ContentType();
0594     QVERIFY(h->isEmpty());
0595 
0596     // Empty content-type means text/plain (RFC 2045 §5.2)
0597     QVERIFY(h->isPlainText());
0598     QVERIFY(h->isText());
0599 
0600     // set a mimetype
0601     h->setMimeType("text/plain");
0602     QVERIFY(!h->isEmpty());
0603     QCOMPARE(h->mimeType(), QByteArray("text/plain"));
0604     QCOMPARE(h->mediaType(), QByteArray("text"));
0605     QCOMPARE(h->subType(), QByteArray("plain"));
0606     QVERIFY(h->isText());
0607     QVERIFY(h->isPlainText());
0608     QVERIFY(!h->isMultipart());
0609     QVERIFY(!h->isPartial());
0610     QVERIFY(h->isMediatype("text"));
0611     QVERIFY(h->isSubtype("plain"));
0612     QCOMPARE(h->as7BitString(true), QByteArray("Content-Type: text/plain"));
0613 
0614     // add some parameters
0615     h->setId("bla");
0616     h->setCharset("us-ascii");
0617     QCOMPARE(h->as7BitString(false), QByteArray("text/plain; charset=\"us-ascii\"; id=\"bla\""));
0618 
0619     // clear header
0620     h->clear();
0621     QVERIFY(h->isEmpty());
0622     delete h;
0623 
0624     // parse a complete header
0625     h = new ContentType;
0626     h->from7BitString("text/plain; charset=us-ascii (Plain text)");
0627     QVERIFY(h->isPlainText());
0628     QCOMPARE(h->charset(), QByteArray("us-ascii"));
0629     delete h;
0630 
0631     // bug #136631 (name with rfc 2231 style parameter wrapping)
0632     h = new ContentType;
0633     h->from7BitString("text/plain;\n name*0=\"PIN_Brief_box1@xx.xxx.censored_Konfigkarte.confi\";\n name*1=\"guration.txt\"");
0634     QVERIFY(h->isPlainText());
0635     QCOMPARE(h->name(), QLatin1String("PIN_Brief_box1@xx.xxx.censored_Konfigkarte.configuration.txt"));
0636     delete h;
0637 
0638     // bug #197958 (name of Content-Type sent by Mozilla Thunderbird are not parsed -- test case generated with v2.0.0.22)
0639     h = new ContentType;
0640     h->from7BitString("text/plain;\n name=\"=?ISO-8859-1?Q?lor=E9m_ipsum=2Etxt?=\"");
0641     QCOMPARE(h->name(), QString::fromUtf8("lorém ipsum.txt"));
0642     delete h;
0643 
0644     // bug #197958 (name of Content-Type sent by Mozilla Thunderbird are not parsed -- test case generated with v2.0.0.22)
0645     // But with unquoted string
0646     QEXPECT_FAIL("", "Unquoted rfc2047 strings are not supported as of now", Continue);
0647     h = new ContentType;
0648     h->from7BitString("text/plain;\n name==?ISO-8859-1?Q?lor=E9m_ipsum=2Etxt?=");
0649     QCOMPARE(h->name(), QString::fromUtf8("lorém ipsum.txt"));
0650     delete h;
0651 
0652     // make ervin's unit test happy
0653     h = new ContentType;
0654     h->setMimeType("MULTIPART/MIXED");
0655     QVERIFY(h->isMultipart());
0656     QVERIFY(h->isMediatype("multipart"));
0657     QVERIFY(h->isMediatype("Multipart"));
0658     QVERIFY(h->isMediatype("MULTIPART"));
0659     QVERIFY(h->isSubtype("mixed"));
0660     QVERIFY(h->isSubtype("Mixed"));
0661     QVERIFY(h->isSubtype("MIXED"));
0662     QCOMPARE(h->mimeType(), QByteArray("MULTIPART/MIXED"));
0663     QCOMPARE(h->mediaType(), QByteArray("MULTIPART"));
0664     QCOMPARE(h->subType(), QByteArray("MIXED"));
0665     delete h;
0666 
0667 }
0668 
0669 void HeaderTest::testTokenHeader()
0670 {
0671     Token *h;
0672 
0673     // empty header
0674     h = new Token();
0675     QVERIFY(h->isEmpty());
0676 
0677     // set a token
0678     h->setToken("bla");
0679     QVERIFY(!h->isEmpty());
0680     QCOMPARE(h->as7BitString(false), QByteArray("bla"));
0681 
0682     // clear it again
0683     h->clear();
0684     QVERIFY(h->isEmpty());
0685     delete h;
0686 
0687     // parse a header
0688     h = new Token;
0689     h->from7BitString("value (comment)");
0690     QCOMPARE(h->token(), QByteArray("value"));
0691     QCOMPARE(h->as7BitString(false), QByteArray("value"));
0692     delete h;
0693 }
0694 
0695 void HeaderTest::testContentTransferEncoding()
0696 {
0697     ContentTransferEncoding *h;
0698 
0699     // empty header
0700     h = new ContentTransferEncoding();
0701     QVERIFY(h->isEmpty());
0702 
0703     // set an encoding
0704     h->setEncoding(CEbinary);
0705     QVERIFY(!h->isEmpty());
0706     QCOMPARE(h->as7BitString(true), QByteArray("Content-Transfer-Encoding: binary"));
0707 
0708     // clear again
0709     h->clear();
0710     QVERIFY(h->isEmpty());
0711     delete h;
0712 
0713     // parse a header
0714     h = new ContentTransferEncoding;
0715     h->from7BitString("(comment) base64");
0716     QCOMPARE(h->encoding(), CEbase64);
0717     QCOMPARE(h->as7BitString(false), QByteArray("base64"));
0718     delete h;
0719 }
0720 
0721 void HeaderTest::testPhraseListHeader()
0722 {
0723     PhraseList *h;
0724 
0725     // empty header
0726     h = new PhraseList();
0727     QVERIFY(h->isEmpty());
0728     delete h;
0729 
0730     // parse a simple phrase list
0731     h = new PhraseList;
0732     h->from7BitString("foo,\n bar");
0733     QVERIFY(!h->isEmpty());
0734     QCOMPARE(h->phrases().count(), 2);
0735     QStringList phrases = h->phrases();
0736     QCOMPARE(phrases.takeFirst(), QLatin1String("foo"));
0737     QCOMPARE(phrases.takeFirst(), QLatin1String("bar"));
0738     QCOMPARE(h->as7BitString(false), QByteArray("foo, bar"));
0739 
0740     // clear header
0741     h->clear();
0742     QVERIFY(h->isEmpty());
0743     delete h;
0744 
0745     // TODO: encoded/quoted phrases
0746 }
0747 
0748 void HeaderTest::testDotAtomHeader()
0749 {
0750     DotAtom *h;
0751 
0752     // empty header
0753     h = new DotAtom;
0754     QVERIFY(h->isEmpty());
0755 
0756     // parse a simple dot atom
0757     h->from7BitString("1.0 (mime version)");
0758     QVERIFY(!h->isEmpty());
0759     QCOMPARE(h->asUnicodeString(), QLatin1String("1.0"));
0760 
0761     // clear again
0762     h->clear();
0763     QVERIFY(h->isEmpty());
0764     delete h;
0765 
0766     // TODO: more complex atoms
0767 }
0768 
0769 void HeaderTest::testDateHeader()
0770 {
0771     Date *h;
0772 
0773     // empty header
0774     h = new Date();
0775     QVERIFY(h->isEmpty());
0776 
0777     // parse a simple date
0778     h->from7BitString("Fri, 21 Nov 1997 09:55:06 -0600");
0779     QVERIFY(!h->isEmpty());
0780     QCOMPARE(h->dateTime().date(), QDate(1997, 11, 21));
0781     QCOMPARE(h->dateTime().time(), QTime(9, 55, 6));
0782     QCOMPARE(h->dateTime().offsetFromUtc(), -6 * 3600);
0783     QCOMPARE(h->as7BitString(), QByteArray("Date: Fri, 21 Nov 1997 09:55:06 -0600"));
0784 
0785     // clear it again
0786     h->clear();
0787     QVERIFY(h->isEmpty());
0788     delete h;
0789 
0790     // white spaces and comment (from RFC 2822, Appendix A.5)
0791     h = new Date;
0792     h->from7BitString("Thu,\n  13\n    Feb\n  1969\n  23:32\n  -0330 (Newfoundland Time)");
0793     QVERIFY(!h->isEmpty());
0794     QCOMPARE(h->dateTime().date(), QDate(1969, 2, 13));
0795     QCOMPARE(h->dateTime().time(), QTime(23, 32));
0796     QCOMPARE(h->dateTime().offsetFromUtc(), -12600);
0797     QCOMPARE(h->as7BitString(false), QByteArray("Thu, 13 Feb 1969 23:32:00 -0330"));
0798     delete h;
0799 
0800     // obsolete date format (from RFC 2822, Appendix A.6.2)
0801     h = new Date;
0802     h->from7BitString("21 Nov 97 09:55:06 GMT");
0803     QVERIFY(!h->isEmpty());
0804     QCOMPARE(h->dateTime().date(), QDate(1997, 11, 21));
0805     QCOMPARE(h->dateTime().time(), QTime(9, 55, 6));
0806     QCOMPARE(h->dateTime().offsetFromUtc(), 0);
0807     delete h;
0808 
0809     // obsolete whitespaces and commnets (from RFC 2822, Appendix A.6.3)
0810     h = new Date;
0811     h->from7BitString("Fri, 21 Nov 1997 09(comment):   55  :  06 -0600");
0812     QVERIFY(!h->isEmpty());
0813     QCOMPARE(h->dateTime().date(), QDate(1997, 11, 21));
0814     QCOMPARE(h->dateTime().time(), QTime(9, 55, 6));
0815     QCOMPARE(h->dateTime().offsetFromUtc(), -6 * 3600);
0816     delete h;
0817 
0818     // Make sure uppercase OCT is parsed correctly - bug 150620
0819     h = new Date;
0820     h->from7BitString("08 OCT 08 16:54:05 +0000");
0821     QVERIFY(!h->isEmpty());
0822     QCOMPARE(h->dateTime().date(), QDate(2008, 10, 8));
0823     QCOMPARE(h->dateTime().time(), QTime(16, 54, 05));
0824     QCOMPARE(h->dateTime().offsetFromUtc(), 0);
0825     delete h;
0826 
0827     // Test for bug 111633, year < 1970
0828     h = new Date;
0829     h->from7BitString("Mon, 27 Aug 1956 21:31:46 +0200");
0830     QVERIFY(!h->isEmpty());
0831     QCOMPARE(h->dateTime().date(), QDate(1956, 8, 27));
0832     QCOMPARE(h->dateTime().time(), QTime(21, 31, 46));
0833     QCOMPARE(h->dateTime().offsetFromUtc(), +2 * 3600);
0834     delete h;
0835 
0836     // Test for bug 207766
0837     h = new Date;
0838     h->from7BitString("Fri, 18 Sep 2009 04:44:55 -0400");
0839     QVERIFY(!h->isEmpty());
0840     QCOMPARE(h->dateTime().date(), QDate(2009, 9, 18));
0841     QCOMPARE(h->dateTime().time(), QTime(4, 44, 55));
0842     QCOMPARE(h->dateTime().offsetFromUtc(), -4 * 3600);
0843     delete h;
0844 
0845     // Test for bug 260761
0846     h = new Date;
0847     h->from7BitString("Sat, 18 Dec 2010 14:01:21 \"GMT\"");
0848     QVERIFY(!h->isEmpty());
0849     QCOMPARE(h->dateTime().date(), QDate(2010, 12, 18));
0850     QCOMPARE(h->dateTime().time(), QTime(14, 1, 21));
0851     QCOMPARE(h->dateTime().offsetFromUtc(), 0);
0852     delete h;
0853 
0854     // old asctime()-like formatted date; regression to KDE3; see bug 117848
0855     h = new Date;
0856     h->from7BitString("Thu Mar 30 18:36:28 CEST 2006");
0857     QVERIFY(!h->isEmpty());
0858     QCOMPARE(h->dateTime().date(), QDate(2006, 3, 30));
0859     QCOMPARE(h->dateTime().time(), QTime(18, 36, 28));
0860     QCOMPARE(h->dateTime().offsetFromUtc(), 2 * 3600);
0861     delete h;
0862 
0863     h = new Date;
0864     h->from7BitString("Thu Mar 30 18:36:28 2006");
0865     QVERIFY(!h->isEmpty());
0866     QCOMPARE(h->dateTime().date(), QDate(2006, 3, 30));
0867     QCOMPARE(h->dateTime().time(), QTime(18, 36, 28));
0868     QCOMPARE(h->dateTime().offsetFromUtc(), 0);
0869     delete h;
0870 
0871     // regression to KDE3; see bug 54098
0872     h = new Date;
0873     h->from7BitString("Tue, Feb 04, 2003 00:01:20 +0000");
0874     QVERIFY(!h->isEmpty());
0875     QCOMPARE(h->dateTime().date(), QDate(2003, 2, 4));
0876     QCOMPARE(h->dateTime().time(), QTime(0, 1, 20));
0877     QCOMPARE(h->dateTime().offsetFromUtc(), 0);
0878     delete h;
0879 
0880     // date in a DST change to the future so in a time that doesn't exist
0881     // unless you take the timezone into account
0882     h = new Date;
0883     h->from7BitString("Sun, 31 Mar 2013 02:29:44 -0500");
0884     QVERIFY(!h->isEmpty());
0885     QCOMPARE(h->dateTime().date(), QDate(2013, 3, 31));
0886     QCOMPARE(h->dateTime().time(), QTime(2, 29, 44));
0887     QCOMPARE(h->dateTime().offsetFromUtc(), -18000);
0888     delete h;
0889 
0890     // Bug Date: 13/10/20 12:51:47
0891     h = new Date;
0892     h->from7BitString("13/10/20 12:51:47");
0893     QVERIFY(!h->isEmpty());
0894     QCOMPARE(h->dateTime().date(), QDate(2020, 10, 13));
0895     QCOMPARE(h->dateTime().time(), QTime(12, 51, 47));
0896     delete h;
0897 
0898     //28/09/23 16:05:54
0899     h = new Date;
0900     h->from7BitString("28/09/23 16:05:54");
0901     QVERIFY(!h->isEmpty());
0902     QCOMPARE(h->dateTime().date(), QDate(2023, 9, 28));
0903     QCOMPARE(h->dateTime().time(), QTime(16, 05, 54));
0904     delete h;
0905 
0906     // Wed, 12 Apr 2030
0907     h = new Date;
0908     h->from7BitString("Wed, 12 Apr 2030");
0909     QVERIFY(!h->isEmpty());
0910     QCOMPARE(h->dateTime().date(), QDate(2030, 4, 12));
0911     QCOMPARE(h->dateTime().time(), QTime(0, 0, 0));
0912     delete h;
0913 }
0914 
0915 void HeaderTest::testLinesHeader()
0916 {
0917     Lines *h;
0918 
0919     // empty header
0920     h = new Lines();
0921     QVERIFY(h->isEmpty());
0922     QVERIFY(h->as7BitString().isEmpty());
0923 
0924     // set some content
0925     h->setNumberOfLines(5);
0926     QVERIFY(!h->isEmpty());
0927     QCOMPARE(h->as7BitString(), QByteArray("Lines: 5"));
0928 
0929     // clear again
0930     h->clear();
0931     QVERIFY(h->isEmpty());
0932     delete h;
0933 
0934     // parse header with comment
0935     h = new Lines;
0936     h->from7BitString("(this is a comment) 10 (and yet another comment)");
0937     QVERIFY(!h->isEmpty());
0938     QCOMPARE(h->numberOfLines(), 10);
0939     delete h;
0940 }
0941 
0942 void HeaderTest::testNewsgroupsHeader()
0943 {
0944     Newsgroups *h;
0945 
0946     // empty header
0947     h = new Newsgroups();
0948     QVERIFY(h->isEmpty());
0949     QVERIFY(h->as7BitString().isEmpty());
0950 
0951     // set newsgroups
0952     QList<QByteArray> groups;
0953     groups << "gmane.comp.kde.devel.core" << "gmane.comp.kde.devel.buildsystem";
0954     h->setGroups(groups);
0955     QVERIFY(!h->isEmpty());
0956     QCOMPARE(h->as7BitString(), QByteArray("Newsgroups: gmane.comp.kde.devel.core,gmane.comp.kde.devel.buildsystem"));
0957 
0958     // and clear again
0959     h->clear();
0960     QVERIFY(h->isEmpty());
0961     delete h;
0962 
0963     // parse a header
0964     h = new Newsgroups;
0965     h->from7BitString("gmane.comp.kde.devel.core,gmane.comp.kde.devel.buildsystem");
0966     groups = h->groups();
0967     QCOMPARE(groups.count(), 2);
0968     QCOMPARE(groups.takeFirst(), QByteArray("gmane.comp.kde.devel.core"));
0969     QCOMPARE(groups.takeFirst(), QByteArray("gmane.comp.kde.devel.buildsystem"));
0970     delete h;
0971 
0972     // same again, this time with whitespaces and comments
0973     h = new Newsgroups();
0974     h->from7BitString("(comment) gmane.comp.kde.devel.core (second comment),\n gmane.comp.kde.devel.buildsystem (that all)");
0975     groups = h->groups();
0976     QCOMPARE(groups.count(), 2);
0977     QCOMPARE(groups.takeFirst(), QByteArray("gmane.comp.kde.devel.core"));
0978     QCOMPARE(groups.takeFirst(), QByteArray("gmane.comp.kde.devel.buildsystem"));
0979     delete h;
0980 }
0981 
0982 void HeaderTest::testControlHeader()
0983 {
0984     Control *h;
0985 
0986     // empty header
0987     h = new Control();
0988     QVERIFY(h->isEmpty());
0989     QVERIFY(h->as7BitString().isEmpty());
0990 
0991     // set some content
0992     h->setCancel("<foo@bar>");
0993     QVERIFY(!h->isEmpty());
0994     QVERIFY(h->isCancel());
0995     QCOMPARE(h->as7BitString(),  QByteArray("Control: cancel <foo@bar>"));
0996 
0997     // clear again
0998     h->clear();
0999     QVERIFY(h->isEmpty());
1000     delete h;
1001 
1002     // parse a control header
1003     h = new Control;
1004     h->from7BitString("cancel <foo@bar>");
1005     QVERIFY(!h->isEmpty());
1006     QCOMPARE(h->parameter(), QByteArray("<foo@bar>"));
1007     QVERIFY(h->isCancel());
1008     QCOMPARE(h->controlType(), QByteArray("cancel"));
1009     delete h;
1010 }
1011 
1012 void HeaderTest::testReturnPath()
1013 {
1014     ReturnPath *h;
1015 
1016     h = new ReturnPath();
1017     QVERIFY(h->isEmpty());
1018     QVERIFY(h->as7BitString().isEmpty());
1019 
1020     h->from7BitString("<foo@bar>");
1021     QVERIFY(!h->isEmpty());
1022     QCOMPARE(h->as7BitString(true), QByteArray("Return-Path: <foo@bar>"));
1023 
1024     delete h;
1025 }
1026 
1027 void HeaderTest::noAbstractHeaders()
1028 {
1029     From *h2 = new From(); delete h2;
1030     auto h3 = new Sender(); delete h3;
1031     To *h4 = new To(); delete h4;
1032     Cc *h5 = new Cc(); delete h5;
1033     Bcc *h6 = new Bcc(); delete h6;
1034     auto h7 = new ReplyTo(); delete h7;
1035     auto h8 = new Keywords(); delete h8;
1036     auto h9 = new MIMEVersion(); delete h9;
1037     auto h10 = new MessageID(); delete h10;
1038     auto h11 = new ContentID(); delete h11;
1039     auto h12 = new Supersedes(); delete h12;
1040     auto h13 = new InReplyTo(); delete h13;
1041     auto h14 = new References(); delete h14;
1042     auto h15 = new Generic(); delete h15;
1043     auto h16 = new Subject(); delete h16;
1044     auto h17 = new Organization(); delete h17;
1045     auto h18 = new ContentDescription(); delete h18;
1046     auto h22 = new FollowUpTo(); delete h22;
1047     auto h24 = new UserAgent(); delete h24;
1048 }
1049 
1050 void HeaderTest::testInvalidButOkQEncoding()
1051 {
1052     // A stray '?' should not confuse the parser
1053     Subject subject;
1054     subject.from7BitString("=?us-ascii?q?Why?_Why_do_some_clients_violate_the_RFC?" "?=");
1055     QCOMPARE(subject.as7BitString(false), QByteArray("Why? Why do some clients violate the RFC?"));
1056 }
1057 
1058 void HeaderTest::testInvalidQEncoding_data()
1059 {
1060     QTest::addColumn<QString>("encodedWord");
1061 
1062     // All examples below should not be treated as invalid encoded strings, since the '?=' is missing
1063     QTest::newRow("") << QString::fromLatin1("=?us-ascii?q?Why?_Why_do_some_clients_violate_the_RFC??");
1064     QTest::newRow("") << QString::fromLatin1("=?us-ascii?q?Why?_Why_do_some_clients_violate_the_RFC?");
1065     QTest::newRow("") << QString::fromLatin1("=?us-ascii?q?Why?_Why_do_some_clients_violate_the_RFC");
1066 }
1067 
1068 void HeaderTest::testInvalidQEncoding()
1069 {
1070     using namespace HeaderParsing;
1071     QFETCH(QString, encodedWord);
1072 
1073     QByteArray tmp = encodedWord.toLatin1();
1074     const char *data = tmp.data();
1075     const char *start = data + 1;
1076     const char *end = data + strlen(data);
1077     QString result;
1078     QByteArray language;
1079     QByteArray usedCS;
1080     QVERIFY(!parseEncodedWord(start, end, result, language, usedCS));
1081 }
1082 
1083 void HeaderTest::testMissingQuotes()
1084 {
1085     QByteArray str = "multipart/signed; boundary=nextPart22807781.u8zn2zYrSU; micalg=pgp-sha1; protocol=application/pgp-signature";
1086 
1087     Headers::ContentType ct;
1088     ct.from7BitString(str);
1089     QCOMPARE(ct.mimeType(), QByteArray{ "multipart/signed" });
1090     QCOMPARE(ct.boundary(), QByteArray{ "nextPart22807781.u8zn2zYrSU" });
1091     QCOMPARE(ct.parameter(QStringLiteral("micalg")), QStringLiteral("pgp-sha1"));
1092     QCOMPARE(ct.parameter(QStringLiteral("protocol")), QStringLiteral("application/pgp-signature"));
1093 
1094 }
1095 
1096 void HeaderTest::testBug271192()
1097 {
1098     QFETCH(QString, displayName);
1099     QFETCH(bool, quote);
1100 
1101     const QString addrSpec = QLatin1String("example@example.com");
1102     const QString mailbox = (quote ? QLatin1String("\"") : QString()) + displayName +
1103                             (quote ? QLatin1String("\"") : QString()) +
1104                             QLatin1String(" <") + addrSpec + QLatin1String(">");
1105 
1106     auto h = new Headers::Generics::SingleMailbox();
1107     h->fromUnicodeString(mailbox, "utf-8");
1108     QCOMPARE(h->displayNames().size(), 1);
1109     QCOMPARE(h->displayNames().first().toUtf8(), displayName.remove(QLatin1String("\\")).toUtf8());
1110     delete h;
1111     h = nullptr;
1112 
1113     auto h2 = new Headers::Generics::MailboxList();
1114     h2->fromUnicodeString(mailbox + QLatin1String(",") + mailbox, "utf-8");
1115     QCOMPARE(h2->displayNames().size(), 2);
1116     QCOMPARE(h2->displayNames()[0].toUtf8(), displayName.remove(QLatin1String("\\")).toUtf8());
1117     QCOMPARE(h2->displayNames()[1].toUtf8(), displayName.remove(QLatin1String("\\")).toUtf8());
1118     delete h2;
1119     h2 = nullptr;
1120 }
1121 
1122 void HeaderTest::testBug271192_data()
1123 {
1124     QTest::addColumn<QString>("displayName");
1125     QTest::addColumn<bool>("quote");
1126 
1127     QTest::newRow("Plain") << QString::fromUtf8("John Doe") << false;
1128     QTest::newRow("Firstname_1") << QString::fromUtf8("Marc-André Lastname") << false;
1129     QTest::newRow("Firstname_2") << QString::fromUtf8("Интернет-компания Lastname") << false;
1130     QTest::newRow("Lastname") << QString::fromUtf8("Tobias König") << false;
1131     QTest::newRow("Firstname_Lastname") << QString::fromUtf8("Интернет-компания König") << false;
1132     QTest::newRow("Quotemarks") << QString::fromUtf8(R"(John \"Rocky\" Doe)") << true;
1133     QTest::newRow("Quotemarks_nonascii") << QString::fromUtf8("Jöhn \\\"Röcky\\\" Döe") << true;
1134 
1135     QTest::newRow("quote_Plain") << QString::fromUtf8("John Doe") << true;
1136     QTest::newRow("quote_Firstname_1") << QString::fromUtf8("Marc-André Lastname") << true;
1137     QTest::newRow("quote_Firstname_2") << QString::fromUtf8("Интернет-компания Lastname") << true;
1138     QTest::newRow("quote_Lastname") << QString::fromUtf8("Tobias König") << true;
1139     QTest::newRow("quote_Firstname_Lastname") << QString::fromUtf8("Интернет-компания König") << true;
1140     QTest::newRow("quote_LastName_comma_Firstname") << QString::fromUtf8("König, Интернет-компания") << true;
1141 }
1142 
1143 void HeaderTest::testParseNextHeader()
1144 {
1145     QByteArray data("From: konqi@kde.org\nTo: katie@kde.org\n\n");
1146     QByteArrayView dataView(data);
1147 
1148     auto header = KMime::HeaderParsing::parseNextHeader(dataView);
1149     QVERIFY(header);
1150     QCOMPARE(header->type(), "From");
1151     QVERIFY(dataView.startsWith("To:"));
1152 
1153     header = KMime::HeaderParsing::parseNextHeader(dataView);
1154     QVERIFY(header);
1155     QCOMPARE(header->type(), "To");
1156 
1157     QVERIFY(!KMime::HeaderParsing::parseNextHeader(dataView));
1158     QVERIFY(dataView.isEmpty());
1159 }
1160 
1161 #include "moc_headertest.cpp"