File indexing completed on 2024-04-21 05:18:09

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