File indexing completed on 2024-04-21 05:18:10
0001 /* 0002 SPDX-FileCopyrightText: 2006 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #undef QT_USE_FAST_CONCATENATION 0008 #undef QT_USE_FAST_OPERATOR_PLUS 0009 0010 #include <QTest> 0011 0012 #include "utiltest.h" 0013 0014 #include <kmime_charfreq.cpp> 0015 #include <kmime_util_p.cpp> 0016 #include "kmime_message.h" 0017 0018 using namespace KMime; 0019 0020 QTEST_MAIN(UtilTest) 0021 0022 void UtilTest::testUnfoldHeader() 0023 { 0024 // empty header 0025 QCOMPARE(KMime::unfoldHeader(""), QByteArray()); 0026 // identity 0027 QCOMPARE(KMime::unfoldHeader("bla"), QByteArray("bla")); 0028 // single folding 0029 QCOMPARE(KMime::unfoldHeader("bla\nblub"), QByteArray("bla blub")); 0030 QCOMPARE(KMime::unfoldHeader("bla\n \t blub"), QByteArray("bla blub")); 0031 QCOMPARE(KMime::unfoldHeader("bla \r\nblub"), QByteArray("bla blub")); 0032 // multiple folding 0033 QCOMPARE(KMime::unfoldHeader("bla\nbla\nblub"), QByteArray("bla bla blub")); 0034 QCOMPARE(KMime::unfoldHeader("bla \r\n bla \r\n blub"), QByteArray("bla bla blub")); 0035 QCOMPARE(KMime::unfoldHeader("bla\n"), QByteArray("bla")); 0036 // bug #86302 - malformed header continuation 0037 QCOMPARE(KMime::unfoldHeader("bla\n=20bla"), QByteArray("bla bla")); 0038 QCOMPARE(KMime::unfoldHeader("bla\n=09bla"), QByteArray("bla bla")); 0039 QCOMPARE(KMime::unfoldHeader("bla\r\n=20bla"), QByteArray("bla bla")); 0040 QCOMPARE(KMime::unfoldHeader("bla\r\n=09bla"), QByteArray("bla bla")); 0041 QCOMPARE(KMime::unfoldHeader("bla \n=20 bla"), QByteArray("bla bla")); 0042 QCOMPARE(KMime::unfoldHeader("bla \n=09 bla"), QByteArray("bla bla")); 0043 QCOMPARE(KMime::unfoldHeader("bla \n =20 bla"), QByteArray("bla =20 bla")); 0044 QCOMPARE(KMime::unfoldHeader("bla \n =09 bla"), QByteArray("bla =09 bla")); 0045 // malformed input resulting in leading linebreaks 0046 QCOMPARE(KMime::unfoldHeader("\n bla"), QByteArray("bla")); 0047 QCOMPARE(KMime::unfoldHeader("\r\n bla"), QByteArray("bla")); 0048 } 0049 0050 void UtilTest::testFoldHeader() 0051 { 0052 // no folding required 0053 // ------------------- 0054 0055 // <78 characters 0056 QCOMPARE(KMime::foldHeader("To: foo@bar"), QByteArray("To: foo@bar")); 0057 // exactly 78 characters 0058 QCOMPARE(KMime::foldHeader("To: fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaar"), 0059 QByteArray("To: fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaar")); 0060 0061 // folding required 0062 // ---------------- 0063 0064 // exactly 79 characters 0065 QCOMPARE(KMime::foldHeader("To: fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar"), 0066 QByteArray("To:\n fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar")); 0067 0068 // >78 characters 0069 QCOMPARE(KMime::foldHeader("To: some@where, fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar"), 0070 QByteArray("To: some@where,\n fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar")); 0071 0072 QCOMPARE(KMime::foldHeader("To: some@where, some@else, fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar"), 0073 QByteArray("To: some@where, some@else,\n fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar")); 0074 0075 QCOMPARE(KMime::foldHeader("To: Some Body <some@where>, fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar"), 0076 QByteArray("To: Some Body <some@where>,\n fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar")); 0077 0078 QCOMPARE(KMime::foldHeader("To: \"Some Body\" <some@where>, fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar"), 0079 QByteArray("To: \"Some Body\" <some@where>,\n fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar")); 0080 0081 QCOMPARE(KMime::foldHeader("To: Some Body <some@where>, John Doe <some@else>, fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar"), 0082 QByteArray("To: Some Body <some@where>, John Doe <some@else>,\n fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar")); 0083 0084 QCOMPARE(KMime::foldHeader("To: \"Some Body\" <some@where>, \"John Doe\" <some@else>, fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar"), 0085 QByteArray("To: \"Some Body\" <some@where>, \"John Doe\" <some@else>,\n fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaaaaaaaaaar")); 0086 0087 QCOMPARE(KMime::foldHeader("To: Some Body <some@where>, Aaaa Bbb <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>"), 0088 QByteArray("To: Some Body <some@where>,\n Aaaa Bbb <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>")); 0089 0090 QCOMPARE(KMime::foldHeader("To: \"Some Body\" <some@where>, \"Aaaa Bbb\" <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>"), 0091 QByteArray("To: \"Some Body\" <some@where>,\n \"Aaaa Bbb\" <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>")); 0092 0093 QCOMPARE(KMime::foldHeader("To: Some Body <some@where>, John Doe <some@else>, Aaaa Bbb <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>"), 0094 QByteArray("To: Some Body <some@where>, John Doe <some@else>,\n Aaaa Bbb <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>")); 0095 0096 QCOMPARE(KMime::foldHeader("To: \"Some Body\" <some@where>, \"John Doe\" <some@else>, \"Aaaa Bbb\" <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>"), 0097 QByteArray("To: \"Some Body\" <some@where>, \"John Doe\" <some@else>,\n \"Aaaa Bbb\" <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>")); 0098 0099 // avoid splitting at comma within quoted string (allowed but discouraged) 0100 QCOMPARE(KMime::foldHeader("To: \"Body, Some\" <some@where>, \"Doe, John\" <some@else>, \"Bbb, Aaaa\" <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>"), 0101 QByteArray("To: \"Body, Some\" <some@where>, \"Doe, John\" <some@else>,\n \"Bbb, Aaaa\" <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>")); 0102 0103 // ignore escaped double quote within quoted string 0104 QCOMPARE(KMime::foldHeader("To: \"\\\"Body, Some\" <some@where>, \"\\\"Doe, John\" <some@else>, \"\\\"Bbb, Aaaa\" <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>"), 0105 QByteArray("To: \"\\\"Body, Some\" <some@where>, \"\\\"Doe, John\" <some@else>,\n \"\\\"Bbb, Aaaa\" <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb>")); 0106 0107 // escaped backslashes, followed by escaped double quote within quoted string 0108 QCOMPARE(KMime::foldHeader("To: \"Body\\\\\\\\\\\", Some\\\\\\\\\" <some@where>, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb"), 0109 QByteArray("To: \"Body\\\\\\\\\\\", Some\\\\\\\\\" <some@where>,\n aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@bbbbb")); 0110 0111 // don't remove already inserted folding 0112 QCOMPARE(KMime::foldHeader("To: some@where,\n some@else"), 0113 QByteArray("To: some@where,\n some@else")); 0114 0115 // honor already inserted folding: no additional folding required 0116 QCOMPARE(KMime::foldHeader("To: some@where,\n some@else, fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaar"), 0117 QByteArray("To: some@where,\n some@else, fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaar")); 0118 0119 // honor already inserted folding: fold at correct position 0120 QCOMPARE(KMime::foldHeader("To: some@where,\n some@else, fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaar"), 0121 QByteArray("To: some@where,\n some@else,\n fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo@baaaaaar")); 0122 } 0123 0124 void UtilTest::testExtractHeader() 0125 { 0126 QByteArray header("To: <foo@bla.org>\n" 0127 "Subject: =?UTF-8?Q?_Notification_for_appointment:?=\n" 0128 " =?UTF-8?Q?_Test?=\n" 0129 "Continuation: =?UTF-8?Q?_TEST\n" 0130 "=20CONT1?= =?UTF-8?Q?_TEST\n" 0131 "=09CONT2?=\n" 0132 "MIME-Version: 1.0"); 0133 0134 // basic tests 0135 QVERIFY(extractHeader(header, "Foo").isEmpty()); 0136 QCOMPARE(extractHeader(header, "To"), QByteArray("<foo@bla.org>")); 0137 0138 // case insensitive matching 0139 QCOMPARE(extractHeader(header, "mime-version"), QByteArray("1.0")); 0140 0141 // extraction of multi-line headers 0142 QCOMPARE(extractHeader(header, "Subject"), 0143 QByteArray("=?UTF-8?Q?_Notification_for_appointment:?= =?UTF-8?Q?_Test?=")); 0144 0145 // bug #86302 - malformed header continuation 0146 QCOMPARE(extractHeader(header, "Continuation"), 0147 QByteArray("=?UTF-8?Q?_TEST CONT1?= =?UTF-8?Q?_TEST CONT2?=")); 0148 0149 // missing space after ':' 0150 QCOMPARE(extractHeader("From:<toma@kovoks.nl>", "From"), QByteArray("<toma@kovoks.nl>")); 0151 } 0152 0153 void UtilTest::testBalanceBidiState() 0154 { 0155 QFETCH(QString, input); 0156 QFETCH(QString, expResult); 0157 0158 QCOMPARE(balanceBidiState(input), expResult); 0159 } 0160 0161 void UtilTest::testBalanceBidiState_data() 0162 { 0163 QTest::addColumn<QString>("input"); 0164 QTest::addColumn<QString>("expResult"); 0165 0166 const QString LRO(QChar(0x202D)); 0167 const QString RLO(QChar(0x202E)); 0168 const QString LRE(QChar(0x202A)); 0169 const QString RLE(QChar(0x202B)); 0170 const QString PDF(QChar(0x202C)); 0171 0172 QTest::newRow("") << QString::fromLatin1("Normal") << QString::fromLatin1("Normal"); 0173 QTest::newRow("") << RLO + QString::fromLatin1("Balanced") + PDF << RLO + QString::fromLatin1("Balanced") + PDF; 0174 QTest::newRow("") << RLO + QString::fromLatin1("MissingPDF1") << RLO + QString::fromLatin1("MissingPDF1") + PDF; 0175 QTest::newRow("") << QString::fromLatin1("\"") + RLO + QString::fromLatin1("Quote\"") << QString::fromLatin1("\"") + RLO + QString::fromLatin1("Quote") + PDF + QString::fromLatin1("\""); 0176 QTest::newRow("") << QString::fromLatin1("MissingPDF2") + RLO << QString::fromLatin1("MissingPDF2") + RLO + PDF; 0177 QTest::newRow("") << RLO + QString::fromLatin1("MultipleRLO") + RLO << RLO + QString::fromLatin1("MultipleRLO") + RLO + PDF + PDF; 0178 QTest::newRow("") << LRO + QString::fromLatin1("Mixed") + LRE + RLE + RLO + QString::fromLatin1("Bla") 0179 << LRO + QString::fromLatin1("Mixed") + LRE + RLE + RLO + QString::fromLatin1("Bla") + PDF.repeated(4); 0180 QTest::newRow("") << RLO + QString::fromLatin1("TooManyPDF") + PDF + RLO + PDF + PDF 0181 << RLO + QString::fromLatin1("TooManyPDF") + PDF + RLO + PDF; 0182 QTest::newRow("") << PDF + QString::fromLatin1("WrongOrder") + RLO 0183 << QString::fromLatin1("WrongOrder") + RLO + PDF; 0184 QTest::newRow("") << QString::fromLatin1("ComplexOrder") + RLO + PDF + PDF + RLO 0185 << QString::fromLatin1("ComplexOrder") + RLO + PDF + RLO + PDF; 0186 QTest::newRow("") << QString::fromLatin1("ComplexOrder2") + RLO + PDF + PDF + PDF + RLO + PDF + PDF + PDF 0187 << QString::fromLatin1("ComplexOrder2") + RLO + PDF + RLO + PDF; 0188 QTest::newRow("") << PDF + PDF + PDF + QString::fromLatin1("ComplexOrder3") + PDF + PDF + RLO + PDF + PDF + PDF 0189 << QString::fromLatin1("ComplexOrder3") + RLO + PDF; 0190 } 0191 0192 void UtilTest::testAddQuotes() 0193 { 0194 QFETCH(QByteArray, input); 0195 QFETCH(QByteArray, expResult); 0196 QFETCH(bool, forceQuotes); 0197 0198 addQuotes(input, forceQuotes); 0199 QCOMPARE(input.data(), expResult.data()); 0200 } 0201 0202 void UtilTest::testAddQuotes_data() 0203 { 0204 QTest::addColumn<QByteArray>("input"); 0205 QTest::addColumn<QByteArray>("expResult"); 0206 QTest::addColumn<bool>("forceQuotes"); 0207 0208 QTest::newRow("") << QByteArray("Test") << QByteArray("Test") << false; 0209 QTest::newRow("") << QByteArray("Test") << QByteArray("\"Test\"") << true; 0210 QTest::newRow("") << QByteArray("Lastname, Firstname") 0211 << QByteArray("\"Lastname, Firstname\"") << false; 0212 QTest::newRow("") << QByteArray("John \"the hacker\" Smith") 0213 << QByteArray(R"("John \"the hacker\" Smith")") << false; 0214 0215 // Test the whole thing on strings as well, for one example 0216 QString string(QLatin1StringView("John \"the hacker\" Smith")); 0217 addQuotes(string, false); 0218 QCOMPARE(string, QString::fromLatin1("\"John \\\"the hacker\\\" Smith\"")); 0219 } 0220 0221 void UtilTest::testIsSigned_data() 0222 { 0223 QTest::addColumn<QByteArray>("input"); 0224 QTest::addColumn<bool>("hasSignature"); 0225 0226 QTest::newRow("pgp") << QByteArray( 0227 "From: xxx xxx <xxx@xxx.xxx>\n" 0228 "To: xxx xxx <xxx@xxx.xxx>\n" 0229 "Subject: Re: xxx\n" 0230 "Date: Mon, 13 Dec 2010 12:22:03 +0100\n" 0231 "MIME-Version: 1.0\n" 0232 "Content-Type: multipart/signed;\n" 0233 " boundary=\"nextPart1571960.gHxU0aGA9V\";\n" 0234 " protocol=\"application/pgp-signature\";\n" 0235 " micalg=pgp-sha1\n" 0236 "Content-Transfer-Encoding: 7bit\n\n" 0237 "--nextPart1571960.gHxU0aGA9V\n" 0238 "Content-Type: text/plain;\n" 0239 " charset=\"iso-8859-15\"\n" 0240 "Content-Transfer-Encoding: quoted-printable\n" 0241 "Content-Disposition: inline\n\n" 0242 "Hi there...\n\n" 0243 "--nextPart1571960.gHxU0aGA9V\n" 0244 "Content-Type: application/pgp-signature; name=signature.asc\n" 0245 "Content-Description: This is a digitally signed message part.\n\n" 0246 "-----BEGIN PGP SIGNATURE-----\n" 0247 "Version: GnuPG v2.0.15 (GNU/Linux)\n" 0248 "...\n" 0249 "-----END PGP SIGNATURE-----\n\n" 0250 "--nextPart1571960.gHxU0aGA9V--\n" 0251 ) << true; 0252 } 0253 0254 void UtilTest::testIsSigned() 0255 { 0256 QFETCH(QByteArray, input); 0257 QFETCH(bool, hasSignature); 0258 0259 KMime::Message::Ptr msg(new KMime::Message); 0260 msg->setContent(input); 0261 msg->parse(); 0262 QCOMPARE(isSigned(msg.data()), hasSignature); 0263 } 0264 0265 void UtilTest::testIsCryptoPart_data() 0266 { 0267 QTest::addColumn<QByteArray>("mimeType"); 0268 QTest::addColumn<QString>("fileName"); 0269 QTest::addColumn<bool>("isCrypto"); 0270 0271 QTest::newRow("pgp-encrypted") << QByteArray("application/pgp-encrypted") << QString() << true; 0272 QTest::newRow("pgp-encrypted upper case") << QByteArray("APPLICATION/PGP-ENCRYPTED") << QString() << true; 0273 QTest::newRow("pgp-signature") << QByteArray("application/pgp-signature") << QString() << true; 0274 QTest::newRow("pkcs7-mime") << QByteArray("application/pkcs7-mime") << QString() << true; 0275 QTest::newRow("pkcs7-signature") << QByteArray("application/pkcs7-signature") << QString() << true; 0276 QTest::newRow("x-pkcs7-signature") << QByteArray("application/x-pkcs7-signature") << QString() << true; 0277 QTest::newRow("msg.asc") << QByteArray("application/octet-stream") << QStringLiteral("msg.asc") << true; 0278 QTest::newRow("msg.asc upper case") << QByteArray("application/octet-stream") << QStringLiteral("MSG.ASC") << true; 0279 QTest::newRow("encrypted.asc") << QByteArray("application/octet-stream") << QStringLiteral("encrypted.asc") << true; 0280 QTest::newRow("octet-stream") << QByteArray("application/octet-stream") << QStringLiteral("bla.foo") << false; 0281 QTest::newRow("wrong mimetype") << QByteArray("application/foo") << QString() << false; 0282 QTest::newRow("text") << QByteArray("text/plain") << QString() << false; 0283 QTest::newRow("encrypted.asc wrong type") << QByteArray("application/foo") << QStringLiteral("encrypted.asc") << false; 0284 QTest::newRow("msc.asc wrong type") << QByteArray("application/foo") << QStringLiteral("msc.asc") << false; 0285 } 0286 0287 void UtilTest::testIsCryptoPart() 0288 { 0289 QFETCH(QByteArray, mimeType); 0290 QFETCH(QString, fileName); 0291 QFETCH(bool, isCrypto); 0292 0293 KMime::Content c; 0294 c.contentType()->setMimeType(mimeType); 0295 c.contentDisposition()->setFilename(fileName); 0296 0297 QCOMPARE(KMime::isCryptoPart(&c), isCrypto); 0298 } 0299 0300 void UtilTest::testLFCRLF_data() 0301 { 0302 QTest::addColumn<QByteArray>("input"); 0303 QTest::addColumn<QByteArray>("expected"); 0304 QTest::addColumn<QByteArray>("convertedBack"); 0305 0306 const QByteArray noNewline("no newline character"); 0307 QTest::newRow("none") << noNewline << noNewline << noNewline; 0308 QTest::newRow("alone") << QByteArray("\n") << QByteArray("\r\n") << QByteArray("\n"); 0309 QTest::newRow("CRLF") << QByteArray("\r\n") << QByteArray("\r\n") << QByteArray("\n"); 0310 QTest::newRow("single_first") << QByteArray("\nfoo") << QByteArray("\r\nfoo") << QByteArray("\nfoo"); 0311 QTest::newRow("single_last") << QByteArray("foo\n") << QByteArray("foo\r\n") << QByteArray("foo\n"); 0312 QTest::newRow("single_two_lines") << QByteArray("foo\nbar") << QByteArray("foo\r\nbar") << QByteArray("foo\nbar"); 0313 QTest::newRow("two_lines") << QByteArray("foo\nbar\n") << QByteArray("foo\r\nbar\r\n") << QByteArray("foo\nbar\n"); 0314 QTest::newRow("already_CRLF") << QByteArray("foo\r\nbar\r\n") << QByteArray("foo\r\nbar\r\n") << QByteArray("foo\nbar\n"); 0315 QTest::newRow("mixed_CRLF_LF_unchanged") << QByteArray("foo\r\nbar\n") << QByteArray("foo\r\nbar\n") << QByteArray("foo\nbar\n"); 0316 // out of scope QTest::newRow("mixed_LF_CRLF_unchanged") << QByteArray("foo\nbar\r\n") << QByteArray("foo\nbar\r\n") << QByteArray("foo\nbar\n"); 0317 } 0318 0319 void UtilTest::testLFCRLF() 0320 { 0321 QFETCH(QByteArray, input); 0322 QFETCH(QByteArray, expected); 0323 QFETCH(QByteArray, convertedBack); 0324 0325 const QByteArray output = KMime::LFtoCRLF(input); 0326 QCOMPARE(output, expected); 0327 const QByteArray output2 = KMime::LFtoCRLF(input.constData()); // test the const char* overload 0328 QCOMPARE(output2, expected); 0329 0330 const QByteArray back = KMime::CRLFtoLF(output); 0331 QCOMPARE(back, convertedBack); 0332 } 0333 0334 void UtilTest::testLFCRLF_performance() 0335 { 0336 const QByteArray line = "This is one line\n"; 0337 const int count = 1000; 0338 QByteArray input; 0339 input.reserve(line.size() * count); 0340 for (int i = 0 ; i < count; ++i) { 0341 input += line; 0342 } 0343 0344 QByteArray output; 0345 QBENCHMARK { 0346 output = KMime::LFtoCRLF(input); 0347 } 0348 QByteArray expected = input; 0349 expected.replace('\n', "\r\n"); 0350 QCOMPARE(output, expected); 0351 } 0352 0353 #include "moc_utiltest.cpp"