diff options
author | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
---|---|---|
committer | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
commit | aa4d426b4d3527d7e166df1a05058c9a4a0f6683 (patch) | |
tree | 4ff17ce8b89a2321b9d0ed4bcfc37c447bcb6820 /vmime-master/tests/parser | |
download | smtps-and-pop3s-console-program-master.tar.gz smtps-and-pop3s-console-program-master.zip |
Diffstat (limited to 'vmime-master/tests/parser')
27 files changed, 5845 insertions, 0 deletions
diff --git a/vmime-master/tests/parser/attachmentHelperTest.cpp b/vmime-master/tests/parser/attachmentHelperTest.cpp new file mode 100644 index 0000000..866f8de --- /dev/null +++ b/vmime-master/tests/parser/attachmentHelperTest.cpp @@ -0,0 +1,335 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(attachmentHelperTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testAddAttachment1) + VMIME_TEST(testAddAttachment2) + VMIME_TEST(testAddAttachment3) + VMIME_TEST(testIsBodyPartAnAttachment1) + VMIME_TEST(testIsBodyPartAnAttachment2) + VMIME_TEST(testIsBodyPartAnAttachment3) + VMIME_TEST(testGetBodyPartAttachment) + VMIME_TEST(testAddAttachmentMessage1) + VMIME_TEST(testGetBodyPartAttachmentMessage) + VMIME_TEST_LIST_END + + + static const vmime::string getStructure(const vmime::shared_ptr <vmime::bodyPart>& part) { + + vmime::shared_ptr <vmime::body> bdy = part->getBody(); + + vmime::string res = part->getBody()->getContentType().generate(); + + if (bdy->getPartCount() == 0) { + return res; + } + + res += "["; + + for (size_t i = 0 ; i < bdy->getPartCount() ; ++i) { + + vmime::shared_ptr <vmime::bodyPart> subPart = bdy->getPartAt(i); + + if (i != 0) { + res += ","; + } + + res += getStructure(subPart); + } + + return res + "]"; + } + + static const vmime::string extractBodyContents( + const vmime::shared_ptr <const vmime::bodyPart>& part + ) { + + vmime::shared_ptr <const vmime::contentHandler> cth = part->getBody()->getContents(); + + vmime::string data; + vmime::utility::outputStreamStringAdapter os(data); + + cth->extract(os); + + return data; + } + + void testAddAttachment1() { + + vmime::string data = +"Content-Type: text/plain\r\n" +"\r\n" +"The text\r\n" +""; + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->parse(data); + + vmime::shared_ptr <vmime::attachment> att = + vmime::make_shared <vmime::defaultAttachment>( + vmime::make_shared <vmime::stringContentHandler>("test"), + vmime::mediaType("image/jpeg") + ); + + vmime::attachmentHelper::addAttachment(msg, att); + + VASSERT_EQ("1", "multipart/mixed[text/plain,image/jpeg]", getStructure(msg)); + VASSERT_EQ("2", "The text\r\n", extractBodyContents(msg->getBody()->getPartAt(0))); + } + + void testAddAttachment2() { + + vmime::string data = +"Content-Type: multipart/mixed; boundary=\"foo\"\r\n" +"\r\n" +"--foo\r\n" +"Content-Type: text/plain\r\n" +"\r\n" +"The text\r\n" +"--foo\r\n" +"Content-Type: application/octet-stream\r\n" +"\r\n" +"Blah\r\n" +"--foo--\r\n" +""; + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->parse(data); + + vmime::shared_ptr <vmime::attachment> att = + vmime::make_shared <vmime::defaultAttachment>( + vmime::make_shared <vmime::stringContentHandler>("test"), + vmime::mediaType("image/jpeg") + ); + + vmime::attachmentHelper::addAttachment(msg, att); + + VASSERT_EQ("1", "multipart/mixed[text/plain,application/octet-stream,image/jpeg]", getStructure(msg)); + VASSERT_EQ("2", "The text", extractBodyContents(msg->getBody()->getPartAt(0))); + VASSERT_EQ("3", "Blah", extractBodyContents(msg->getBody()->getPartAt(1))); + VASSERT_EQ("4", "test", extractBodyContents(msg->getBody()->getPartAt(2))); + } + + // Initial part is encoded + void testAddAttachment3() { + + vmime::string data = +"Content-Type: text/plain\r\n" +"Content-Transfer-Encoding: base64\r\n" +"\r\n" +"TWVzc2FnZSBib2R5"; + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->parse(data); + + vmime::shared_ptr <vmime::attachment> att = + vmime::make_shared <vmime::defaultAttachment>( + vmime::make_shared <vmime::stringContentHandler>("test"), + vmime::mediaType("image/jpeg") + ); + + vmime::attachmentHelper::addAttachment(msg, att); + + VASSERT_EQ("1", "multipart/mixed[text/plain,image/jpeg]", getStructure(msg)); + VASSERT_EQ("2", "Message body", extractBodyContents(msg->getBody()->getPartAt(0))); + } + + // Content-Disposition: attachment + // No other field + void testIsBodyPartAnAttachment1() { + + vmime::string data = "Content-Disposition: attachment\r\n\r\nFoo\r\n"; + + vmime::shared_ptr <vmime::bodyPart> p = vmime::make_shared <vmime::bodyPart>(); + p->parse(data); + + VASSERT_EQ("1", true, vmime::attachmentHelper::isBodyPartAnAttachment(p)); + } + + // No Content-Disposition field + // Content-Type: multipart/* or text/* + void testIsBodyPartAnAttachment2() { + + vmime::string data = "Content-Type: multipart/*\r\n\r\nFoo\r\n"; + + vmime::shared_ptr <vmime::bodyPart> p = vmime::make_shared <vmime::bodyPart>(); + p->parse(data); + + VASSERT_EQ("1", false, vmime::attachmentHelper::isBodyPartAnAttachment(p)); + + data = "Content-Type: text/*\r\n\r\nFoo\r\n"; + + p->parse(data); + + VASSERT_EQ("2", false, vmime::attachmentHelper::isBodyPartAnAttachment(p)); + } + + // No Content-Disposition field + void testIsBodyPartAnAttachment3() { + + vmime::string data = "Content-Type: application/octet-stream\r\n\r\nFoo\r\n"; + + vmime::shared_ptr <vmime::bodyPart> p = vmime::make_shared <vmime::bodyPart>(); + p->parse(data); + + VASSERT_EQ("1", true, vmime::attachmentHelper::isBodyPartAnAttachment(p)); + } + + // Content-Disposition: attachment + // Content-Id field present + void testIsBodyPartAnAttachment4() { + + vmime::string data = "Content-Disposition: attachment\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Id: bar\r\n" + "\r\nFoo\r\n"; + + vmime::shared_ptr <vmime::bodyPart> p = vmime::make_shared <vmime::bodyPart>(); + p->parse(data); + + VASSERT_EQ("1", false, vmime::attachmentHelper::isBodyPartAnAttachment(p)); + } + + void testGetBodyPartAttachment() { + + vmime::string data = + "Content-Type: image/jpeg\r\n" + "Content-Description: foobar\r\n" + "Content-Transfer-Encoding: x-baz\r\n" + "Content-Disposition: attachment; filename=\"foobar.baz\"\r\n" + "\r\n" + "Foo bar baz"; + + vmime::shared_ptr <vmime::bodyPart> part = vmime::make_shared <vmime::bodyPart>(); + part->parse(data); + + vmime::shared_ptr <const vmime::attachment> att = + vmime::attachmentHelper::getBodyPartAttachment(part); + + VASSERT_EQ("1", "image/jpeg", att->getType().generate()); + VASSERT_EQ("2", "foobar", att->getDescription().generate()); + VASSERT_EQ("3", "x-baz", att->getEncoding().generate()); + VASSERT_EQ("4", "foobar.baz", att->getName().generate()); + + vmime::string attData; + vmime::utility::outputStreamStringAdapter out(attData); + att->getData()->extractRaw(out); // 'x-baz' encoding not supported + + VASSERT_EQ("5", "Foo bar baz", attData); + + //VASSERT_EQ("6", part, att->getPart()); + VASSERT_EQ("6", part->generate(), vmime::dynamicCast <const vmime::component>(att->getPart())->generate()); + //VASSERT_EQ("7", part->getHeader(), att->getHeader()); + VASSERT_EQ("7", part->getHeader()->generate(), att->getHeader()->generate()); + } + + void testAddAttachmentMessage1() { + + const vmime::string data = +"Subject: Test message\r\n" +"Content-Type: text/plain\r\n" +"\r\n" +"Message body"; + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->parse(data); + + const vmime::string attData = +"Subject: Attached message\r\n" +"Content-Type: text/plain\r\n" +"Content-Transfer-Encoding: base64\r\n" +"\r\n" +"QXR0YWNoZWQgbWVzc2FnZSBib2R5"; + + vmime::shared_ptr <vmime::message> amsg = vmime::make_shared <vmime::message>(); + amsg->parse(attData); + + vmime::attachmentHelper::addAttachment(msg, amsg); + + VASSERT_EQ("1", "multipart/mixed[text/plain,message/rfc822]", getStructure(msg)); + VASSERT_EQ("2", "Message body", extractBodyContents(msg->getBody()->getPartAt(0))); + + // Ensure message has been encoded properly + vmime::shared_ptr <const vmime::bodyPart> attPart = msg->getBody()->getPartAt(1); + vmime::shared_ptr <const vmime::contentHandler> attCth = attPart->getBody()->getContents(); + + vmime::string attDataOut; + vmime::utility::outputStreamStringAdapter attDataOutOs(attDataOut); + + attCth->extract(attDataOutOs); + + vmime::shared_ptr <vmime::message> amsgOut = vmime::make_shared <vmime::message>(); + amsgOut->parse(attDataOut); + + vmime::shared_ptr <vmime::header> hdr = amsgOut->getHeader(); + + VASSERT_EQ("3", "Attached message", hdr->Subject()->getValue <vmime::text>()->generate()); + VASSERT_EQ("4", "Attached message body", extractBodyContents(amsgOut)); + } + + void testGetBodyPartAttachmentMessage() { + + const vmime::string data = +"Subject: Test message\r\n" +"Content-Type: multipart/mixed; boundary=\"foo\"\r\n" +"\r\n" +"--foo\r\n" +"Content-Type: message/rfc822\r\n" +"\r\n" +"Subject: Attached message\r\n" +"\r\n" +"Attached message body\r\n" +"--foo\r\n" +"Content-Type: text/plain\r\n" +"\r\n" +"FooBar\r\n" +"--foo--\r\n"; + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->parse(data); + + VASSERT_EQ("0", 2, msg->getBody()->getPartCount()); + + vmime::shared_ptr <const vmime::attachment> att = + vmime::attachmentHelper::getBodyPartAttachment(msg->getBody()->getPartAt(0)); + + VASSERT("1", att != NULL); + + vmime::shared_ptr <const vmime::messageAttachment> msgAtt = + vmime::dynamicCast <const vmime::messageAttachment>(att); + + VASSERT("2", msgAtt != NULL); + + vmime::shared_ptr <vmime::message> amsg = msgAtt->getMessage(); + vmime::shared_ptr <vmime::header> hdr = amsg->getHeader(); + + VASSERT_EQ("3", "Attached message", hdr->Subject()->getValue <vmime::text>()->generate()); + VASSERT_EQ("4", "Attached message body", extractBodyContents(amsg)); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/bodyPartTest.cpp b/vmime-master/tests/parser/bodyPartTest.cpp new file mode 100644 index 0000000..3aaadd0 --- /dev/null +++ b/vmime-master/tests/parser/bodyPartTest.cpp @@ -0,0 +1,414 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(bodyPartTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParse) + VMIME_TEST(testGenerate) + VMIME_TEST(testParseGuessBoundary) + VMIME_TEST(testParseGuessBoundaryWithTransportPadding) + VMIME_TEST(testParseMissingLastBoundary) + VMIME_TEST(testPrologEpilog) + VMIME_TEST(testPrologEncoding) + VMIME_TEST(testSuccessiveBoundaries) + VMIME_TEST(testTransportPaddingInBoundary) + VMIME_TEST(testGenerate7bit) + VMIME_TEST(testTextUsageForQPEncoding) + VMIME_TEST(testParseVeryBigMessage) + VMIME_TEST(testParseBoundaryPrefix) + VMIME_TEST_LIST_END + + + static const vmime::string extractComponentString( + const vmime::string& buffer, + const vmime::component& c + ) { + + return vmime::string( + buffer.begin() + c.getParsedOffset(), + buffer.begin() + c.getParsedOffset() + c.getParsedLength() + ); + } + + static const vmime::string extractContents( + const vmime::shared_ptr <const vmime::contentHandler>& cts + ) { + + std::ostringstream oss; + vmime::utility::outputStreamAdapter os(oss); + + cts->extract(os); + + return oss.str(); + } + + + void testParse() { + + vmime::string str1 = "HEADER\r\n\r\nBODY"; + vmime::bodyPart p1; + p1.parse(str1); + + VASSERT_EQ("1", "HEADER\r\n\r\n", extractComponentString(str1, *p1.getHeader())); + VASSERT_EQ("2", "BODY", extractComponentString(str1, *p1.getBody())); + + vmime::string str2 = "HEADER\n\nBODY"; + vmime::bodyPart p2; + p2.parse(str2); + + VASSERT_EQ("3", "HEADER\n\n", extractComponentString(str2, *p2.getHeader())); + VASSERT_EQ("4", "BODY", extractComponentString(str2, *p2.getBody())); + + vmime::string str3 = "HEADER\r\n\nBODY"; + vmime::bodyPart p3; + p3.parse(str3); + + VASSERT_EQ("5", "HEADER\r\n\n", extractComponentString(str3, *p3.getHeader())); + VASSERT_EQ("6", "BODY", extractComponentString(str3, *p3.getBody())); + } + + void testParseMissingLastBoundary() { + + vmime::string str = + "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\"" + "\r\n\r\n" + "--MY-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n" + "--MY-BOUNDARY\r\nHEADER2\r\n\r\nBODY2"; + + vmime::bodyPart p; + p.parse(str); + + VASSERT_EQ("count", 2, p.getBody()->getPartCount()); + + VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents())); + VASSERT_EQ("part2-body", "BODY2", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents())); + } + + void testGenerate() { + + vmime::bodyPart p1; + p1.getHeader()->getField("Foo")->setValue(vmime::string("bar")); + p1.getBody()->setContents(vmime::make_shared <vmime::stringContentHandler>("Baz")); + + VASSERT_EQ("1", "Foo: bar\r\n\r\nBaz", p1.generate()); + } + + void testPrologEpilog() { + + const char testMail[] = + "To: test@vmime.org\r\n" + "From: test@vmime.org\r\n" + "Subject: Prolog and epilog test\r\n" + "Content-Type: multipart/mixed; \r\n" + " boundary=\"=_boundary\"\r\n" + "\r\n" + "Prolog text\r\n" + "--=_boundary\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "Part1\r\n" + "--=_boundary--\r\n" + "Epilog text"; + + vmime::bodyPart part; + part.parse(testMail); + + VASSERT_EQ("prolog", "Prolog text", part.getBody()->getPrologText()); + VASSERT_EQ("epilog", "Epilog text", part.getBody()->getEpilogText()); + } + + // Test for bug fix: prolog should not be encoded + // http://sourceforge.net/tracker/?func=detail&atid=525568&aid=3174903&group_id=69724 + void testPrologEncoding() { + + const char testmail[] = + "To: test@vmime.org\r\n" + "From: test@vmime.org\r\n" + "Subject: Prolog encoding test\r\n" + "Content-Type: multipart/mixed; \r\n" + " boundary=\"=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q\"\r\n" + "\r\n" + "This is a multi-part message in MIME format. Your mail reader does not\r\n" + "understand MIME message format.\r\n" + "--=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q\r\n" + "Content-Type: text/html; charset=windows-1251\r\n" + "Content-Transfer-Encoding: quoted-printable\r\n" + "\r\n" + "=DD=F2=EE =F2=E5=EA=F1=F2=EE=E2=E0=FF =F7=E0=F1=F2=FC =F1=EB=EE=E6=ED=EE=E3=\r\n" + "=EE =F1=EE=EE=E1=F9=E5=ED=E8=FF\r\n" + "--=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q\r\n" + "Content-Type: application/octet-stream; charset=windows-1251\r\n" + "Content-Disposition: attachment; filename=FNS.zip\r\n" + "Content-Transfer-Encoding: base64\r\n" + "\r\n" + "UEsDBB...snap...EEAAAAAA==\r\n" + "--=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q--\r\n" + "Epilog text"; + + vmime::shared_ptr<vmime::message> msg = vmime::make_shared<vmime::message>(); + + std::string istr(testmail); + + std::string ostr; + vmime::utility::outputStreamStringAdapter out(ostr); + + for (int i = 0 ; i < 10 ; ++i) { + + ostr.clear(); + + msg->parse(istr); + msg->generate(out); + + istr = ostr; + } + + VASSERT_EQ("prolog", "This is a multi-part message in MIME format. Your mail reader" + " does not understand MIME message format.", msg->getBody()->getPrologText()); + VASSERT_EQ("epilog", "Epilog text", msg->getBody()->getEpilogText()); + } + + void testSuccessiveBoundaries() { + + vmime::string str = + "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\"" + "\r\n\r\n" + "--MY-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n" + "--MY-BOUNDARY\r\n" + "--MY-BOUNDARY--\r\n"; + + vmime::bodyPart p; + p.parse(str); + + VASSERT_EQ("count", 2, p.getBody()->getPartCount()); + + VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents())); + VASSERT_EQ("part2-body", "", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents())); + } + + void testTransportPaddingInBoundary() { + + vmime::string str = + "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\"" + "\r\n\r\n" + "--MY-BOUNDARY \t \r\nHEADER1\r\n\r\nBODY1\r\n" + "--MY-BOUNDARY\r\n" + "--MY-BOUNDARY-- \r\n"; + + vmime::bodyPart p; + p.parse(str); + + VASSERT_EQ("count", 2, p.getBody()->getPartCount()); + + VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents())); + VASSERT_EQ("part2-body", "", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents())); + } + + /** Ensure '7bit' encoding is used when body is 7-bit only. */ + void testGenerate7bit() { + + vmime::shared_ptr <vmime::plainTextPart> p1 = vmime::make_shared <vmime::plainTextPart>(); + p1->setText(vmime::make_shared <vmime::stringContentHandler>("Part1 is US-ASCII only.")); + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + p1->generateIn(msg, msg); + + vmime::shared_ptr <vmime::header> header1 = msg->getBody()->getPartAt(0)->getHeader(); + VASSERT_EQ("1", "7bit", header1->ContentTransferEncoding()->getValue()->generate()); + } + + void testTextUsageForQPEncoding() { + + vmime::shared_ptr <vmime::plainTextPart> part = vmime::make_shared <vmime::plainTextPart>(); + part->setText(vmime::make_shared <vmime::stringContentHandler>("Part1-line1\r\nPart1-line2\r\n\x89")); + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + part->generateIn(msg, msg); + + vmime::shared_ptr <vmime::body> body = msg->getBody()->getPartAt(0)->getBody(); + vmime::shared_ptr <vmime::header> header = msg->getBody()->getPartAt(0)->getHeader(); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter os(oss); + body->generate(os, 80); + + VASSERT_EQ("1", "quoted-printable", header->ContentTransferEncoding()->getValue()->generate()); + + // This should *NOT* be: + // Part1-line1=0D=0APart1-line2=0D=0A=89 + VASSERT_EQ("2", "Part1-line1\r\nPart1-line2\r\n=89", oss.str()); + } + + void testParseGuessBoundary() { + + // Boundary is not specified in "Content-Type" field + // Parser will try to guess it from message contents. + + vmime::string str = + "Content-Type: multipart/mixed" + "\r\n\r\n" + "--UNKNOWN-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n" + "--UNKNOWN-BOUNDARY\r\nHEADER2\r\n\r\nBODY2\r\n" + "--UNKNOWN-BOUNDARY--"; + + vmime::bodyPart p; + p.parse(str); + + VASSERT_EQ("count", 2, p.getBody()->getPartCount()); + + VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents())); + VASSERT_EQ("part2-body", "BODY2", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents())); + } + + void testParseGuessBoundaryWithTransportPadding() { + + // Boundary is not specified in "Content-Type" field + // Parser will try to guess it from message contents. + // Transport padding white spaces should be ignored. + + vmime::string str = + "Content-Type: multipart/mixed" + "\r\n\r\n" + "--UNKNOWN-BOUNDARY \t \r\nHEADER1\r\n\r\nBODY1\r\n" + "--UNKNOWN-BOUNDARY\r\nHEADER2\r\n\r\nBODY2\r\n" + "--UNKNOWN-BOUNDARY--"; + + vmime::bodyPart p; + p.parse(str); + + VASSERT_EQ("count", 2, p.getBody()->getPartCount()); + + VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents())); + VASSERT_EQ("part2-body", "BODY2", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents())); + } + + void testParseVeryBigMessage() { + + // When parsing from a seekable input stream, body contents should not + // be kept in memory in a "stringContentHandler" object. Instead, content + // should be accessible via a "streamContentHandler" object. + + static const std::string BODY1_BEGIN = "BEGIN1BEGIN1BEGIN1"; + static const std::string BODY1_LINE = "BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1"; + static const std::string BODY1_END = "END1END1"; + static const unsigned int BODY1_REPEAT = 35000; + static const size_t BODY1_LENGTH = + BODY1_BEGIN.length() + BODY1_LINE.length() * BODY1_REPEAT + BODY1_END.length(); + + static const std::string BODY2_LINE = "BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2"; + static const unsigned int BODY2_REPEAT = 20000; + + std::ostringstream oss; + oss << "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\"" + << "\r\n\r\n" + << "--MY-BOUNDARY\r\n" + << "HEADER1\r\n" + << "\r\n"; + + oss << BODY1_BEGIN; + + for (unsigned int i = 0 ; i < BODY1_REPEAT ; ++i) { + oss << BODY1_LINE; + } + + oss << BODY1_END; + + oss << "\r\n" + << "--MY-BOUNDARY\r\n" + << "HEADER2\r\n" + << "\r\n"; + + for (unsigned int i = 0 ; i < BODY2_REPEAT ; ++i) { + oss << BODY2_LINE; + } + + oss << "\r\n" + << "--MY-BOUNDARY--\r\n"; + + vmime::shared_ptr <vmime::utility::inputStreamStringAdapter> is = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(oss.str()); + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->parse(is, oss.str().length()); + + vmime::shared_ptr <vmime::body> body1 = msg->getBody()->getPartAt(0)->getBody(); + vmime::shared_ptr <const vmime::contentHandler> body1Cts = body1->getContents(); + + vmime::shared_ptr <vmime::body> body2 = msg->getBody()->getPartAt(1)->getBody(); + vmime::shared_ptr <const vmime::contentHandler> body2Cts = body2->getContents(); + + vmime::string body1CtsExtracted; + vmime::utility::outputStreamStringAdapter body1CtsExtractStream(body1CtsExtracted); + body1Cts->extract(body1CtsExtractStream); + + VASSERT_EQ("1.1", BODY1_LENGTH, body1Cts->getLength()); + VASSERT("1.2", vmime::dynamicCast <const vmime::streamContentHandler>(body1Cts) != NULL); + VASSERT_EQ("1.3", BODY1_LENGTH, body1CtsExtracted.length()); + VASSERT_EQ("1.4", BODY1_BEGIN, body1CtsExtracted.substr(0, BODY1_BEGIN.length())); + VASSERT_EQ("1.5", BODY1_END, body1CtsExtracted.substr(BODY1_LENGTH - BODY1_END.length(), BODY1_END.length())); + + VASSERT_EQ("2.1", BODY2_LINE.length() * BODY2_REPEAT, body2Cts->getLength()); + VASSERT("2.2", vmime::dynamicCast <const vmime::streamContentHandler>(body2Cts) != NULL); + } + + void testParseBoundaryPrefix() { + /* + * Clients are not supposed to create boundary identifiers that + * contain a prefix of another (RFC 2046 section 5.1), but alas + * CANCOM FortiMail produces this garbage. + */ + vmime::string str = + "Content-Type: multipart/related; boundary=\"--b12\"\r\n" + "\r\n" + "----b12\r\n" + "Content-Type: multipart/alternative; boundary=\"--b12-1\"\r\n" + "\r\n" + "----b12-1\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" + "\r\n" + "P11\r\n" + "----b12-1\r\n" + "Content-Type: text/html; charset=utf-8\r\n" + "\r\n" + "P12\r\n" + "----b12-1--\r\n" + "----b12\r\n" + "\r\n" + "P2\r\n" + "----b12--\r\n"; + + vmime::bodyPart relco; + relco.parse(str); + auto relbd = relco.getBody(); + VASSERT_EQ("global-partcount", 2, relbd->getPartCount()); + auto altbd = relbd->getPartAt(0)->getBody(); + VASSERT_EQ("part1-partcount", 2, altbd->getPartCount()); + VASSERT_EQ("part1.1-body", "P11", extractContents(altbd->getPartAt(0)->getBody()->getContents())); + VASSERT_EQ("part1.2-body", "P12", extractContents(altbd->getPartAt(1)->getBody()->getContents())); + VASSERT_EQ("part2-body", "P2", extractContents(relbd->getPartAt(1)->getBody()->getContents())); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/bodyTest.cpp b/vmime-master/tests/parser/bodyTest.cpp new file mode 100644 index 0000000..31054f3 --- /dev/null +++ b/vmime-master/tests/parser/bodyTest.cpp @@ -0,0 +1,79 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(bodyTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testGenerate_Text) + VMIME_TEST(testGenerate_NonText) + VMIME_TEST_LIST_END + + void testGenerate_Text() { + + // RFC-2015: [Quoted-Printable encoding] A line break in a text body, + // represented as a CRLF sequence in the text canonical form, must be + // represented by a line break which is also a CRLF sequence, in the + // Quoted-Printable encoding + + vmime::bodyPart p; + p.getBody()->setContents( + vmime::make_shared <vmime::stringContentHandler>( + "Foo éé\r\né bar\r\nbaz" + ), + vmime::mediaType("text", "plain"), + vmime::charset("utf-8"), + vmime::encoding("quoted-printable") + ); + + VASSERT_EQ( + "generate", + "Foo =C3=A9=C3=A9\r\n" + "=C3=A9 bar\r\n" + "baz", + p.getBody()->generate() + ); + } + + void testGenerate_NonText() { + + vmime::bodyPart p; + p.getBody()->setContents( + vmime::make_shared <vmime::stringContentHandler>( + "Binary\xfa\xfb\r\ndata\r\n\r\n\xfc" + ), + vmime::mediaType("application", "octet-stream"), + vmime::charset("utf-8"), + vmime::encoding("quoted-printable") + ); + + VASSERT_EQ( + "generate", + "Binary=FA=FB=0D=0Adata=0D=0A=0D=0A=FC", + p.getBody()->generate() + ); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/charsetFilteredOutputStreamTest.cpp b/vmime-master/tests/parser/charsetFilteredOutputStreamTest.cpp new file mode 100644 index 0000000..0ebd83a --- /dev/null +++ b/vmime-master/tests/parser/charsetFilteredOutputStreamTest.cpp @@ -0,0 +1,213 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include "vmime/charset.hpp" +#include "vmime/charsetConverter.hpp" + +#include "charsetTestSuites.hpp" + + +VMIME_TEST_SUITE_BEGIN(charsetFilteredOutputStreamTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testInputBufferUnderflow) + VMIME_TEST(testInvalidInput1) + VMIME_TEST(testStreamCopy) + VMIME_TEST(testOneByteAtTime) + VMIME_TEST(testVariableInputChunk) + VMIME_TEST_LIST_END + + + void testInputBufferUnderflow() { + + vmime::shared_ptr <vmime::charsetConverter> cc = + vmime::charsetConverter::create("utf-8", "iso-8859-1"); + + vmime::string output; + vmime::utility::outputStreamStringAdapter os(output); + vmime::shared_ptr <vmime::utility::filteredOutputStream> cfos = cc->getFilteredOutputStream(os); + + VASSERT_NOT_NULL("filteredOutputStream availability", cfos); + + // føo = 66 c3 b8 6f [UTF8] + // føo = 66 f8 6f [latin1] + + cfos->write("\x66\xc3", 2); + + // Incomplete UTF-8 sequence was not converted + VASSERT_EQ("chunk 1", toHex("f"), toHex(output)); + + // Write second byte of UTF-8 sequence + cfos->write("\xb8\x6f", 2); + + VASSERT_EQ("chunk 2", toHex("f\xf8o"), toHex(output)); + } + + void testInvalidInput1() { + + vmime::string in("foo\xab\xcd\xef bar"); + vmime::string expectedOut("foo??? bar"); + + vmime::string actualOut; + vmime::utility::outputStreamStringAdapter osa(actualOut); + + vmime::shared_ptr <vmime::charsetConverter> conv = + vmime::charsetConverter::create(vmime::charset("utf-8"), vmime::charset("iso-8859-1")); + + vmime::shared_ptr <vmime::utility::charsetFilteredOutputStream> os = + conv->getFilteredOutputStream(osa); + + VASSERT_NOT_NULL("filteredOutputStream availability", os); + + vmime::utility::inputStreamStringAdapter is(in); + + vmime::byte_t buffer[16]; + + for (int i = 0 ; !is.eof() ; ++i) + os->write(buffer, is.read(buffer, 1)); + + os->flush(); + + VASSERT_EQ("1", toHex(expectedOut), toHex(actualOut)); + } + + // Using 'bufferedStreamCopy' + void testStreamCopy() { + + for (unsigned int i = 0 ; i < charsetTestSuitesCount ; ++i) { + + const charsetTestSuiteStruct& entry = charsetTestSuites[i]; + + std::ostringstream testName; + testName << i << ": " << entry.fromCharset << " -> " << entry.toCharset; + + const unsigned long inLength = (entry.fromLength == 0 ? strlen(entry.fromBytes) : entry.fromLength); + vmime::string in(entry.fromBytes, entry.fromBytes + inLength); + + const unsigned long outLength = (entry.toLength == 0 ? strlen(entry.toBytes) : entry.toLength); + vmime::string expectedOut(entry.toBytes, entry.toBytes + outLength); + + vmime::string actualOut; + vmime::utility::outputStreamStringAdapter osa(actualOut); + + vmime::shared_ptr <vmime::charsetConverter> conv = + vmime::charsetConverter::create(entry.fromCharset, entry.toCharset); + + vmime::shared_ptr <vmime::utility::charsetFilteredOutputStream> os = + conv->getFilteredOutputStream(osa); + + VASSERT_NOT_NULL("filteredOutputStream availability", os); + + vmime::utility::inputStreamStringAdapter is(in); + + vmime::utility::bufferedStreamCopy(is, *os); + + os->flush(); + + VASSERT_EQ(testName.str(), toHex(expectedOut), toHex(actualOut)); + } + } + + // One byte at a time + void testOneByteAtTime() { + + for (unsigned int i = 0 ; i < charsetTestSuitesCount ; ++i) { + + const charsetTestSuiteStruct& entry = charsetTestSuites[i]; + + std::ostringstream testName; + testName << i << ": " << entry.fromCharset << " -> " << entry.toCharset; + + const unsigned long inLength = (entry.fromLength == 0 ? strlen(entry.fromBytes) : entry.fromLength); + vmime::string in(entry.fromBytes, entry.fromBytes + inLength); + + const unsigned long outLength = (entry.toLength == 0 ? strlen(entry.toBytes) : entry.toLength); + vmime::string expectedOut(entry.toBytes, entry.toBytes + outLength); + + vmime::string actualOut; + vmime::utility::outputStreamStringAdapter osa(actualOut); + + vmime::shared_ptr <vmime::charsetConverter> conv = + vmime::charsetConverter::create(entry.fromCharset, entry.toCharset); + + vmime::shared_ptr <vmime::utility::charsetFilteredOutputStream> os = + conv->getFilteredOutputStream(osa); + + VASSERT_NOT_NULL("filteredOutputStream availability", os); + + vmime::utility::inputStreamStringAdapter is(in); + + vmime::byte_t buffer[16]; + + for (int i = 0 ; !is.eof() ; ++i) + os->write(buffer, is.read(buffer, 1)); + + os->flush(); + + VASSERT_EQ(testName.str(), toHex(expectedOut), toHex(actualOut)); + } + } + + // Variable chunks + void testVariableInputChunk() { + + for (unsigned int i = 0 ; i < charsetTestSuitesCount ; ++i) { + + const charsetTestSuiteStruct& entry = charsetTestSuites[i]; + + std::ostringstream testName; + testName << i << ": " << entry.fromCharset << " -> " << entry.toCharset; + + const unsigned long inLength = (entry.fromLength == 0 ? strlen(entry.fromBytes) : entry.fromLength); + vmime::string in(entry.fromBytes, entry.fromBytes + inLength); + + const unsigned long outLength = (entry.toLength == 0 ? strlen(entry.toBytes) : entry.toLength); + vmime::string expectedOut(entry.toBytes, entry.toBytes + outLength); + + vmime::string actualOut; + vmime::utility::outputStreamStringAdapter osa(actualOut); + + vmime::shared_ptr <vmime::charsetConverter> conv = + vmime::charsetConverter::create(entry.fromCharset, entry.toCharset); + + vmime::shared_ptr <vmime::utility::charsetFilteredOutputStream> os = + conv->getFilteredOutputStream(osa); + + VASSERT_NOT_NULL("filteredOutputStream availability", os); + + vmime::utility::inputStreamStringAdapter is(in); + + vmime::byte_t buffer[16]; + + for (int i = 0 ; !is.eof() ; ++i) + os->write(buffer, is.read(buffer, (i % 5) + 1)); + + os->flush(); + + VASSERT_EQ(testName.str(), toHex(expectedOut), toHex(actualOut)); + } + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/charsetTest.cpp b/vmime-master/tests/parser/charsetTest.cpp new file mode 100644 index 0000000..e44bef5 --- /dev/null +++ b/vmime-master/tests/parser/charsetTest.cpp @@ -0,0 +1,252 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include <algorithm> + +#include "tests/testUtils.hpp" + +#include "charsetTestSuites.hpp" + + +VMIME_TEST_SUITE_BEGIN(charsetTest) + + VMIME_TEST_LIST_BEGIN + // Test valid input + VMIME_TEST(testConvertStringValid) + VMIME_TEST(testConvertStreamValid) + VMIME_TEST(testEncodingHebrew1255) + + // IDNA + VMIME_TEST(testEncodeIDNA) + VMIME_TEST(testDecodeIDNA) + + VMIME_TEST(testUTF7Support) + + VMIME_TEST(testReplaceInvalidSequence) + VMIME_TEST(testStopOnInvalidSequence) + + VMIME_TEST(testStatus) + VMIME_TEST(testStatusWithInvalidSequence) + + VMIME_TEST(testIsValidText) + VMIME_TEST_LIST_END + + + void testConvertStringValid() { + + for (unsigned int i = 0 ; i < charsetTestSuitesCount ; ++i) { + + const charsetTestSuiteStruct& entry = charsetTestSuites[i]; + + std::ostringstream testName; + testName << i << ": " << entry.fromCharset << " -> " << entry.toCharset; + + const unsigned long inLength = (entry.fromLength == 0 ? strlen(entry.fromBytes) : entry.fromLength); + vmime::string in(entry.fromBytes, entry.fromBytes + inLength); + + const unsigned long outLength = (entry.toLength == 0 ? strlen(entry.toBytes) : entry.toLength); + vmime::string expectedOut(entry.toBytes, entry.toBytes + outLength); + + vmime::string actualOut; + + vmime::charset::convert + (in, actualOut, entry.fromCharset, entry.toCharset); + + VASSERT_EQ(testName.str(), toHex(expectedOut), toHex(actualOut)); + } + } + + void testConvertStreamValid() { + + for (unsigned int i = 0 ; i < charsetTestSuitesCount ; ++i) { + + const charsetTestSuiteStruct& entry = charsetTestSuites[i]; + + std::ostringstream testName; + testName << i << ": " << entry.fromCharset << " -> " << entry.toCharset; + + const unsigned long inLength = (entry.fromLength == 0 ? strlen(entry.fromBytes) : entry.fromLength); + vmime::string in(entry.fromBytes, entry.fromBytes + inLength); + + const unsigned long outLength = (entry.toLength == 0 ? strlen(entry.toBytes) : entry.toLength); + vmime::string expectedOut(entry.toBytes, entry.toBytes + outLength); + + vmime::string actualOut; + vmime::utility::outputStreamStringAdapter os(actualOut); + + vmime::utility::inputStreamStringAdapter is(in); + + vmime::charset::convert(is, os, entry.fromCharset, entry.toCharset); + + os.flush(); + + VASSERT_EQ(testName.str(), toHex(expectedOut), toHex(actualOut)); + } + } + + void testEncodingHebrew1255() { + + // hewbrew string in windows-1255 charset + const char data[] = "\xe9\xf9\xf7\xf8\xe9\xf9\xf8\xf7\xe9\xe9\xf9"; + vmime::word w = vmime::word(data, "windows-1255"); + vmime::string encoded = w.generate(); + // less than 60% ascii, base64 received + VASSERT_EQ("1", "=?windows-1255?B?6fn3+On5+Pfp6fk=?=", encoded); + } + + static const vmime::string convertHelper( + const vmime::string& in, + const vmime::charset& csrc, + const vmime::charset& cdest, + const vmime::charsetConverterOptions& opts = vmime::charsetConverterOptions(), + vmime::charsetConverter::status* st = NULL + ) { + + vmime::shared_ptr <vmime::charsetConverter> conv = + vmime::charsetConverter::create(csrc, cdest, opts); + + vmime::string out; + conv->convert(in, out, st); + + return out; + } + + void testEncodeIDNA() { + + VASSERT_EQ("1", "xn--espaol-zwa", convertHelper("español", "utf-8", "idna")); + + // Tests from ICANN + VASSERT_EQ("2.1", "xn--hxajbheg2az3al", convertHelper("παράδειγμα", "utf-8", "idna")); + VASSERT_EQ("2.2", "xn--jxalpdlp", convertHelper("δοκιμή", "utf-8", "idna")); + + VASSERT_EQ("3.1", "xn--mgbh0fb", convertHelper("مثال", "utf-8", "idna")); + VASSERT_EQ("3.2", "xn--kgbechtv", convertHelper("إختبار", "utf-8", "idna")); + } + + void testDecodeIDNA() { + + VASSERT_EQ("1", "español", convertHelper("xn--espaol-zwa", "idna", "utf-8")); + + // Tests from ICANN + VASSERT_EQ("2.1", "παράδειγμα", convertHelper("xn--hxajbheg2az3al", "idna", "utf-8")); + VASSERT_EQ("2.2", "δοκιμή", convertHelper("xn--jxalpdlp", "idna", "utf-8")); + + VASSERT_EQ("3.1", "مثال", convertHelper("xn--mgbh0fb", "idna", "utf-8")); + VASSERT_EQ("3.2", "إختبار", convertHelper("xn--kgbechtv", "idna", "utf-8")); + } + + void testUTF7Support() { + + // Ensure UTF-7 is supported, because it is used for IMAP + VASSERT_EQ("1", "VMime +- UTF-7 encoding", convertHelper("VMime + UTF-7 encoding", "utf-8", "utf-7")); + VASSERT_EQ("2", "f+APg-o", convertHelper("\x66\xc3\xb8\x6f", "utf-8", "utf-7")); + } + + void testReplaceInvalidSequence() { + + vmime::charsetConverterOptions opts; + opts.silentlyReplaceInvalidSequences = true; + opts.invalidSequence = "?"; + + vmime::string res = convertHelper( + "\x61\xf1\x80\x80\xe1\x80\xc2\x62\x80\x63\x80\xbf\x64", + "utf-8", "iso-8859-1", opts + ); + + // Result should be in the form "a???b?c??d" or "a??????b?c??d"... + // Remove consecutive question marks for easier matching. + res.erase(std::unique(res.begin(), res.end()), res.end()); + + VASSERT_EQ( + "Illegal UTF-8 sequence", + "a?b?c?d", + res + ); + } + + void testStopOnInvalidSequence() { + + vmime::charsetConverterOptions opts; + opts.silentlyReplaceInvalidSequences = false; + + VASSERT_THROW( + "Illegal UTF-8 sequence", + convertHelper("\x61\xf1\x80\x80\xe1\x80\xc2\x62\x80\x63\x80\xbf\x64", "utf-8", "iso-8859-1", opts), + vmime::exceptions::illegal_byte_sequence_for_charset + ); + } + + void testStatus() { + + vmime::charsetConverterOptions opts; + opts.silentlyReplaceInvalidSequences = false; + + vmime::charsetConverter::status st; + + // 012345 6 7 + convertHelper("Gwena\xc3\xabl", "utf-8", "iso-8859-1", opts, &st); + + VASSERT_EQ("inputBytesRead", 8, st.inputBytesRead); + VASSERT_EQ("outputBytesWritten", 7, st.outputBytesWritten); + } + + void testStatusWithInvalidSequence() { + + vmime::charsetConverterOptions opts; + opts.silentlyReplaceInvalidSequences = false; + + vmime::charsetConverter::status st; + + try { + + // 01234 5 6789 0 1 + convertHelper("Fran\xc3\xa7ois\xf1\x80\x65", "utf-8", "iso-8859-1", opts, &st); + + } catch (vmime::exceptions::illegal_byte_sequence_for_charset& e) { + + } catch (...) { + + throw; + } + + VASSERT_EQ("inputBytesRead", 9, st.inputBytesRead); + VASSERT_EQ("outputBytesWritten", 8, st.outputBytesWritten); + } + + void testIsValidText() { + + // Invalid text + const vmime::string invalidText("Fran\xc3\xa7ois\xf1\x80\x65"); + vmime::string::size_type firstInvalidByte; + + VASSERT_EQ("invalid.isValidText", false, vmime::charset("utf-8").isValidText(invalidText, &firstInvalidByte)); + VASSERT_EQ("invalid.firstInvalidByte", 9, firstInvalidByte); + + // Valid text + const vmime::string validText("Gwena\xc3\xabl"); + + VASSERT_EQ("valid.isValidText", true, vmime::charset("utf-8").isValidText(validText, &firstInvalidByte)); + VASSERT_EQ("valid.firstInvalidByte", 8, firstInvalidByte); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/charsetTestSuites.hpp b/vmime-master/tests/parser/charsetTestSuites.hpp new file mode 100644 index 0000000..9653016 --- /dev/null +++ b/vmime-master/tests/parser/charsetTestSuites.hpp @@ -0,0 +1,102 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + + +struct charsetTestSuiteStruct { + + const char* fromCharset; + const char* toCharset; + const char* fromBytes; + const unsigned int fromLength; + const char* toBytes; + const unsigned int toLength; +}; + + +static const charsetTestSuiteStruct charsetTestSuites[] = { + + // Test data 1 (excerpt from http://www.gnu.org) + { + "gb2312", "utf-8", + + "\xbb\xb6\xd3\xad\xc0\xb4\xb5\xbd\x20\x47\x4e\x55\x20\xb9\xa4\xb3" + "\xcc\xb5\xc4\xcd\xf8\xd2\xb3\xcb\xc5\xb7\xfe\xd6\xf7\xbb\xfa\x20" + "\x77\x77\x77\x2e\x67\x6e\x75\x2e\x6f\x72\x67\x20\xa1\xa3\x20\x47" + "\x4e\x55\x20\xb9\xa4\xb3\xcc\x20\xbf\xaa\xca\xbc\xec\xb6\xd2\xbb" + "\xbe\xc5\xb0\xcb\xcb\xc4\xc4\xea\xa3\xac\xd6\xbc\xd4\xda\xb7\xa2" + "\xd5\xb9\xd2\xbb\xb8\xf6\xc0\xe0\xcb\xc6\x20\x55\x6e\x69\x78\x20" + "\xa3\xac\xc7\xd2\xce\xaa\x20\xd7\xd4\xd3\xc9\xc8\xed\xbc\xfe\x20" + "\xb5\xc4\xcd\xea\xd5\xfb\xb2\xd9\xd7\xf7\xcf\xb5\xcd\xb3\xa3\xba" + "\x20\x47\x4e\x55\x20\xcf\xb5\xcd\xb3\xa1\xa3\xa3\xa8\x47\x4e\x55" + "\x20\xca\xc7\xd3\xc9\xa1\xb0\x47\x4e\x55\x27\x73\x20\x4e\x6f\x74" + "\x20\x55\x6e\x69\x78\xa1\xb1\xcb\xf9\xb5\xdd\xbb\xd8\xb6\xa8\xd2" + "\xe5\xb3\xf6\xb5\xc4\xca\xd7\xd7\xd6\xc4\xb8\xcb\xf5\xd0\xb4\xd3" + "\xef\xa3\xbb\xcb\xfc\xb5\xc4\xb7\xa2\xd2\xf4\xce\xaa\xa1\xb0\x67" + "\x75\x68\x2d\x4e\x45\x57\xa1\xb1\xa3\xa9\xa1\xa3\xb8\xf7\xd6\xd6" + "\xca\xb9\xd3\xc3\x20\x4c\x69\x6e\x75\x78\x20\xd7\xf7\xce\xaa\xc4" + "\xda\xba\xcb\xb5\xc4\x20\x47\x4e\x55\x20\xb2\xd9\xd7\xf7\xcf\xb5" + "\xcd\xb3\xd5\xfd\xb1\xbb\xb9\xe3\xb7\xba\xb5\xd8\xca\xb9\xd3\xc3" + "\xd6\xf8\xa3\xbb\xcb\xe4\xc8\xbb\xd5\xe2\xd0\xa9\xcf\xb5\xcd\xb3" + "\xcd\xa8\xb3\xa3\xb1\xbb\xb3\xc6\xd7\xf7\xce\xaa\xa1\xb0\x4c\x69" + "\x6e\x75\x78\xa1\xb1\xa3\xac\xb5\xab\xca\xc7\xcb\xfc\xc3\xc7\xd3" + "\xa6\xb8\xc3\xb8\xfc\xbe\xab\xc8\xb7\xb5\xd8\xb1\xbb\xb3\xc6\xce" + "\xaa\x20\x47\x4e\x55\x2f\x4c\x69\x6e\x75\x78\x20\xcf\xb5\xcd\xb3" + "\x20\xa1\xa3\x0a", + 0, + + "\xe6\xac\xa2\xe8\xbf\x8e\xe6\x9d\xa5\xe5\x88\xb0\x20\x47\x4e\x55" + "\x20\xe5\xb7\xa5\xe7\xa8\x8b\xe7\x9a\x84\xe7\xbd\x91\xe9\xa1\xb5" + "\xe4\xbc\xba\xe6\x9c\x8d\xe4\xb8\xbb\xe6\x9c\xba\x20\x77\x77\x77" + "\x2e\x67\x6e\x75\x2e\x6f\x72\x67\x20\xe3\x80\x82\x20\x47\x4e\x55" + "\x20\xe5\xb7\xa5\xe7\xa8\x8b\x20\xe5\xbc\x80\xe5\xa7\x8b\xe6\x96" + "\xbc\xe4\xb8\x80\xe4\xb9\x9d\xe5\x85\xab\xe5\x9b\x9b\xe5\xb9\xb4" + "\xef\xbc\x8c\xe6\x97\xa8\xe5\x9c\xa8\xe5\x8f\x91\xe5\xb1\x95\xe4" + "\xb8\x80\xe4\xb8\xaa\xe7\xb1\xbb\xe4\xbc\xbc\x20\x55\x6e\x69\x78" + "\x20\xef\xbc\x8c\xe4\xb8\x94\xe4\xb8\xba\x20\xe8\x87\xaa\xe7\x94" + "\xb1\xe8\xbd\xaf\xe4\xbb\xb6\x20\xe7\x9a\x84\xe5\xae\x8c\xe6\x95" + "\xb4\xe6\x93\x8d\xe4\xbd\x9c\xe7\xb3\xbb\xe7\xbb\x9f\xef\xbc\x9a" + "\x20\x47\x4e\x55\x20\xe7\xb3\xbb\xe7\xbb\x9f\xe3\x80\x82\xef\xbc" + "\x88\x47\x4e\x55\x20\xe6\x98\xaf\xe7\x94\xb1\xe2\x80\x9c\x47\x4e" + "\x55\x27\x73\x20\x4e\x6f\x74\x20\x55\x6e\x69\x78\xe2\x80\x9d\xe6" + "\x89\x80\xe9\x80\x92\xe5\x9b\x9e\xe5\xae\x9a\xe4\xb9\x89\xe5\x87" + "\xba\xe7\x9a\x84\xe9\xa6\x96\xe5\xad\x97\xe6\xaf\x8d\xe7\xbc\xa9" + "\xe5\x86\x99\xe8\xaf\xad\xef\xbc\x9b\xe5\xae\x83\xe7\x9a\x84\xe5" + "\x8f\x91\xe9\x9f\xb3\xe4\xb8\xba\xe2\x80\x9c\x67\x75\x68\x2d\x4e" + "\x45\x57\xe2\x80\x9d\xef\xbc\x89\xe3\x80\x82\xe5\x90\x84\xe7\xa7" + "\x8d\xe4\xbd\xbf\xe7\x94\xa8\x20\x4c\x69\x6e\x75\x78\x20\xe4\xbd" + "\x9c\xe4\xb8\xba\xe5\x86\x85\xe6\xa0\xb8\xe7\x9a\x84\x20\x47\x4e" + "\x55\x20\xe6\x93\x8d\xe4\xbd\x9c\xe7\xb3\xbb\xe7\xbb\x9f\xe6\xad" + "\xa3\xe8\xa2\xab\xe5\xb9\xbf\xe6\xb3\x9b\xe5\x9c\xb0\xe4\xbd\xbf" + "\xe7\x94\xa8\xe8\x91\x97\xef\xbc\x9b\xe8\x99\xbd\xe7\x84\xb6\xe8" + "\xbf\x99\xe4\xba\x9b\xe7\xb3\xbb\xe7\xbb\x9f\xe9\x80\x9a\xe5\xb8" + "\xb8\xe8\xa2\xab\xe7\xa7\xb0\xe4\xbd\x9c\xe4\xb8\xba\xe2\x80\x9c" + "\x4c\x69\x6e\x75\x78\xe2\x80\x9d\xef\xbc\x8c\xe4\xbd\x86\xe6\x98" + "\xaf\xe5\xae\x83\xe4\xbb\xac\xe5\xba\x94\xe8\xaf\xa5\xe6\x9b\xb4" + "\xe7\xb2\xbe\xe7\xa1\xae\xe5\x9c\xb0\xe8\xa2\xab\xe7\xa7\xb0\xe4" + "\xb8\xba\x20\x47\x4e\x55\x2f\x4c\x69\x6e\x75\x78\x20\xe7\xb3\xbb" + "\xe7\xbb\x9f\x20\xe3\x80\x82\x0a", + 0 + } +}; + +static const vmime::size_t charsetTestSuitesCount = sizeof(charsetTestSuites) / sizeof(charsetTestSuites[0]); diff --git a/vmime-master/tests/parser/datetimeTest.cpp b/vmime-master/tests/parser/datetimeTest.cpp new file mode 100644 index 0000000..61c8522 --- /dev/null +++ b/vmime-master/tests/parser/datetimeTest.cpp @@ -0,0 +1,123 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(datetimeTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParse) + VMIME_TEST(testGenerate) + VMIME_TEST(testCompare) + VMIME_TEST_LIST_END + + + void testParse() { + + struct datetimePair { + vmime::string parseBuffer; + vmime::datetime result; + }; + + // Here, we can't test all the possible structures for date/time, + // so we test some cases. Don't forget to add a new test case + // each time you encounter a bug in date/time parsing (after + // you have fixed it). + datetimePair pairs[] = { + + { /* 1 */ "Mon, 8 Nov 2004 13:42:56 +0000 (GMT)", + vmime::datetime(2004, 11, 8, 13, 42, 56, vmime::datetime::GMT) }, + + { /* 2 */ "Sun, 7 Nov 2004 00:43:22 -0500 (EST)", + vmime::datetime(2004, 11, 7, 0, 43, 22, vmime::datetime::GMT_5) }, + + { /* 3 */ "Thu Nov 18 12:11:16 2004", + vmime::datetime(vmime::datetime::now().getYear(), 11, 18, 12, 11, 16, vmime::datetime::GMT) }, + + { /* 4 */ "Sat, 18, 2004 22:36:32 -0400", + vmime::datetime(2004, 1, 18, 22, 36, 32, vmime::datetime::GMT_4) }, + + { /* 5 */ "Mon Dec 13 21:57:18 2004", + vmime::datetime(vmime::datetime::now().getYear(), 12, 13, 21, 57, 18, vmime::datetime::GMT) }, + + { /* 6 */ "18 Nov 2004 21:44:54 +0300", + vmime::datetime(2004, 11, 18, 21, 44, 54, vmime::datetime::GMT3) } + }; + + for (unsigned int i = 0 ; i < sizeof(pairs) / sizeof(pairs[0]) ; ++i) { + + vmime::datetime d; + d.parse(pairs[i].parseBuffer); + + std::ostringstream oss; + oss << (i + 1); + + VASSERT_EQ(oss.str(), pairs[i].result, d); + } + } + + void testGenerate() { + + vmime::datetime d1(2005, 7, 8, 4, 5, 6, 1 * 60 + 23); + + VASSERT_EQ("1", "Fri, 8 Jul 2005 04:05:06 +0123", d1.generate()); + } + + void testCompare() { + + // Date1 = Date2 + vmime::datetime d1(2005, 4, 22, 14, 6, 0, vmime::datetime::GMT2); + vmime::datetime d2(2005, 4, 22, 10, 6, 0, vmime::datetime::GMT_2); + + VASSERT_EQ("1.1", true, d1 == d2); + VASSERT_EQ("1.2", false, d1 != d2); + VASSERT_EQ("1.3", true, d1 <= d2); + VASSERT_EQ("1.4", false, d1 < d2); + VASSERT_EQ("1.5", true, d1 >= d2); + VASSERT_EQ("1.6", false, d1 > d2); + + // Date1 < Date2 + vmime::datetime d3(2005, 4, 22, 14, 6, 0); + vmime::datetime d4(2005, 4, 22, 15, 6, 0); + + VASSERT_EQ("2.1", false, d3 == d4); + VASSERT_EQ("2.2", true, d3 != d4); + VASSERT_EQ("2.3", true, d3 <= d4); + VASSERT_EQ("2.4", true, d3 < d4); + VASSERT_EQ("2.5", false, d3 >= d4); + VASSERT_EQ("2.6", false, d3 > d4); + + // Date1 > Date2 + vmime::datetime d5(2005, 4, 22, 15, 6, 0); + vmime::datetime d6(2005, 4, 22, 14, 6, 0); + + VASSERT_EQ("3.1", false, d5 == d6); + VASSERT_EQ("3.2", true, d5 != d6); + VASSERT_EQ("3.3", false, d5 <= d6); + VASSERT_EQ("3.4", false, d5 < d6); + VASSERT_EQ("3.5", true, d5 >= d6); + VASSERT_EQ("3.6", true, d5 > d6); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/dispositionTest.cpp b/vmime-master/tests/parser/dispositionTest.cpp new file mode 100644 index 0000000..f51c51e --- /dev/null +++ b/vmime-master/tests/parser/dispositionTest.cpp @@ -0,0 +1,150 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(dispositionTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParse) + VMIME_TEST(testGenerate) + VMIME_TEST(testModifiers) + VMIME_TEST_LIST_END + + + void testParse() { + + // disposition-mode ";" disposition-type + // [ "/" disposition-modifier *( "," disposition-modifier ) ] + // + // disposition-mode = action-mode "/" sending-mode + + vmime::disposition disp1; + disp1.parse("mode"); + + VASSERT_EQ("1.1", "mode", disp1.getActionMode()); + VASSERT_EQ("1.2", "", disp1.getSendingMode()); + VASSERT_EQ("1.3", "", disp1.getType()); + VASSERT_EQ("1.4", 0, static_cast <int>(disp1.getModifierList().size())); + + vmime::disposition disp2; + disp2.parse("amode/smode"); + + VASSERT_EQ("2.1", "amode", disp2.getActionMode()); + VASSERT_EQ("2.2", "smode", disp2.getSendingMode()); + VASSERT_EQ("2.3", "", disp2.getType()); + VASSERT_EQ("2.4", 0, static_cast <int>(disp2.getModifierList().size())); + + vmime::disposition disp3; + disp3.parse("amode/smode;type"); + + VASSERT_EQ("3.1", "amode", disp3.getActionMode()); + VASSERT_EQ("3.2", "smode", disp3.getSendingMode()); + VASSERT_EQ("3.3", "type", disp3.getType()); + VASSERT_EQ("3.4", 0, static_cast <int>(disp3.getModifierList().size())); + + vmime::disposition disp4; + disp4.parse("amode/smode;type/modif"); + + VASSERT_EQ("4.1", "amode", disp4.getActionMode()); + VASSERT_EQ("4.2", "smode", disp4.getSendingMode()); + VASSERT_EQ("4.3", "type", disp4.getType()); + VASSERT_EQ("4.4", 1, static_cast <int>(disp4.getModifierList().size())); + VASSERT_EQ("4.5", "modif", disp4.getModifierList()[0]); + + vmime::disposition disp5; + disp5.parse("amode/smode;type/modif1,modif2"); + + VASSERT_EQ("5.1", "amode", disp5.getActionMode()); + VASSERT_EQ("5.2", "smode", disp5.getSendingMode()); + VASSERT_EQ("5.3", "type", disp5.getType()); + VASSERT_EQ("5.4", 2, static_cast <int>(disp5.getModifierList().size())); + VASSERT_EQ("5.5", "modif1", disp5.getModifierList()[0]); + VASSERT_EQ("5.6", "modif2", disp5.getModifierList()[1]); + } + + void testGenerate() { + + vmime::disposition disp; + + VASSERT_EQ("1", "automatic-action/MDN-sent-automatically;displayed", disp.generate()); + + disp.setActionMode("amode"); + + VASSERT_EQ("2", "amode/MDN-sent-automatically;displayed", disp.generate()); + + disp.setActionMode("amode"); + disp.setSendingMode("smode"); + + VASSERT_EQ("3", "amode/smode;displayed", disp.generate()); + + disp.setType("type"); + + VASSERT_EQ("4", "amode/smode;type", disp.generate()); + + disp.addModifier("modif1"); + + VASSERT_EQ("5", "amode/smode;type/modif1", disp.generate()); + + disp.addModifier("modif2"); + + VASSERT_EQ("6", "amode/smode;type/modif1,modif2", disp.generate()); + } + + void testModifiers() { + + vmime::disposition disp1; + + VASSERT_EQ("1", false, disp1.hasModifier("foo")); + VASSERT_EQ("2", 0, static_cast <int>(disp1.getModifierList().size())); + + disp1.addModifier("bar"); + + VASSERT_EQ("3", false, disp1.hasModifier("foo")); + VASSERT_EQ("4", true, disp1.hasModifier("bar")); + VASSERT_EQ("5", 1, static_cast <int>(disp1.getModifierList().size())); + + disp1.addModifier("plop"); + + VASSERT_EQ("6", false, disp1.hasModifier("foo")); + VASSERT_EQ("7", true, disp1.hasModifier("bar")); + VASSERT_EQ("8", true, disp1.hasModifier("plop")); + VASSERT_EQ("9", 2, static_cast <int>(disp1.getModifierList().size())); + + disp1.removeModifier("bar"); + + VASSERT_EQ("10", false, disp1.hasModifier("foo")); + VASSERT_EQ("11", false, disp1.hasModifier("bar")); + VASSERT_EQ("12", true, disp1.hasModifier("plop")); + VASSERT_EQ("13", 1, static_cast <int>(disp1.getModifierList().size())); + + disp1.removeModifier("PlOp"); + + VASSERT_EQ("14", false, disp1.hasModifier("foo")); + VASSERT_EQ("15", false, disp1.hasModifier("bar")); + VASSERT_EQ("16", false, disp1.hasModifier("plop")); + VASSERT_EQ("17", 0, static_cast <int>(disp1.getModifierList().size())); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/emailAddressTest.cpp b/vmime-master/tests/parser/emailAddressTest.cpp new file mode 100644 index 0000000..f2a7070 --- /dev/null +++ b/vmime-master/tests/parser/emailAddressTest.cpp @@ -0,0 +1,281 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include "vmime/platform.hpp" + +#include <locale> +#include <clocale> + + +VMIME_TEST_SUITE_BEGIN(emailAddressTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParseASCII) + VMIME_TEST(testParseEAI) + VMIME_TEST(testParseInvalid) + VMIME_TEST(testGenerateASCII) + VMIME_TEST(testGenerateEAI) + VMIME_TEST(testParseSpecialChars) + VMIME_TEST(testParseCommentInLocalPart) + VMIME_TEST(testParseCommentInDomainPart) + VMIME_TEST(testParseRFC2047EncodedLocalPart) + VMIME_TEST(testGenerateSpecialChars) + VMIME_TEST_LIST_END + + + void setUp() { + + // Set the global C and C++ locale to the user-configured locale. + // The locale should use UTF-8 encoding for these tests to run successfully. + try { + std::locale::global(std::locale("")); + } catch (std::exception &) { + std::setlocale(LC_ALL, ""); + } + } + + void tearDown() { + + // Restore default locale + std::locale::global(std::locale("C")); + } + + + void testParseASCII() { + + vmime::emailAddress eml1("local@domain"); + VASSERT_EQ("1/local", "local", eml1.getLocalName()); + VASSERT_EQ("1/domain", "domain", eml1.getDomainName()); + + // When not specified, domain should be local host name + vmime::emailAddress eml2("local"); + VASSERT_EQ("2/local", "local", eml2.getLocalName()); + VASSERT_EQ("2/domain", vmime::platform::getHandler()->getHostName(), eml2.getDomainName()); + } + + void testParseEAI() { + + // Examples taken from Wikipedia (http://en.wikipedia.org/wiki/Email_address) + + // Latin Alphabet (with diacritics): + vmime::emailAddress eml1("Pelé@example.com"); + VASSERT_EQ("1/local", "Pelé", eml1.getLocalName()); + VASSERT_EQ("1/domain", "example.com", eml1.getDomainName()); + + // Greek Alphabet + vmime::emailAddress eml2("δοκιμή@παράδειγμα.δοκιμή"); + VASSERT_EQ("2/local", "δοκιμή", eml2.getLocalName()); + VASSERT_EQ("2/domain", "παράδειγμα.δοκιμή", eml2.getDomainName()); + + // Japanese Characters + vmime::emailAddress eml3("甲斐@黒川.日本"); + VASSERT_EQ("3/local", "甲斐", eml3.getLocalName()); + VASSERT_EQ("3/domain", "黒川.日本", eml3.getDomainName()); + + // Cyrillic Characters + vmime::emailAddress eml4("чебурашка@ящик-с-апельсинами.рф"); + VASSERT_EQ("4/local", "чебурашка", eml4.getLocalName()); + VASSERT_EQ("4/domain", "ящик-с-апельсинами.рф", eml4.getDomainName()); + } + + void testParseInvalid() { + + // Only one @ is allowed outside quotation marks + vmime::emailAddress eml1("local@part@domain"); + VASSERT_EQ("1/local", "local", eml1.getLocalName()); + VASSERT_EQ("1/domain", "part@domain", eml1.getDomainName()); + + // Quoted strings must be dot separated, or the only element making up + // the local-part: should be parsed correctly, but it still represents + // an invalid email address + vmime::emailAddress eml2("Just\"not\"right@example.com"); + VASSERT_EQ("2/local", "Just\"not\"right", eml2.getLocalName()); + VASSERT_EQ("2/domain", "example.com", eml2.getDomainName()); + + // An @ character must separate the local and domain parts + vmime::emailAddress eml3("Abc.example.com"); + VASSERT_EQ("3/local", "Abc.example.com", eml3.getLocalName()); + VASSERT_EQ("3/domain", vmime::platform::getHandler()->getHostName(), eml3.getDomainName()); + + // Whitespace must be escaped + vmime::emailAddress eml4("local part@domain"); + VASSERT_EQ("4/local", "local", eml4.getLocalName()); + VASSERT_EQ("4/domain", vmime::platform::getHandler()->getHostName(), eml4.getDomainName()); + + vmime::emailAddress eml5("this\\ still\\\"not\\\\allowed@example.com"); + VASSERT_EQ("5/local", "this\\", eml5.getLocalName()); + VASSERT_EQ("5/domain", vmime::platform::getHandler()->getHostName(), eml5.getDomainName()); + } + + void testParseSpecialChars() { + + // Examples taken from Wikipedia (http://en.wikipedia.org/wiki/Email_address) + + vmime::emailAddress eml1("\" \"@example.org"); + VASSERT_EQ("1/local", " ", eml1.getLocalName()); + VASSERT_EQ("1/domain", "example.org", eml1.getDomainName()); + + vmime::emailAddress eml2("\"()<>[]:,;@\\\\\\\"!#$%&'*+-/=?^_`{}| ~.a\"@example.org"); + VASSERT_EQ("2/local", "()<>[]:,;@\\\"!#$%&'*+-/=?^_`{}| ~.a", eml2.getLocalName()); + VASSERT_EQ("3/domain", "example.org", eml2.getDomainName()); + + vmime::emailAddress eml3("!#$%&'*+-/=?^_`{}|~@example.org"); + VASSERT_EQ("3/local", "!#$%&'*+-/=?^_`{}|~", eml3.getLocalName()); + VASSERT_EQ("3/domain", "example.org", eml3.getDomainName()); + + vmime::emailAddress eml4("!#$%&'*+-/=?^_`{}|~@example.org"); + VASSERT_EQ("4/local", "!#$%&'*+-/=?^_`{}|~", eml4.getLocalName()); + VASSERT_EQ("4/domain", "example.org", eml4.getDomainName()); + + vmime::emailAddress eml5("\"very.unusual.@.unusual.com\"@example.com"); + VASSERT_EQ("5/local", "very.unusual.@.unusual.com", eml5.getLocalName()); + VASSERT_EQ("5/domain", "example.com", eml5.getDomainName()); + + vmime::emailAddress eml6("\"very.(),:;<>[]\\\".VERY.\\\"very@\\\\ \\\"very\\\".unusual\"@strange.example.com"); + VASSERT_EQ("6/local", "very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual", eml6.getLocalName()); + VASSERT_EQ("6/domain", "strange.example.com", eml6.getDomainName()); + } + + void testParseCommentInLocalPart() { + + vmime::emailAddress eml1("john.smith(comment)@example.com"); + VASSERT_EQ("1/local", "john.smith", eml1.getLocalName()); + VASSERT_EQ("1/domain", "example.com", eml1.getDomainName()); + + vmime::emailAddress eml2("(comment)john.smith@example.com"); + VASSERT_EQ("2/local", "john.smith", eml2.getLocalName()); + VASSERT_EQ("2/domain", "example.com", eml2.getDomainName()); + + vmime::emailAddress eml3("(comment (comment in comment))john.smith@example.com"); + VASSERT_EQ("3/local", "john.smith", eml3.getLocalName()); + VASSERT_EQ("3/domain", "example.com", eml3.getDomainName()); + + vmime::emailAddress eml4("(comment \\) end comment)john.smith@example.com"); + VASSERT_EQ("4/local", "john.smith", eml4.getLocalName()); + VASSERT_EQ("4/domain", "example.com", eml4.getDomainName()); + } + + void testParseCommentInDomainPart() { + + vmime::emailAddress eml1("john.smith@(comment)example.com"); + VASSERT_EQ("1/local", "john.smith", eml1.getLocalName()); + VASSERT_EQ("1/domain", "example.com", eml1.getDomainName()); + + vmime::emailAddress eml2("john.smith@example.com(comment)"); + VASSERT_EQ("2/local", "john.smith", eml2.getLocalName()); + VASSERT_EQ("2/domain", "example.com", eml2.getDomainName()); + + vmime::emailAddress eml3("john.smith@(comment (comment in comment))example.com"); + VASSERT_EQ("3/local", "john.smith", eml3.getLocalName()); + VASSERT_EQ("3/domain", "example.com", eml3.getDomainName()); + + vmime::emailAddress eml4("john.smith@(comment \\) end comment)example.com"); + VASSERT_EQ("4/local", "john.smith", eml4.getLocalName()); + VASSERT_EQ("4/domain", "example.com", eml4.getDomainName()); + } + + void testParseRFC2047EncodedLocalPart() { + + vmime::emailAddress eml1("=?utf-8?Q?Pel=C3=A9?=@example.com"); + VASSERT_EQ("1/local", "Pelé", eml1.getLocalName()); + VASSERT_EQ("1/domain", "example.com", eml1.getDomainName()); + + vmime::emailAddress eml2("=?utf-8?B?55Sy5paQ?=@xn--5rtw95l.xn--wgv71a"); + VASSERT_EQ("2/local", "甲斐", eml2.getLocalName()); + VASSERT_EQ("2/domain", "黒川.日本", eml2.getDomainName()); + + vmime::emailAddress eml3("=?utf-8?B?55Sy5paQ?=@xn--5rtw95l.com"); + VASSERT_EQ("3/local", "甲斐", eml3.getLocalName()); + VASSERT_EQ("3/domain", "黒川.com", eml3.getDomainName()); + } + + void testGenerateASCII() { + + VASSERT_EQ( + "email 1", "local@domain", + vmime::emailAddress("local", "domain").generate() + ); + VASSERT_EQ( + "email 2", "=?utf-8?Q?Pel=C3=A9?=@example.com", + vmime::emailAddress("Pelé", "example.com").generate() + ); + VASSERT_EQ( + "email 3", "=?utf-8?B?55Sy5paQ?=@xn--5rtw95l.xn--wgv71a", + vmime::emailAddress("甲斐", "黒川.日本").generate() + ); + VASSERT_EQ( + "email 4", "mailtest@xn--r8jz45g.xn--zckzah", + vmime::emailAddress("mailtest", "例え.テスト").generate() + ); + VASSERT_EQ( + "email 5", "mailtest@xn--mgbh0fb.xn--kgbechtv", + vmime::emailAddress("mailtest", "مثال.إختبار").generate() + ); + } + + void testGenerateEAI() { + + vmime::generationContext ctx(vmime::generationContext::getDefaultContext()); + ctx.setInternationalizedEmailSupport(true); + + vmime::generationContext::switcher <vmime::generationContext> contextSwitcher(ctx); + + VASSERT_EQ( + "email 1", "Pelé@example.com", + vmime::emailAddress("Pelé", "example.com").generate() + ); + VASSERT_EQ( + "email 2", "δοκιμή@παράδειγμα.δοκιμή", + vmime::emailAddress("δοκιμή", "παράδειγμα.δοκιμή").generate() + ); + VASSERT_EQ( + "email 3", "甲斐@黒川.日本", + vmime::emailAddress("甲斐", "黒川.日本").generate() + ); + VASSERT_EQ( + "email 4", "чебурашка@ящик-с-апельсинами.рф", + vmime::emailAddress("чебурашка", "ящик-с-апельсинами.рф").generate() + ); + } + + void testGenerateSpecialChars() { + + VASSERT_EQ( + "email 1", "\"very.unusual.@.unusual.com\"@example.com", + vmime::emailAddress("very.unusual.@.unusual.com", "example.com").generate() + ); + + VASSERT_EQ( + "email 2", "\"very.(),:;<>[]\\\".VERY.\\\"very@\\\\ \\\"very\\\".unusual\"@strange.example.com", + vmime::emailAddress("very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual", "strange.example.com").generate() + ); + + VASSERT_EQ( + "email 3", "\" \"@example.com", + vmime::emailAddress(" ", "example.com").generate() + ); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/emptyContentHandlerTest.cpp b/vmime-master/tests/parser/emptyContentHandlerTest.cpp new file mode 100644 index 0000000..43cd8c2 --- /dev/null +++ b/vmime-master/tests/parser/emptyContentHandlerTest.cpp @@ -0,0 +1,99 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" + + +VMIME_TEST_SUITE_BEGIN(emptyContentHandlerTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testIsEmpty) + VMIME_TEST(testGetLength) + VMIME_TEST(testIsEncoded) + VMIME_TEST(testExtract) + VMIME_TEST(testExtractRaw) + VMIME_TEST(testGenerate) + VMIME_TEST_LIST_END + + + void testIsEmpty() { + + vmime::emptyContentHandler cth; + + VASSERT_TRUE("empty", cth.isEmpty()); + } + + void testGetLength() { + + vmime::emptyContentHandler cth; + + VASSERT_EQ("length", 0, cth.getLength()); + } + + void testIsEncoded() { + + vmime::emptyContentHandler cth; + + VASSERT_FALSE("encoded", cth.isEncoded()); + VASSERT_EQ("encoding", vmime::contentHandler::NO_ENCODING, cth.getEncoding()); + } + + void testExtract() { + + vmime::emptyContentHandler cth; + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extract(osa); + + VASSERT_EQ("extract", "", oss.str()); + } + + void testExtractRaw() { + + vmime::emptyContentHandler cth; + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extractRaw(osa); + + VASSERT_EQ("extractRaw", "", oss.str()); + } + + void testGenerate() { + + vmime::emptyContentHandler cth; + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.generate(osa, vmime::encoding("base64")); + + VASSERT_EQ("generate", "", oss.str()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/fileContentHandlerTest.cpp b/vmime-master/tests/parser/fileContentHandlerTest.cpp new file mode 100644 index 0000000..e979b52 --- /dev/null +++ b/vmime-master/tests/parser/fileContentHandlerTest.cpp @@ -0,0 +1,134 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" + + +VMIME_TEST_SUITE_BEGIN(fileContentHandlerTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testIsEmpty) + VMIME_TEST(testGetLength) + VMIME_TEST(testIsEncoded) + VMIME_TEST(testExtract) + VMIME_TEST(testExtractRaw) + VMIME_TEST(testGenerate) + VMIME_TEST_LIST_END + + + vmime::shared_ptr <vmime::utility::file> testFile; + vmime::string testDataEncoded, testDataDecoded; + + + void setUp() { + + testDataDecoded = "ABCDEFGHIJKLMNOPQRSTUVWXYZ \x12\x34\x56\x78\x90 abcdefghijklmnopqrstuvwxyz0123456789"; + testDataEncoded = "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVogEjRWeJAgYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5"; + + std::ostringstream testFilePath; + testFilePath << "/tmp/vmime_test_" << (rand() % 999999999); + + vmime::shared_ptr <vmime::utility::fileSystemFactory> fsf = + vmime::platform::getHandler()->getFileSystemFactory(); + + testFile = fsf->create(fsf->stringToPath(testFilePath.str())); + testFile->createFile(); + testFile->getFileWriter()->getOutputStream()->write(testDataEncoded.data(), testDataEncoded.length()); + } + + void tearDown() { + + testFile->remove(); + testFile = vmime::null; + } + + + void testIsEmpty() { + + vmime::fileContentHandler cth; + + VASSERT_TRUE("empty", cth.isEmpty()); + } + + void testGetLength() { + + vmime::fileContentHandler cth(testFile); + + VASSERT_FALSE("empty", cth.isEmpty()); + VASSERT_EQ("length", testDataEncoded.length(), cth.getLength()); + } + + void testIsEncoded() { + + vmime::fileContentHandler cth(testFile, vmime::encoding("base64")); + + VASSERT_TRUE("encoded", cth.isEncoded()); + VASSERT_EQ("encoding", "base64", cth.getEncoding().generate()); + } + + void testExtract() { + + vmime::fileContentHandler cth(testFile, vmime::encoding("base64")); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extract(osa); + + // Data should be decoded from B64 + VASSERT_EQ("extract", testDataDecoded, oss.str()); + } + + void testExtractRaw() { + + vmime::fileContentHandler cth(testFile, vmime::encoding("base64")); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extractRaw(osa); + + // Data should not be decoded + VASSERT_EQ("extractRaw", testDataEncoded, oss.str()); + } + + void testGenerate() { + + vmime::fileContentHandler cth(testFile, vmime::encoding("base64")); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.generate(osa, vmime::encoding("quoted-printable")); + + // Data should be reencoded from B64 to QP + VASSERT_EQ( + "generate", + "ABCDEFGHIJKLMNOPQRSTUVWXYZ =124Vx=90 abcdefghijklmnopqrstuvwxyz0123456789", + oss.str() + ); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/headerFieldTest.cpp b/vmime-master/tests/parser/headerFieldTest.cpp new file mode 100644 index 0000000..78d0480 --- /dev/null +++ b/vmime-master/tests/parser/headerFieldTest.cpp @@ -0,0 +1,112 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(headerFieldTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testBadValueType) + VMIME_TEST(testValueOnNextLine) + VMIME_TEST(testStripSpacesAtEnd) + VMIME_TEST(testValueWithEmptyLine) + VMIME_TEST_LIST_END + + + void testBadValueType() { + + vmime::shared_ptr <vmime::headerFieldFactory> hfactory = + vmime::headerFieldFactory::getInstance(); + + // "To" header field accepts values of type "addressList" + vmime::shared_ptr <vmime::headerField> to = hfactory->create(vmime::fields::TO); + + VASSERT_THROW( + "to", + to->setValue(vmime::mailbox("email@vmime.org")), + vmime::exceptions::bad_field_value_type + ); + + // Unregistered header field accepts any value type + vmime::shared_ptr <vmime::headerField> custom = hfactory->create("X-MyCustomHeader"); + + VASSERT_NO_THROW( + "custom/1", + custom->setValue(vmime::mailbox("email@vmime.org")) + ); + VASSERT_NO_THROW( + "custom/2", + custom->setValue(vmime::text("field value text")) + ); + } + + void testValueOnNextLine() { + + vmime::parsingContext ctx; + + const vmime::string buffer = "Field: \r\n\tfield data"; + + vmime::shared_ptr <vmime::headerField> hfield = + vmime::headerField::parseNext(ctx, buffer, 0, buffer.size()); + + vmime::shared_ptr <vmime::text> hvalue = + hfield->getValue <vmime::text>(); + + VASSERT_EQ("Field name", "Field", hfield->getName()); + VASSERT_EQ("Field value", "field data", hvalue->getWholeBuffer()); + } + + void testStripSpacesAtEnd() { + + vmime::parsingContext ctx; + + const vmime::string buffer = "Field: \r\n\tfield data "; + + vmime::shared_ptr <vmime::headerField> hfield = + vmime::headerField::parseNext(ctx, buffer, 0, buffer.size()); + + vmime::shared_ptr <vmime::text> hvalue = + hfield->getValue <vmime::text>(); + + VASSERT_EQ("Field name", "Field", hfield->getName()); + VASSERT_EQ("Field value", toHex("field data"), toHex(hvalue->getWholeBuffer())); + } + + void testValueWithEmptyLine() { + + vmime::parsingContext ctx; + + const vmime::string buffer = "Field: \r\n\tdata1\r\n\tdata2\r\n\t\r\n\tdata3"; + + vmime::shared_ptr <vmime::headerField> hfield = + vmime::headerField::parseNext(ctx, buffer, 0, buffer.size()); + + vmime::shared_ptr <vmime::text> hvalue = + hfield->getValue <vmime::text>(); + + VASSERT_EQ("Field name", "Field", hfield->getName()); + VASSERT_EQ("Field value", "data1 data2 data3", hvalue->getWholeBuffer()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/headerTest.cpp b/vmime-master/tests/parser/headerTest.cpp new file mode 100644 index 0000000..839acee --- /dev/null +++ b/vmime-master/tests/parser/headerTest.cpp @@ -0,0 +1,375 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(headerTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testHas1) + VMIME_TEST(testHas2) + + VMIME_TEST(testAppend1) + VMIME_TEST(testAppend2) + + VMIME_TEST(testInsertFieldBefore1) + VMIME_TEST(testInsertFieldBefore2) + + VMIME_TEST(testInsertFieldAfter1) + VMIME_TEST(testInsertFieldAfter2) + + VMIME_TEST(testReplaceField) + + VMIME_TEST(testRemoveField1) + VMIME_TEST(testRemoveField2) + + VMIME_TEST(testRemoveAllFields) + + VMIME_TEST(testgetFieldCount) + + VMIME_TEST(testIsEmpty1) + VMIME_TEST(testIsEmpty2) + + VMIME_TEST(testGetFieldAt) + + VMIME_TEST(testGetFieldList1) + VMIME_TEST(testGetFieldList2) + + VMIME_TEST(testFind1) + + VMIME_TEST(testFindAllFields1) + VMIME_TEST(testFindAllFields2) + VMIME_TEST(testFindAllFields3) + VMIME_TEST_LIST_END + + + static const std::string getFieldValue(const vmime::headerField& field) { + + std::ostringstream oss; + vmime::utility::outputStreamAdapter voss(oss); + field.generate(voss); + + return oss.str(); + } + + // has function tests + void testHas1() { + + vmime::header hdr; + hdr.parse("From: x\r\nTo: y\r\nTo: z\r\n"); + + bool res = hdr.hasField("Z"); + + VASSERT_EQ("Value", false, res); + } + + void testHas2() { + + vmime::header hdr; + hdr.parse("X: x\r\nTo: y\r\nTo: z\r\n"); + + bool res = hdr.hasField("To"); + + VASSERT_EQ("Value", true, res); + } + + // appendField function tests + void testAppend1() { + + vmime::header hdr; + hdr.parse(""); + + vmime::shared_ptr <vmime::headerField> hf = vmime::headerFieldFactory::getInstance()->create("A", "a"); + hdr.appendField(hf); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(1), res.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res[0])); + } + + void testAppend2() { + + vmime::header hdr; + hdr.parse("A: a\r\n"); + + vmime::shared_ptr <vmime::headerField> hf = vmime::headerFieldFactory::getInstance()->create("B", "b"); + hdr.appendField(hf); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(2), res.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res[0])); + VASSERT_EQ("Second value", "B: b", headerTest::getFieldValue(*res[1])); + } + + // insertFieldBefore + void testInsertFieldBefore1() { + + vmime::header hdr; + hdr.parse("A: a\r\nC: c\r\n"); + + vmime::shared_ptr <vmime::headerField> hf = vmime::headerFieldFactory::getInstance()->create("B", "b"); + hdr.insertFieldBefore(hdr.getField("C"), hf); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(3), res.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res[0])); + VASSERT_EQ("Second value", "B: b", headerTest::getFieldValue(*res[1])); + VASSERT_EQ("Third value", "C: c", headerTest::getFieldValue(*res[2])); + } + + void testInsertFieldBefore2() { + + vmime::header hdr; + hdr.parse("A: a\r\nC: c\r\n"); + + vmime::shared_ptr <vmime::headerField> hf = vmime::headerFieldFactory::getInstance()->create("B", "b"); + hdr.insertFieldBefore(1, hf); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(3), res.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res[0])); + VASSERT_EQ("Second value", "B: b", headerTest::getFieldValue(*res[1])); + VASSERT_EQ("Third value", "C: c", headerTest::getFieldValue(*res[2])); + } + + // insertFieldAfter + void testInsertFieldAfter1() { + + vmime::header hdr; + hdr.parse("A: a\r\nC: c\r\n"); + + vmime::shared_ptr <vmime::headerField> hf = vmime::headerFieldFactory::getInstance()->create("B", "b"); + hdr.insertFieldAfter(hdr.getField("A"), hf); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(3), res.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res[0])); + VASSERT_EQ("Second value", "B: b", headerTest::getFieldValue(*res[1])); + VASSERT_EQ("Third value", "C: c", headerTest::getFieldValue(*res[2])); + } + + void testInsertFieldAfter2() { + + vmime::header hdr; + hdr.parse("A: a\r\nC: c\r\n"); + + vmime::shared_ptr <vmime::headerField> hf = vmime::headerFieldFactory::getInstance()->create("B", "b"); + hdr.insertFieldAfter(0, hf); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(3), res.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res[0])); + VASSERT_EQ("Second value", "B: b", headerTest::getFieldValue(*res[1])); + VASSERT_EQ("Third value", "C: c", headerTest::getFieldValue(*res[2])); + } + + // replaceField + void testReplaceField() { + + vmime::header hdr; + hdr.parse("A: a\r\nB: b\r\nC: c\r\n"); + + vmime::shared_ptr <vmime::headerField> hf = vmime::headerFieldFactory::getInstance()->create("Z", "z"); + hdr.replaceField(hdr.getField("B"), hf); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(3), res.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res[0])); + VASSERT_EQ("Second value", "Z: z", headerTest::getFieldValue(*res[1])); + VASSERT_EQ("Third value", "C: c", headerTest::getFieldValue(*res[2])); + } + + // removeField + void testRemoveField1() { + + vmime::header hdr1, hdr2; + hdr1.parse("A: a\r\nB: b\r\nC: c\r\n"); + hdr2.parse("A: a\r\nB: b\r\nC: c\r\n"); + + hdr1.removeField(hdr1.getField("B")); + hdr2.removeField(1); + + std::vector <vmime::shared_ptr <vmime::headerField> > res1 = hdr1.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(2), res1.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res1[0])); + VASSERT_EQ("Second value", "C: c", headerTest::getFieldValue(*res1[1])); + + std::vector <vmime::shared_ptr <vmime::headerField> > res2 = hdr2.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(2), res2.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res2[0])); + VASSERT_EQ("Second value", "C: c", headerTest::getFieldValue(*res2[1])); + } + + void testRemoveField2() { + + vmime::header hdr1, hdr2; + hdr1.parse("A: a\r\n"); + hdr2.parse("A: a\r\n"); + + hdr1.removeField(hdr1.getField("A")); + hdr2.removeField(0); + + std::vector <vmime::shared_ptr <vmime::headerField> > res1 = hdr1.getFieldList(); + VASSERT_EQ("Count", static_cast <unsigned int>(0), res1.size()); + + std::vector <vmime::shared_ptr <vmime::headerField> > res2 = hdr2.getFieldList(); + VASSERT_EQ("Count", static_cast <unsigned int>(0), res2.size()); + } + + // removeAllFields + void testRemoveAllFields() { + + vmime::header hdr1, hdr2; + hdr1.parse("A: a\r\n"); + hdr2.parse("A: a\r\nB: b\r\n"); + + hdr1.removeAllFields(); + hdr2.removeAllFields(); + + std::vector <vmime::shared_ptr <vmime::headerField> > res1 = hdr1.getFieldList(); + VASSERT_EQ("Count", static_cast <unsigned int>(0), res1.size()); + + std::vector <vmime::shared_ptr <vmime::headerField> > res2 = hdr2.getFieldList(); + VASSERT_EQ("Count", static_cast <unsigned int>(0), res2.size()); + } + + // getFieldCount + void testgetFieldCount() { + + vmime::header hdr; + hdr.parse("A: a\r\nB: b\r\nC: c\r\nD: d\r\n"); + + VASSERT_EQ("Value", 4, hdr.getFieldCount()); + } + + // isEmpty + void testIsEmpty1() { + + vmime::header hdr; + hdr.parse("A: a\r\nB: b\r\nC: c\r\n"); + + VASSERT_EQ("Value", false, hdr.isEmpty()); + } + + void testIsEmpty2() { + + vmime::header hdr; + hdr.parse("\r\n"); + + VASSERT_EQ("Value", true, hdr.isEmpty()); + } + + // getFieldAt + void testGetFieldAt() { + + vmime::header hdr; + hdr.parse("B: b\r\nA: a\r\nC: c\r\n"); + + vmime::shared_ptr <vmime::headerField> res = hdr.getFieldAt(2); + + VASSERT_EQ("Value", "C: c", getFieldValue(*res)); + } + + // getFieldList + void testGetFieldList1() { + + vmime::header hdr; + hdr.parse("A: a\r\nB: b1\r\nC: c\r\nB: b2\r\n"); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(4), res.size()); + VASSERT_EQ("First value", "A: a", headerTest::getFieldValue(*res[0])); + VASSERT_EQ("Second value", "B: b1", headerTest::getFieldValue(*res[1])); + VASSERT_EQ("Third value", "C: c", headerTest::getFieldValue(*res[2])); + VASSERT_EQ("Thourth value", "B: b2", headerTest::getFieldValue(*res[3])); + } + + void testGetFieldList2() { + + vmime::header hdr; + hdr.parse("\r\n"); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.getFieldList(); + + VASSERT_EQ("Count", static_cast <unsigned int>(0), res.size()); + } + + // find function tests + void testFind1() { + + vmime::header hdr; + hdr.parse("A: a\r\nB: b\r\nC: c\r\nB: d\r\n"); + + vmime::shared_ptr <vmime::headerField> res = hdr.findField("B"); + + VASSERT_EQ("Value", "B: b", getFieldValue(*res)); + } + + // getAllByName function tests + void testFindAllFields1() { + + vmime::header hdr; + hdr.parse("A: a1\nC: c1\n"); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.findAllFields("B"); + + VASSERT_EQ("Count", static_cast <unsigned int>(0), res.size()); + } + + void testFindAllFields2() { + + vmime::header hdr; + hdr.parse("A: a1\nB: b1\nB: b2\nC: c1\n"); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.findAllFields("B"); + + VASSERT_EQ("Count", static_cast <unsigned int>(2), res.size()); + VASSERT_EQ("First value", "B: b1", headerTest::getFieldValue(*res[0])); + VASSERT_EQ("Second value", "B: b2", headerTest::getFieldValue(*res[1])); + } + + void testFindAllFields3() { + + vmime::header hdr; + hdr.parse("A: a1\nB: b1\nB: b2\nC: c1\nC: c3\nC: c2\n"); + + std::vector <vmime::shared_ptr <vmime::headerField> > res = hdr.findAllFields("C"); + + VASSERT_EQ("Count", static_cast <unsigned int>(3), res.size()); + VASSERT_EQ("First value", "C: c1", headerTest::getFieldValue(*res[0])); + VASSERT_EQ("Second value", "C: c3", headerTest::getFieldValue(*res[1])); + VASSERT_EQ("Second value", "C: c2", headerTest::getFieldValue(*res[2])); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/htmlTextPartTest.cpp b/vmime-master/tests/parser/htmlTextPartTest.cpp new file mode 100644 index 0000000..6dfaaa0 --- /dev/null +++ b/vmime-master/tests/parser/htmlTextPartTest.cpp @@ -0,0 +1,241 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" +#include "vmime/htmlTextPart.hpp" + + +VMIME_TEST_SUITE_BEGIN(htmlTextPartTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParseText) + VMIME_TEST(testParseEmbeddedObjectsCID) + VMIME_TEST(testParseEmbeddedObjectsLocation) + VMIME_TEST_LIST_END + + + static const vmime::string extractContent( + const vmime::shared_ptr <const vmime::contentHandler>& cth + ) { + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth->extract(osa); + + return oss.str(); + } + + + void testParseText() { + + const vmime::string msgString = "" +"MIME-Version: 1.0\r\n" +"Content-Type: multipart/alternative; boundary=\"LEVEL1\"\r\n" +"\r\n" +"--LEVEL1\r\n" +"Content-Type: text/plain; charset=\"x-ch1\"\r\n" +"\r\n" +"Plain text part\r\n" +"--LEVEL1\r\n" +"Content-Type: multipart/related; boundary=\"LEVEL2\"\r\n" +"\r\n" +"--LEVEL2\r\n" +"Content-Type: text/html; charset=\"x-ch2\"\r\n" +"\r\n" +"<img src=\"cid:image@test\"/>\r\n" +"--LEVEL2\r\n" +"Content-Type: image/png; name=\"image.png\"\r\n" +"Content-Disposition: inline; filename=\"image.png\"\r\n" +"Content-ID: <image@test>\r\n" +"\r\n" +"Image\r\n" +"--LEVEL2--\r\n" +"\r\n" +"--LEVEL1--\r\n" +""; + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->parse(msgString); + + // Sanity checks + VASSERT_EQ("part-count1", 2, msg->getBody()->getPartCount()); + VASSERT_EQ("part-count2", 2, msg->getBody()->getPartAt(1)->getBody()->getPartCount()); + + vmime::htmlTextPart htmlPart; + htmlPart.parse(msg, msg->getBody()->getPartAt(1), + msg->getBody()->getPartAt(1)->getBody()->getPartAt(0)); + + VASSERT_EQ("plain", "Plain text part", extractContent(htmlPart.getPlainText())); + VASSERT_EQ("html", "<img src=\"cid:image@test\"/>", extractContent(htmlPart.getText())); + + // Should return the charset of the HTML part + VASSERT_EQ("charset", "x-ch2", htmlPart.getCharset().generate()); + } + + /** Test parsing of embedded objects by CID (Content-Id). + */ + void testParseEmbeddedObjectsCID() { + + const vmime::string msgString = "" +"MIME-Version: 1.0\r\n" +"Content-Type: multipart/alternative; boundary=\"LEVEL1\"\r\n" +"\r\n" +"--LEVEL1\r\n" +"Content-Type: text/plain; charset=\"x-ch1\"\r\n" +"\r\n" +"Plain text part\r\n" +"--LEVEL1\r\n" +"Content-Type: multipart/related; boundary=\"LEVEL2\"\r\n" +"\r\n" +"--LEVEL2\r\n" // one embedded object before... +"Content-Type: image/png; name=\"image1.png\"\r\n" +"Content-Disposition: inline; filename=\"image1.png\"\r\n" +"Content-ID: <image1@test>\r\n" +"\r\n" +"Image1\r\n" +"--LEVEL2\r\n" // ...the actual text part... +"Content-Type: text/html; charset=\"x-ch2\"\r\n" +"\r\n" +"<img src=\"cid:image1@test\"/>\r\n" +"<img src=\"CID:image2@test\"/>\r\n" +"--LEVEL2\r\n" // ...and one after +"Content-Type: image/jpeg; name=\"image2.jpg\"\r\n" +"Content-Disposition: inline; filename=\"image2.jpg\"\r\n" +"Content-ID: <image2@test>\r\n" +"\r\n" +"Image2\r\n" +"--LEVEL2--\r\n" +"\r\n" +"--LEVEL1--\r\n" +""; + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->parse(msgString); + + // Sanity checks + VASSERT_EQ("part-count1", 2, msg->getBody()->getPartCount()); + VASSERT_EQ("part-count2", 3, msg->getBody()->getPartAt(1)->getBody()->getPartCount()); + + vmime::htmlTextPart htmlPart; + htmlPart.parse( + msg, msg->getBody()->getPartAt(1), + msg->getBody()->getPartAt(1)->getBody()->getPartAt(1) + ); + + // Two embedded objects should be found. + // BUGFIX: "CID:" prefix is not case-sensitive. + VASSERT_EQ("count", 2, htmlPart.getObjectCount()); + + // Ensure the right objects have been found. + VASSERT_EQ("has-obj1", true, htmlPart.hasObject("image1@test")); + VASSERT_EQ("has-obj2", true, htmlPart.hasObject("image2@test")); + + // hasObject() should also work with prefixes + VASSERT_EQ("has-obj1-pre", true, htmlPart.hasObject("CID:image1@test")); + VASSERT_EQ("has-obj2-pre", true, htmlPart.hasObject("cid:image2@test")); + + // Check data in objects + vmime::shared_ptr <const vmime::htmlTextPart::embeddedObject> obj; + + obj = htmlPart.findObject("image1@test"); + + VASSERT_EQ("ref-type1", vmime::htmlTextPart::embeddedObject::REFERENCED_BY_ID, obj->getReferenceType()); + VASSERT_EQ("id-obj1", "image1@test", obj->getId()); + VASSERT_EQ("data-obj1", "Image1", extractContent(obj->getData())); + VASSERT_EQ("type-obj1", "image/png", obj->getType().generate()); + + obj = htmlPart.findObject("image2@test"); + + VASSERT_EQ("ref-type2", vmime::htmlTextPart::embeddedObject::REFERENCED_BY_ID, obj->getReferenceType()); + VASSERT_EQ("id-obj2", "image2@test", obj->getId()); + VASSERT_EQ("data-obj2", "Image2", extractContent(obj->getData())); + VASSERT_EQ("type-obj2", "image/jpeg", obj->getType().generate()); + } + + /** Test parsing of embedded objects by location. + */ + void testParseEmbeddedObjectsLocation() { + + const vmime::string msgString = "" +"MIME-Version: 1.0\r\n" +"Content-Type: multipart/alternative; boundary=\"LEVEL1\"\r\n" +"\r\n" +"--LEVEL1\r\n" +"Content-Type: text/plain; charset=\"x-ch1\"\r\n" +"\r\n" +"Plain text part\r\n" +"--LEVEL1\r\n" +"Content-Type: multipart/related; boundary=\"LEVEL2\"\r\n" +"\r\n" +"--LEVEL2\r\n" +"Content-Type: image/png; name=\"image1.png\"\r\n" +"Content-Location: http://www.vmime.org/test/image1.png\r\n" +"Content-Disposition: inline; filename=\"image1.png\"\r\n" +"Content-Id: <image1@test>\r\n" +"\r\n" +"Image1\r\n" +"--LEVEL2\r\n" +"Content-Type: text/html; charset=\"x-ch2\"\r\n" +"\r\n" +"<img src=\"http://www.vmime.org/test/image1.png\"/>\r\n" +"--LEVEL2--\r\n" +"\r\n" +"--LEVEL1--\r\n" +""; + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->parse(msgString); + + // Sanity checks + VASSERT_EQ("part-count1", 2, msg->getBody()->getPartCount()); + VASSERT_EQ("part-count2", 2, msg->getBody()->getPartAt(1)->getBody()->getPartCount()); + + vmime::htmlTextPart htmlPart; + htmlPart.parse( + msg, msg->getBody()->getPartAt(1), + msg->getBody()->getPartAt(1)->getBody()->getPartAt(1) + ); + + // Only one embedded object + VASSERT_EQ("count", 1, htmlPart.getObjectCount()); + + // Should work only with Content-Location as the Content-Id is + // not referenced in the HTML contents + VASSERT_EQ("has-obj-loc", true, htmlPart.hasObject("http://www.vmime.org/test/image1.png")); + VASSERT_EQ("has-obj-cid", false, htmlPart.hasObject("image1@test")); + + // Check data + vmime::shared_ptr <const vmime::htmlTextPart::embeddedObject> obj; + + obj = htmlPart.findObject("http://www.vmime.org/test/image1.png"); + + VASSERT_EQ("ref-type", vmime::htmlTextPart::embeddedObject::REFERENCED_BY_LOCATION, obj->getReferenceType()); + VASSERT_EQ("id-obj", "http://www.vmime.org/test/image1.png", obj->getId()); + VASSERT_EQ("data-obj", "Image1", extractContent(obj->getData())); + VASSERT_EQ("type-obj", "image/png", obj->getType().generate()); + } + + // TODO: test generation of text parts + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/mailboxGroupTest.cpp b/vmime-master/tests/parser/mailboxGroupTest.cpp new file mode 100644 index 0000000..18ff425 --- /dev/null +++ b/vmime-master/tests/parser/mailboxGroupTest.cpp @@ -0,0 +1,107 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(mailboxGroupTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParseExtraWhitespaces) + VMIME_TEST(testParseNoEndDelimiter) + VMIME_TEST(testParseExtraChars) + VMIME_TEST(testEmptyGroup) + VMIME_TEST(testEncodedEmptyGroup) + VMIME_TEST_LIST_END + + + void testParseExtraWhitespaces() { + + vmime::mailboxGroup mgrp; + mgrp.parse(" \t group : aaa <aaa@vmime.org>, bbb <bbb@vmime.org>"); + + VASSERT_EQ("name", "group", mgrp.getName().getWholeBuffer()); + VASSERT_EQ("count", 2, mgrp.getMailboxCount()); + + VASSERT_EQ("mbox1.email", "aaa@vmime.org", mgrp.getMailboxAt(0)->getEmail()); + VASSERT_EQ("mbox1.name", "aaa", mgrp.getMailboxAt(0)->getName()); + + VASSERT_EQ("mbox2.email", "bbb@vmime.org", mgrp.getMailboxAt(1)->getEmail()); + VASSERT_EQ("mbox2.name", "bbb", mgrp.getMailboxAt(1)->getName()); + } + + void testParseNoEndDelimiter() { + + vmime::addressList addrs; + addrs.parse("group: aaa <aaa@vmime.org>, bbb <bbb@vmime.org>"); + + VASSERT_EQ("count", 1, addrs.getAddressCount()); + VASSERT_TRUE("is group", addrs.getAddressAt(0)->isGroup()); + + vmime::shared_ptr <vmime::mailboxGroup> mgrp = + vmime::dynamicCast <vmime::mailboxGroup>(addrs.getAddressAt(0)); + + VASSERT_EQ("name", "group", mgrp->getName().getWholeBuffer()); + VASSERT_EQ("count", 2, mgrp->getMailboxCount()); + + VASSERT_EQ("mbox1.email", "aaa@vmime.org", mgrp->getMailboxAt(0)->getEmail()); + VASSERT_EQ("mbox1.name", "aaa", mgrp->getMailboxAt(0)->getName()); + + VASSERT_EQ("mbox2.email", "bbb@vmime.org", mgrp->getMailboxAt(1)->getEmail()); + VASSERT_EQ("mbox2.name", "bbb", mgrp->getMailboxAt(1)->getName()); + } + + void testParseExtraChars() { + + vmime::mailboxGroup mgrp; + mgrp.parse("group: aaa <aaa@vmime.org>, bbb <bbb@vmime.org>; extra chars here..."); + + VASSERT_EQ("name", "group", mgrp.getName().getWholeBuffer()); + VASSERT_EQ("count", 2, mgrp.getMailboxCount()); + + VASSERT_EQ("mbox1.email", "aaa@vmime.org", mgrp.getMailboxAt(0)->getEmail()); + VASSERT_EQ("mbox1.name", "aaa", mgrp.getMailboxAt(0)->getName()); + + VASSERT_EQ("mbox2.email", "bbb@vmime.org", mgrp.getMailboxAt(1)->getEmail()); + VASSERT_EQ("mbox2.name", "bbb", mgrp.getMailboxAt(1)->getName()); + } + + void testEmptyGroup() { + + vmime::mailboxGroup mgrp; + mgrp.parse("Undisclosed recipients:;"); + + VASSERT_EQ("name", "Undisclosed recipients", mgrp.getName().getWholeBuffer()); + VASSERT_EQ("count", 0, mgrp.getMailboxCount()); + } + + void testEncodedEmptyGroup() { + + vmime::mailboxGroup mgrp; + mgrp.parse("=?us-ascii?Q?Undisclosed_recipients?=:;"); + + VASSERT_EQ("name", "Undisclosed recipients", mgrp.getName().getWholeBuffer()); + VASSERT_EQ("count", 0, mgrp.getMailboxCount()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/mailboxListTest.cpp b/vmime-master/tests/parser/mailboxListTest.cpp new file mode 100644 index 0000000..7505acd --- /dev/null +++ b/vmime-master/tests/parser/mailboxListTest.cpp @@ -0,0 +1,47 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(mailboxListTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParseGroup) + VMIME_TEST_LIST_END + + + // Disposition-Notification-To:: <email@domain.com> + void testParseGroup() { + + // Groups should be extracted to multiple mailboxes in mailbox lists + vmime::mailboxList mboxList; + mboxList.parse("email1@domain1.com, : <email2@domain2.com>, email3@domain3.com"); + + VASSERT_EQ("count", 3, mboxList.getMailboxCount()); + VASSERT_EQ("email", "email1@domain1.com", mboxList.getMailboxAt(0)->getEmail().generate()); + VASSERT_EQ("email", "email2@domain2.com", mboxList.getMailboxAt(1)->getEmail().generate()); + VASSERT_EQ("email", "email3@domain3.com", mboxList.getMailboxAt(2)->getEmail().generate()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/mailboxTest.cpp b/vmime-master/tests/parser/mailboxTest.cpp new file mode 100644 index 0000000..997a6a3 --- /dev/null +++ b/vmime-master/tests/parser/mailboxTest.cpp @@ -0,0 +1,187 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(mailboxTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParse) + VMIME_TEST(testEmptyEmailAddress) + VMIME_TEST(testSeparatorInComment) + VMIME_TEST(testMalformations) + VMIME_TEST(testExcessiveQuoting) + VMIME_TEST_LIST_END + + + void testParse() { + + static const vmime::string testSuitesParse[] = { + + // Test 1 + "My (this is a comment)name <me(another \\)comment) @ somewhere(else).com>", + + "[address-list: [[mailbox: name=[text: [[word: charset=us-ascii, buffer=My name]]], email=me@somewhere.com]]]", + + // Test 2 + "mailbox1 <mailbox@one>,;,,, ,, ,,;group1:mailbox1@group1, mailbox2@group2,,\"mailbox #3\" <mailbox3@group2>;, <mailbox@two>,,,,,,,,=?iso-8859-1?q?mailbox_number_3?= <mailbox@three>, =?abc?Q?mailbox?= =?def?Q?_number_4?= <mailbox@four>", + + "[address-list: [[mailbox: name=[text: [[word: charset=us-ascii, buffer=mailbox1]]], email=mailbox@one],[mailbox-group: name=[text: [[word: charset=us-ascii, buffer=group1]]], list=[[mailbox: name=[text: []], email=mailbox1@group1],[mailbox: name=[text: []], email=mailbox2@group2],[mailbox: name=[text: [[word: charset=us-ascii, buffer=mailbox #3]]], email=mailbox3@group2]]],[mailbox: name=[text: []], email=mailbox@two],[mailbox: name=[text: [[word: charset=iso-8859-1, buffer=mailbox number 3]]], email=mailbox@three],[mailbox: name=[text: [[word: charset=abc, buffer=mailbox],[word: charset=def, buffer= number 4]]], email=mailbox@four]]]", + + // Test 3 + "John Doe <john.doe@acme.com>", + + "[address-list: [[mailbox: name=[text: [[word: charset=us-ascii, buffer=John Doe]]], email=john.doe@acme.com]]]", + + // Test 4 + "john.doe@acme.com (John Doe)", + + "[address-list: [[mailbox: name=[text: []], email=john.doe@acme.com]]]", + + // Test 5 + "John.Doe(ignore)@acme.com (John Doe)", + + "[address-list: [[mailbox: name=[text: []], email=John.Doe@acme.com]]]", + + // Test 6 + "<john.doe@acme.com>", + + "[address-list: [[mailbox: name=[text: []], email=john.doe@acme.com]]]", + + // Test 7 + "john.doe@acme.com", + + "[address-list: [[mailbox: name=[text: []], email=john.doe@acme.com]]]", + + // Test 8 + "\"John Doe\" <john.doe@acme.com>", + + "[address-list: [[mailbox: name=[text: [[word: charset=us-ascii, buffer=John Doe]]], email=john.doe@acme.com]]]", + + // Test 9 + "=?us-ascii?q?John?=<john.doe@acme.com>", + + "[address-list: [[mailbox: name=[text: [[word: charset=us-ascii, buffer=John]]], email=john.doe@acme.com]]]", + + // Test 10 + "\"John\"<john.doe@acme.com>", + + "[address-list: [[mailbox: name=[text: [[word: charset=us-ascii, buffer=John]]], email=john.doe@acme.com]]]", + + // Test 11 + "John<john.doe@acme.com>", + + "[address-list: [[mailbox: name=[text: [[word: charset=us-ascii, buffer=John]]], email=john.doe@acme.com]]]" + }; + + for (unsigned int i = 0 ; i < sizeof(testSuitesParse) / sizeof(testSuitesParse[0]) / 2 ; ++i) { + + vmime::string in = testSuitesParse[i * 2]; + vmime::string out = testSuitesParse[i * 2 + 1]; + + std::ostringstream oss; + oss << "Test " << (i + 1); + + vmime::addressList addrList; + addrList.parse(in); + + std::ostringstream cmp; + cmp << addrList; + + VASSERT_EQ(oss.str(), out, cmp.str()); + } + } + + void testEmptyEmailAddress() { + + vmime::addressList addrList; + addrList.parse("\"Full Name\" <>"); + + VASSERT_EQ("count", 1, addrList.getAddressCount()); + VASSERT_EQ("!group", false, addrList.getAddressAt(0)->isGroup()); + + vmime::shared_ptr <vmime::mailbox> mbox = + vmime::dynamicCast <vmime::mailbox>(addrList.getAddressAt(0)); + + VASSERT_EQ("name", "Full Name", mbox->getName()); + VASSERT_EQ("email", "", mbox->getEmail()); + } + + void testSeparatorInComment() { + + vmime::addressList addrList; + addrList.parse("aaa(comment,comment)@vmime.org, bbb@vmime.org"); + + VASSERT_EQ("count", 2, addrList.getAddressCount()); + + vmime::shared_ptr <vmime::mailbox> mbox1 = + vmime::dynamicCast <vmime::mailbox>(addrList.getAddressAt(0)); + vmime::shared_ptr <vmime::mailbox> mbox2 = + vmime::dynamicCast <vmime::mailbox>(addrList.getAddressAt(1)); + + VASSERT_EQ("name1", vmime::text(), mbox1->getName()); + VASSERT_EQ("email1", "aaa@vmime.org", mbox1->getEmail()); + + VASSERT_EQ("name2", vmime::text(), mbox2->getName()); + VASSERT_EQ("email2", "bbb@vmime.org", mbox2->getEmail()); + } + + void testMalformations() { + vmime::mailbox mbox; + + mbox.parse("a@b.c <e@f.g>"); + VASSERT_EQ("name", vmime::text("a@b.c"), mbox.getName()); + VASSERT_EQ("email", "e@f.g", mbox.getEmail()); + + mbox.parse("a@b.c e@f.g <h@i.j>"); + VASSERT_EQ("name", vmime::text("a@b.c e@f.g"), mbox.getName()); + VASSERT_EQ("email", "h@i.j", mbox.getEmail()); + + mbox.parse("Foo <bar<baz@quux.com>"); + VASSERT_EQ("name", vmime::text("Foo <bar"), mbox.getName()); + VASSERT_EQ("email", "baz@quux.com", mbox.getEmail()); + + mbox.parse("Foo <foo@x.com> <bar@x.com>"); + VASSERT_EQ("name", vmime::text("Foo <foo@x.com>"), mbox.getName()); + VASSERT_EQ("email", "bar@x.com", mbox.getEmail()); + + mbox.parse("Foo <foo@x.com> Bar <bar@y.com>"); + VASSERT_EQ("name", vmime::text("Foo <foo@x.com> Bar"), mbox.getName()); + VASSERT_EQ("email", "bar@y.com", mbox.getEmail()); + } + + void testExcessiveQuoting() { + using namespace vmime; + + // Check that ASCII display names are not encoded more than necessary + emailAddress e("a@b.com"); + auto a = make_shared<mailbox>(text(word("Foo B@r", charsets::US_ASCII)), e); + VASSERT_EQ("generate", "\"Foo B@r\" <a@b.com>", a->generate()); + VASSERT_NEQ("generate", "=?utf-8?Q?Foo_B=40r?= <a@b.com>", a->generate()); + + a = make_shared<mailbox>(text(word("Foo B@r", charsets::UTF_8)), e); + VASSERT_EQ("generate", "=?utf-8?Q?Foo_B=40r?= <a@b.com>", a->generate()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/mediaTypeTest.cpp b/vmime-master/tests/parser/mediaTypeTest.cpp new file mode 100644 index 0000000..e98cfcd --- /dev/null +++ b/vmime-master/tests/parser/mediaTypeTest.cpp @@ -0,0 +1,100 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(mediaTypeTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testConstructors) + VMIME_TEST(testCopy) + VMIME_TEST(testSetFromString) + VMIME_TEST(testParse) + VMIME_TEST(testGenerate) + VMIME_TEST_LIST_END + + + void testConstructors() { + + vmime::mediaType t1; + + VASSERT_EQ("1.1", vmime::mediaTypes::APPLICATION, t1.getType()); + VASSERT_EQ("1.2", vmime::mediaTypes::APPLICATION_OCTET_STREAM, t1.getSubType()); + + vmime::mediaType t2("type", "sub"); + + VASSERT_EQ("2.1", "type", t2.getType()); + VASSERT_EQ("2.2", "sub", t2.getSubType()); + + vmime::mediaType t3("type/sub"); + + VASSERT_EQ("3.1", "type", t3.getType()); + VASSERT_EQ("3.2", "sub", t3.getSubType()); + } + + void testCopy() { + + vmime::mediaType t1("type/sub"); + + VASSERT_EQ("eq1", "type", t1.getType()); + VASSERT_EQ("eq2", "sub", t1.getSubType()); + + VASSERT("operator==", t1 == t1); + VASSERT("clone", t1 == *vmime::clone(t1)); + + VASSERT_EQ("eq3", "type", vmime::clone(t1)->getType()); + VASSERT_EQ("eq4", "sub", vmime::clone(t1)->getSubType()); + + vmime::mediaType t2; + t2.copyFrom(t1); + + VASSERT("copyFrom", t1 == t2); + } + + void testSetFromString() { + + vmime::mediaType t1; + t1.setFromString("type/sub"); + + VASSERT_EQ("1.1", "type", t1.getType()); + VASSERT_EQ("1.2", "sub", t1.getSubType()); + } + + void testParse() { + + vmime::mediaType t1; + t1.parse("type/sub"); + + VASSERT_EQ("1.1", "type", t1.getType()); + VASSERT_EQ("1.2", "sub", t1.getSubType()); + } + + void testGenerate() { + + vmime::mediaType t1("type", "sub"); + + VASSERT_EQ("1", "type/sub", t1.generate()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/messageIdSequenceTest.cpp b/vmime-master/tests/parser/messageIdSequenceTest.cpp new file mode 100644 index 0000000..0237108 --- /dev/null +++ b/vmime-master/tests/parser/messageIdSequenceTest.cpp @@ -0,0 +1,78 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(messageIdSequenceTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParse) + VMIME_TEST(testGenerate) + VMIME_TEST_LIST_END + + + void testParse() { + + vmime::messageIdSequence s1; + s1.parse(""); + + VASSERT_EQ("1", 0, s1.getMessageIdCount()); + + vmime::messageIdSequence s2; + s2.parse(" \t "); + + VASSERT_EQ("2", 0, s2.getMessageIdCount()); + + vmime::messageIdSequence s3; + s3.parse("<a@b>"); + + VASSERT_EQ("3.1", 1, s3.getMessageIdCount()); + VASSERT_EQ("3.2", "a", s3.getMessageIdAt(0)->getLeft()); + VASSERT_EQ("3.3", "b", s3.getMessageIdAt(0)->getRight()); + + vmime::messageIdSequence s4; + s4.parse("<a@b> \r\n\t<c@d>"); + + VASSERT_EQ("4.1", 2, s4.getMessageIdCount()); + VASSERT_EQ("4.2", "a", s4.getMessageIdAt(0)->getLeft()); + VASSERT_EQ("4.3", "b", s4.getMessageIdAt(0)->getRight()); + VASSERT_EQ("4.4", "c", s4.getMessageIdAt(1)->getLeft()); + VASSERT_EQ("4.5", "d", s4.getMessageIdAt(1)->getRight()); + } + + void testGenerate() { + + vmime::messageIdSequence s1; + s1.appendMessageId(vmime::make_shared <vmime::messageId>("a", "b")); + + VASSERT_EQ("1", "<a@b>", s1.generate()); + + vmime::messageIdSequence s2; + s2.appendMessageId(vmime::make_shared <vmime::messageId>("a", "b")); + s2.appendMessageId(vmime::make_shared <vmime::messageId>("c", "d")); + + VASSERT_EQ("2", "<a@b> <c@d>", s2.generate()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/messageIdTest.cpp b/vmime-master/tests/parser/messageIdTest.cpp new file mode 100644 index 0000000..8433093 --- /dev/null +++ b/vmime-master/tests/parser/messageIdTest.cpp @@ -0,0 +1,77 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(messageIdTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParse) + VMIME_TEST(testParseInvalid) + VMIME_TEST(testGenerate) + VMIME_TEST_LIST_END + + + void testParse() { + + vmime::messageId m1; + m1.parse("<a@b>"); + + VASSERT_EQ("1.1", "a", m1.getLeft()); + VASSERT_EQ("1.2", "b", m1.getRight()); + } + + void testParseInvalid() { + + vmime::messageId m1; + m1.parse("foo@bar"); + + VASSERT_EQ("1.1", "foo", m1.getLeft()); + VASSERT_EQ("1.2", "bar", m1.getRight()); + } + + void testGenerate() { + + vmime::messageId m1; + + VASSERT_EQ("1", "<>", m1.generate()); + + vmime::messageId m2; + m2.setLeft("a"); + + VASSERT_EQ("2", "<a>", m2.generate()); + + vmime::messageId m3; + m3.setRight("b"); + + VASSERT_EQ("3", "<@b>", m3.generate()); + + vmime::messageId m4; + m4.setLeft("a"); + m4.setRight("b"); + + VASSERT_EQ("4", "<a@b>", m4.generate()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/messageTest.cpp b/vmime-master/tests/parser/messageTest.cpp new file mode 100644 index 0000000..b89d63e --- /dev/null +++ b/vmime-master/tests/parser/messageTest.cpp @@ -0,0 +1,64 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(messageTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testGetGeneratedSize) + VMIME_TEST_LIST_END + + + void testGetGeneratedSize() { + + vmime::generationContext ctx; + + vmime::shared_ptr <vmime::message> msg = vmime::make_shared <vmime::message>(); + msg->getHeader()->getField("Foo")->setValue(vmime::string("bar")); + + vmime::htmlTextPart textPart; + textPart.setPlainText( + vmime::make_shared <vmime::stringContentHandler>( + "Foo bar bazé foo foo foo" + ) + ); + textPart.setText( + vmime::make_shared <vmime::stringContentHandler>( + "Foo bar <strong>bazé</strong> foo foo foo" + ) + ); + textPart.generateIn(msg, msg); + + // Estimated/computed generated size must be greater than the actual generated size + const vmime::size_t genSize = msg->getGeneratedSize(ctx); + const vmime::size_t actualSize = msg->generate().length(); + + std::ostringstream oss; + oss << "estimated size (" << genSize << ") >= actual size (" << actualSize << ")"; + + VASSERT(oss.str(), genSize >= actualSize); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/parameterTest.cpp b/vmime-master/tests/parser/parameterTest.cpp new file mode 100644 index 0000000..464c305 --- /dev/null +++ b/vmime-master/tests/parser/parameterTest.cpp @@ -0,0 +1,722 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include <locale> +#include <clocale> + + +VMIME_TEST_SUITE_BEGIN(parameterTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParse) + VMIME_TEST(testParseRFC2231) + VMIME_TEST(testGenerate) + VMIME_TEST(testGetGeneratedSize) + VMIME_TEST(testGenerateRFC2231) + VMIME_TEST(testGetGeneratedSizeRFC2231) + VMIME_TEST(testNonStandardEncodedParam) + VMIME_TEST(testParseNonSignificantWS) + VMIME_TEST(testEncodeTSpecials) + VMIME_TEST(testEncodeTSpecialsInRFC2231) + VMIME_TEST(testWhitespaceBreaksTheValue) + VMIME_TEST_LIST_END + + + // HACK: parameterizedHeaderField constructor is private + class parameterizedHeaderField : public vmime::parameterizedHeaderField { + + public: + + parameterizedHeaderField() { + + setName("F"); + setValue(vmime::headerFieldFactory::getInstance()->createValue(getName())); + setValue(vmime::word("X")); + } + + using vmime::parameterizedHeaderField::generate; + + const vmime::string generate( + const vmime::generationContext::EncodedParameterValueModes genMode, + const vmime::size_t maxLineLength = 0 + ) const { + + vmime::generationContext ctx(vmime::generationContext::getDefaultContext()); + ctx.setEncodedParameterValueMode(genMode); + + if (maxLineLength != 0) { + ctx.setMaxLineLength(maxLineLength); + } + + std::ostringstream oss; + vmime::utility::outputStreamAdapter adapter(oss); + + vmime::parameterizedHeaderField::generate(ctx, adapter); + + return oss.str(); + } + }; + + + const vmime::string generateParameter( + const vmime::parameter& param, + const vmime::generationContext& ctx, + const vmime::size_t maxLineLength = 0 + ) const { + + vmime::generationContext ctx2(ctx); + + if (maxLineLength != 0) { + ctx2.setMaxLineLength(maxLineLength); + } + + std::ostringstream oss; + vmime::utility::outputStreamAdapter adapter(oss); + + param.generate(ctx2, adapter); + + return oss.str(); + } + + +#define FIELD_VALUE(f) (f.getValue()->generate()) +#define PARAM_VALUE(p, n) (p.getParameterAt(n)->getValue().generate()) +#define PARAM_NAME(p, n) (p.getParameterAt(n)->getName()) +#define PARAM_CHARSET(p, n) \ + (p.getParameterAt(n)->getValue().getCharset().generate()) +#define PARAM_LANG(p, n) \ + (p.getParameterAt(n)->getValue().getLanguage()) +#define PARAM_BUFFER(p, n) \ + (p.getParameterAt(n)->getValue().getBuffer()) + + + void setUp() { + + // Set the global C and C++ locale to the user-configured locale. + // The locale should use UTF-8 encoding for these tests to run successfully. + try { + std::locale::global(std::locale("")); + } catch (std::exception &) { + std::setlocale(LC_ALL, ""); + } + } + + void tearDown() { + + // Restore default locale + std::locale::global(std::locale("C")); + } + + + void testParse() { + + // Simple parameter + parameterizedHeaderField p1; + p1.parse("X; param1=value1;\r\n"); + + VASSERT_EQ("1.1", 1, p1.getParameterCount()); + VASSERT_EQ("1.2", "param1", PARAM_NAME(p1, 0)); + VASSERT_EQ("1.3", "value1", PARAM_VALUE(p1, 0)); + + // Multi-section parameters (1/2) + parameterizedHeaderField p2a; + p2a.parse( + "X; param1=value1;\r\n" + " param2*0=\"val\";\r\n" + " param2*1=\"ue2\";" + ); + + VASSERT_EQ("2a.1", 2, p2a.getParameterCount()); + VASSERT_EQ("2a.2", "param1", PARAM_NAME(p2a, 0)); + VASSERT_EQ("2a.3", "value1", PARAM_VALUE(p2a, 0)); + VASSERT_EQ("2a.4", "param2", PARAM_NAME(p2a, 1)); + VASSERT_EQ("2a.5", "value2", PARAM_VALUE(p2a, 1)); + + // Multi-section parameters (2/2) + parameterizedHeaderField p2b; + p2b.parse( + "X; param1=value1;\r\n" + " param2=\"should be ignored\";\r\n" + " param2*0=\"val\";\r\n" + " param2*1=\"ue2\";" + ); + + VASSERT_EQ("2b.1", 2, p2b.getParameterCount()); + VASSERT_EQ("2b.2", "param1", PARAM_NAME(p2b, 0)); + VASSERT_EQ("2b.3", "value1", PARAM_VALUE(p2b, 0)); + VASSERT_EQ("2b.4", "param2", PARAM_NAME(p2b, 1)); + VASSERT_EQ("2b.5", "value2", PARAM_VALUE(p2b, 1)); + + // Extended parameter (charset and language information) + parameterizedHeaderField p3; + p3.parse("X; param1*=charset'language'value1;\r\n"); + + VASSERT_EQ("3.1", 1, p3.getParameterCount()); + VASSERT_EQ("3.2", "param1", PARAM_NAME(p3, 0)); + VASSERT_EQ("3.3", "charset", PARAM_CHARSET(p3, 0)); + VASSERT_EQ("3.4", "value1", PARAM_BUFFER(p3, 0)); + + // Encoded characters in extended parameter values + parameterizedHeaderField p4; + p4.parse("X; param1*=a%20value%20with%20multiple%20word%73"); // 0x73 = 's' + + VASSERT_EQ("4.1", 1, p4.getParameterCount()); + VASSERT_EQ("4.2", "param1", PARAM_NAME(p4, 0)); + VASSERT_EQ("4.3", "a value with multiple words", PARAM_VALUE(p4, 0)); + + // Invalid encoded character + parameterizedHeaderField p5; + p5.parse("X; param1*=test%20value%"); + + VASSERT_EQ("5.1", 1, p5.getParameterCount()); + VASSERT_EQ("5.2", "param1", PARAM_NAME(p5, 0)); + VASSERT_EQ("5.3", "test value%", PARAM_VALUE(p5, 0)); + + // Spaces before and after '=' + parameterizedHeaderField p6; + p6.parse("X; param1\t= \"value1\""); + + VASSERT_EQ("6.1", 1, p6.getParameterCount()); + VASSERT_EQ("6.2", "param1", PARAM_NAME(p6, 0)); + VASSERT_EQ("6.3", "value1", PARAM_VALUE(p6, 0)); + + // Quoted strings and escaped chars + parameterizedHeaderField p7; + p7.parse("X; param1=\"this is a slash: \\\"\\\\\\\"\""); // \"\\\" + + VASSERT_EQ("7.1", 1, p7.getParameterCount()); + VASSERT_EQ("7.2", "param1", PARAM_NAME(p7, 0)); + VASSERT_EQ("7.3", "this is a slash: \"\\\"", PARAM_VALUE(p7, 0)); + } + + void testParseRFC2231() { + + // Extended parameter with charset specified in more than one + // section (this is forbidden by RFC, but is should not fail) + parameterizedHeaderField p1; + p1.parse( + "X; param1*0*=charset1'language1'value1;\r\n" + " param1*1*=charset2'language2'value2;" + ); + + VASSERT_EQ("1.1", 1, p1.getParameterCount()); + VASSERT_EQ("1.2", "param1", PARAM_NAME(p1, 0)); + VASSERT_EQ("1.3", "charset1", PARAM_CHARSET(p1, 0)); + VASSERT_EQ("1.4", "value1charset2'language2'value2", PARAM_BUFFER(p1, 0)); + + // Charset not specified in the first section (that is not encoded), + // but specified in the second one (legal) + parameterizedHeaderField p2; + p2.parse( + "X; param1*0=value1;\r\n" + " param1*1*=charset'language'value2;" + ); + + VASSERT_EQ("2.1", 1, p2.getParameterCount()); + VASSERT_EQ("2.2", "param1", PARAM_NAME(p2, 0)); + VASSERT_EQ("2.3", "charset", PARAM_CHARSET(p2, 0)); + VASSERT_EQ("2.4", "value1value2", PARAM_BUFFER(p2, 0)); + + // Characters prefixed with '%' in a simple (not extended) section + // should not be decoded + parameterizedHeaderField p3; + p3.parse("X; param1=val%20ue1"); + + VASSERT_EQ("3.1", 1, p3.getParameterCount()); + VASSERT_EQ("3.2", "param1", PARAM_NAME(p3, 0)); + VASSERT_EQ("3.3", "val%20ue1", PARAM_VALUE(p3, 0)); + + // Multiple sections + charset specified and encoding + parameterizedHeaderField p4; + p4.parse( + "X; param1*0*=charset'language'value1a%20;" + " param1*1*=value1b%20;" + " param1*2=value1c" + ); + + VASSERT_EQ("4.1", 1, p4.getParameterCount()); + VASSERT_EQ("4.2", "param1", PARAM_NAME(p4, 0)); + VASSERT_EQ("4.3", "charset", PARAM_CHARSET(p4, 0)); + VASSERT_EQ("4.4", "value1a value1b value1c", PARAM_BUFFER(p4, 0)); + + // No charset specified: defaults to US-ASCII + parameterizedHeaderField p5; + p5.parse("X; param1*='language'value1"); + + VASSERT_EQ("5.1", 1, p5.getParameterCount()); + VASSERT_EQ("5.2", "param1", PARAM_NAME(p5, 0)); + VASSERT_EQ("5.3", "us-ascii", PARAM_CHARSET(p5, 0)); + VASSERT_EQ("5.4", "value1", PARAM_BUFFER(p5, 0)); + + // Language specification + parameterizedHeaderField p6; + p6.parse("X; param1*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A"); + + VASSERT_EQ("6.1", 1, p6.getParameterCount()); + VASSERT_EQ("6.2", "param1", PARAM_NAME(p6, 0)); + VASSERT_EQ("6.3", "us-ascii", PARAM_CHARSET(p6, 0)); + VASSERT_EQ("6.4", "en-us", PARAM_LANG(p6, 0)); + VASSERT_EQ("6.5", "This is ***fun***", PARAM_BUFFER(p6, 0)); + } + + void testGenerate() { + + // Simple parameter/value + parameterizedHeaderField p1; + p1.appendParameter(vmime::make_shared <vmime::parameter>("param1", "value1")); + + VASSERT_EQ("1", "F: X; param1=value1", p1.generate()); + + // Value that needs quoting (1/2) + parameterizedHeaderField p2a; + p2a.appendParameter(vmime::make_shared <vmime::parameter>("param1", "value1a;value1b")); + + VASSERT_EQ("2a", "F: X; param1=\"value1a;value1b\"", p2a.generate()); + + // Value that needs quoting (2/2) + parameterizedHeaderField p2b; + p2b.appendParameter(vmime::make_shared <vmime::parameter>("param1", "va\\lue\"1")); + + VASSERT_EQ("2b", "F: X; param1=\"va\\\\lue\\\"1\"", p2b.generate()); + } + + void testGetGeneratedSize() { + + vmime::generationContext ctx(vmime::generationContext::getDefaultContext()); + + vmime::parameter p1("param1", "value1"); + VASSERT("1", p1.getGeneratedSize(ctx) >= generateParameter(p1, ctx).length()); + + vmime::parameter p2a("param1", "value1a;value1b"); + VASSERT("2&", p2a.getGeneratedSize(ctx) >= generateParameter(p2a, ctx).length()); + + vmime::parameter p2b("param1", "va\\lue\"1"); + VASSERT("1", p2b.getGeneratedSize(ctx) >= generateParameter(p2b, ctx).length()); + } + + void testGenerateRFC2231() { + + // Extended parameter with charset specifier + parameterizedHeaderField p1; + p1.appendParameter( + vmime::make_shared <vmime::parameter>( + "param1", + vmime::word("value 1\xe9", vmime::charset("charset")) + ) + ); + + VASSERT_EQ( + "1.no-encoding", + "F: X; param1=\"value 1\"", + p1.generate(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING) + ); + + VASSERT_EQ( + "1.rfc2047", + "F: X; param1=\"=?charset?Q?value_1=E9?=\"", + p1.generate(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY) + ); + + VASSERT_EQ( + "1.rfc2231", + "F: X; param1*=charset''value%201%E9", + p1.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY) + ); + + VASSERT_EQ( + "1.both", + "F: X; param1=\"=?charset?Q?value_1=E9?=\";param1*=charset''value%201%E9", + p1.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047) + ); + + // Value that spans on multiple lines + parameterizedHeaderField p2; + p2.appendParameter( + vmime::make_shared <vmime::parameter>( + "param1", + vmime::word( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + vmime::charset("charset") + ) + ) + ); + + VASSERT_EQ( + "2.no-encoding", + "F: X; \r\n " + "param1=abcdefghijkl", + p2.generate(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING, 25) // max line length = 25 + ); + + VASSERT_EQ( + "2.rfc2047", + "F: X; \r\n " + "param1=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + p2.generate(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY, 25) // max line length = 25 + ); + + VASSERT_EQ( + "2.rfc2231", + "F: X; \r\n " + "param1*0*=charset''abc;\r\n " + "param1*1*=defghijkl;\r\n " + "param1*2*=mnopqrstu;\r\n " + "param1*3*=vwxyzABCD;\r\n " + "param1*4*=EFGHIJKLM;\r\n " + "param1*5*=NOPQRSTUV;\r\n " + "param1*6*=WXYZ", + p2.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY, 25) // max line length = 25 + ); + + VASSERT_EQ( + "2.both", + "F: X; \r\n " + "param1=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ;\r\n " + "param1*0*=charset''abc;\r\n " + "param1*1*=defghijkl;\r\n " + "param1*2*=mnopqrstu;\r\n " + "param1*3*=vwxyzABCD;\r\n " + "param1*4*=EFGHIJKLM;\r\n " + "param1*5*=NOPQRSTUV;\r\n " + "param1*6*=WXYZ", + p2.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047, 25) // max line length = 25 + ); + + // Non-ASCII parameter value + parameterizedHeaderField p3; + p3.appendParameter( + vmime::make_shared <vmime::parameter>( + "param1", + vmime::word( + "δσσσσσσσσσσσσσσσσσσσσδσδα δσαδσδσαδσαδασδασ δσαδασδσα δσαδασδσα δασδασδασ δασαχφδδσα 2008.doc", + vmime::charset("utf-8") + ) + ) + ); + + VASSERT_EQ( + "3.no-encoding", + "F: X; \r\n " + "param1=\" 2008.doc\"", + p3.generate(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING, 80) // max line length = 80 + ); + + VASSERT_EQ( + "3.7bit-only", + "F: X; \r\n " + "param1=\"=?utf-8?B?zrTPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+DzrTPg860?=\r\n " + "=?utf-8?B?zrEgzrTPg86xzrTPg860z4POsc60z4POsc60zrHPg860zrHPgyDOtM+DzrHOtM6x?=\r\n " + "=?utf-8?B?z4POtM+DzrEgzrTPg86xzrTOsc+DzrTPg86xIM60zrHPg860zrHPg860zrHPgyDOtA==?=\r\n " + "=?utf-8?B?zrHPg86xz4fPhs60zrTPg86xIDIwMDguZG9j?=\"", + p3.generate(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY, 80) // max line length = 80 + ); + + VASSERT_EQ( + "3.both", + "F: X; \r\n " + "param1=\"=?utf-8?B?zrTPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+Dz4PPg8+DzrTPg860?=\r\n " + "=?utf-8?B?zrEgzrTPg86xzrTPg860z4POsc60z4POsc60zrHPg860zrHPgyDOtM+DzrHOtM6x?=\r\n " + "=?utf-8?B?z4POtM+DzrEgzrTPg86xzrTOsc+DzrTPg86xIM60zrHPg860zrHPg860zrHPgyDOtA==?=\r\n " + "=?utf-8?B?zrHPg86xz4fPhs60zrTPg86xIDIwMDguZG9j?=\";\r\n " + "param1*0*=utf-8''%CE%B4%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n " + "param1*1*=%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n " + "param1*2*=%CE%B4%CF%83%CE%B4%CE%B1%20%CE%B4%CF%83%CE%B1%CE%B4%CF%83%CE%B4%CF;\r\n " + "param1*3*=%83%CE%B1%CE%B4%CF%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CE%B1%CF%83%20;\r\n " + "param1*4*=%CE%B4%CF%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CF%83%CE%B1%20%CE%B4%CF;\r\n " + "param1*5*=%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CF%83%CE%B1%20%CE%B4%CE%B1%CF%83;\r\n " + "param1*6*=%CE%B4%CE%B1%CF%83%CE%B4%CE%B1%CF%83%20%CE%B4%CE%B1%CF%83%CE%B1%CF;\r\n " + "param1*7*=%87%CF%86%CE%B4%CE%B4%CF%83%CE%B1%202008.doc", + p3.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047, 80) // max line length = 80 + ); + + VASSERT_EQ( + "3.either", + "F: X; param1*0*=utf-8''%CE%B4%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n " + "param1*1*=%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83%CF%83;\r\n " + "param1*2*=%CE%B4%CF%83%CE%B4%CE%B1%20%CE%B4%CF%83%CE%B1%CE%B4%CF%83%CE%B4%CF;\r\n " + "param1*3*=%83%CE%B1%CE%B4%CF%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CE%B1%CF%83%20;\r\n " + "param1*4*=%CE%B4%CF%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CF%83%CE%B1%20%CE%B4%CF;\r\n " + "param1*5*=%83%CE%B1%CE%B4%CE%B1%CF%83%CE%B4%CF%83%CE%B1%20%CE%B4%CE%B1%CF%83;\r\n " + "param1*6*=%CE%B4%CE%B1%CF%83%CE%B4%CE%B1%CF%83%20%CE%B4%CE%B1%CF%83%CE%B1%CF;\r\n " + "param1*7*=%87%CF%86%CE%B4%CE%B4%CF%83%CE%B1%202008.doc", + p3.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY, 80) // max line length = 80 + ); + + // No encoding needed + parameterizedHeaderField p4; + p4.appendParameter( + vmime::make_shared <vmime::parameter>( + "param1", + vmime::word("va lue", vmime::charset("charset")) + ) + ); + + VASSERT_EQ( + "4.no-encoding", + "F: X; param1=\"va lue\"", + p4.generate(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING) + ); + + VASSERT_EQ( + "4.rfc2047", + "F: X; param1=\"va lue\"", + p4.generate(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY) + ); + + VASSERT_EQ( + "4.rfc2231", + "F: X; param1=\"va lue\"", + p4.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY) + ); + + VASSERT_EQ( + "4.both", + "F: X; param1=\"va lue\"", + p4.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047) + ); + + // Language specification + parameterizedHeaderField p5; + p5.appendParameter( + vmime::make_shared <vmime::parameter>("param1", + vmime::word("This is ***fun***", vmime::charset("us-ascii"), "en-us")) + ); + + VASSERT_EQ( + "5.no-encoding", + "F: X; param1=\"This is ***fun***\"", + p5.generate(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING) + ); + + VASSERT_EQ( + "5.rfc2047", + "F: X; param1=\"=?us-ascii*en-us?Q?This_is_***fun***?=\"", + p5.generate(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY) + ); + + VASSERT_EQ( + "5.rfc2231", + "F: X; param1*=us-ascii''This%20is%20***fun***", + p5.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY) + ); + + VASSERT_EQ( + "5.both", + "F: X; " + "param1=\"=?us-ascii*en-us?Q?This_is_***fun***?=\";\r\n " + "param1*=us-ascii''This%20is%20***fun***", + p5.generate(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047)); + } + + void testGetGeneratedSizeRFC2231() { + + vmime::generationContext ctx(vmime::generationContext::getDefaultContext()); + + // Extended parameter with charset specifier + vmime::parameter p1( + "param1", + vmime::word("value 1\xe9", vmime::charset("charset")) + ); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING); + VASSERT("1.no-encoding", p1.getGeneratedSize(ctx) >= generateParameter(p1, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY); + VASSERT("1.rfc2047", p1.getGeneratedSize(ctx) >= generateParameter(p1, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY); + VASSERT("1.rfc2231", p1.getGeneratedSize(ctx) >= generateParameter(p1, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047); + VASSERT("1.both", p1.getGeneratedSize(ctx) >= generateParameter(p1, ctx).length()); + + // Value that spans on multiple lines + vmime::parameter p2( + "param1", + vmime::word( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + vmime::charset("charset") + ) + ); + + ctx.setMaxLineLength(25); + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING); + VASSERT("2.no-encoding", p2.getGeneratedSize(ctx) >= generateParameter(p2, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY); + VASSERT("2.rfc2047", p2.getGeneratedSize(ctx) >= generateParameter(p2, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY); + VASSERT("2.rfc2231", p2.getGeneratedSize(ctx) >= generateParameter(p2, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047); + VASSERT("2.both", p2.getGeneratedSize(ctx) >= generateParameter(p2, ctx).length()); + + // Non-ASCII parameter value + vmime::parameter p3( + "param1", + vmime::word( + "δσσσσσσσσσσσσσσσσσσσσδσδα δσαδσδσαδσαδασδασ δσαδασδσα δσαδασδσα δασδασδασ δασαχφδδσα 2008.doc", + vmime::charset("utf-8") + ) + ); + + ctx.setMaxLineLength(vmime::generationContext::getDefaultContext().getMaxLineLength()); + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING); + VASSERT("3.no-encoding", p3.getGeneratedSize(ctx) >= generateParameter(p3, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY); + VASSERT("3.rfc2047", p3.getGeneratedSize(ctx) >= generateParameter(p3, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY); + VASSERT("3.rfc2231", p3.getGeneratedSize(ctx) >= generateParameter(p3, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047); + VASSERT("3.both", p3.getGeneratedSize(ctx) >= generateParameter(p3, ctx).length()); + + // No encoding needed + vmime::parameter p4( + "param1", + vmime::word("va lue", vmime::charset("charset")) + ); + + ctx.setMaxLineLength(vmime::generationContext::getDefaultContext().getMaxLineLength()); + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING); + VASSERT("4.no-encoding", p4.getGeneratedSize(ctx) >= generateParameter(p4, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY); + VASSERT("4.rfc2047", p4.getGeneratedSize(ctx) >= generateParameter(p4, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY); + VASSERT("4.rfc2231", p4.getGeneratedSize(ctx) >= generateParameter(p4, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047); + VASSERT("4.both", p4.getGeneratedSize(ctx) >= generateParameter(p4, ctx).length()); + + // Language specification + vmime::parameter p5( + "param1", + vmime::word("This is ***fun***", vmime::charset("us-ascii"), "en-us") + ); + + ctx.setMaxLineLength(vmime::generationContext::getDefaultContext().getMaxLineLength()); + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_NO_ENCODING); + VASSERT("5.no-encoding", p5.getGeneratedSize(ctx) >= generateParameter(p5, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2047_ONLY); + VASSERT("5.rfc2047", p5.getGeneratedSize(ctx) >= generateParameter(p5, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_ONLY); + VASSERT("5.rfc2231", p5.getGeneratedSize(ctx) >= generateParameter(p5, ctx).length()); + + ctx.setEncodedParameterValueMode(vmime::generationContext::PARAMETER_VALUE_RFC2231_AND_RFC2047); + VASSERT("5.both", p5.getGeneratedSize(ctx) >= generateParameter(p5, ctx).length()); + } + + void testNonStandardEncodedParam() { + + // This syntax is non-standard (expressly prohibited + // by RFC-2047), but is used by Mozilla: + // + // Content-Type: image/png; + // name="=?us-ascii?Q?Logo_VMime=2Epng?=" + + parameterizedHeaderField p1; + p1.parse("image/png; name=\"=?us-ascii?Q?Logo_VMime=2Epng?=\""); + + VASSERT_EQ("1.1", 1, p1.getParameterCount()); + VASSERT_EQ("1.2", "name", PARAM_NAME(p1, 0)); + VASSERT_EQ("1.3", "Logo VMime.png", PARAM_VALUE(p1, 0)); + + parameterizedHeaderField p2; + p2.parse("image/png; name=\"Logo =?us-ascii?Q?VMime=2Epng?=\""); + + VASSERT_EQ("2.1", 1, p2.getParameterCount()); + VASSERT_EQ("2.2", "name", PARAM_NAME(p2, 0)); + VASSERT_EQ("2.3", "Logo VMime.png", PARAM_VALUE(p2, 0)); + } + + // Parse parameters with non-significant whitespaces + void testParseNonSignificantWS() { + + parameterizedHeaderField p1; + p1.parse(" \t X \r\n"); + + VASSERT_EQ("1.1", "X", FIELD_VALUE(p1)); + + parameterizedHeaderField p2; + p2.parse(" X ; param1 = value1 \r\n"); + + VASSERT_EQ("2.1", 1, p2.getParameterCount()); + VASSERT_EQ("2.2", "X", FIELD_VALUE(p2)); + VASSERT_EQ("2.3", "param1", PARAM_NAME(p2, 0)); + VASSERT_EQ("2.4", "value1", PARAM_VALUE(p2, 0)); + } + + // Encode "tspecials" + void testEncodeTSpecials() { + + VASSERT_EQ(" 1", "p=\"val(ue\"", vmime::make_shared <vmime::parameter>("p", "val(ue")->generate()); + VASSERT_EQ(" 2", "p=\"val)ue\"", vmime::make_shared <vmime::parameter>("p", "val)ue")->generate()); + VASSERT_EQ(" 3", "p=\"val<ue\"", vmime::make_shared <vmime::parameter>("p", "val<ue")->generate()); + VASSERT_EQ(" 4", "p=\"val>ue\"", vmime::make_shared <vmime::parameter>("p", "val>ue")->generate()); + VASSERT_EQ(" 5", "p=\"val@ue\"", vmime::make_shared <vmime::parameter>("p", "val@ue")->generate()); + VASSERT_EQ(" 6", "p=\"val,ue\"", vmime::make_shared <vmime::parameter>("p", "val,ue")->generate()); + VASSERT_EQ(" 7", "p=\"val;ue\"", vmime::make_shared <vmime::parameter>("p", "val;ue")->generate()); + VASSERT_EQ(" 8", "p=\"val:ue\"", vmime::make_shared <vmime::parameter>("p", "val:ue")->generate()); + VASSERT_EQ(" 9", "p=\"val/ue\"", vmime::make_shared <vmime::parameter>("p", "val/ue")->generate()); + VASSERT_EQ("10", "p=\"val[ue\"", vmime::make_shared <vmime::parameter>("p", "val[ue")->generate()); + VASSERT_EQ("11", "p=\"val]ue\"", vmime::make_shared <vmime::parameter>("p", "val]ue")->generate()); + VASSERT_EQ("12", "p=\"val?ue\"", vmime::make_shared <vmime::parameter>("p", "val?ue")->generate()); + VASSERT_EQ("13", "p=\"val=ue\"", vmime::make_shared <vmime::parameter>("p", "val=ue")->generate()); + VASSERT_EQ("14", "p=\"val ue\"", vmime::make_shared <vmime::parameter>("p", "val ue")->generate()); + VASSERT_EQ("15", "p=\"val\tue\"", vmime::make_shared <vmime::parameter>("p", "val\tue")->generate()); + } + + // http://sourceforge.net/projects/vmime/forums/forum/237356/topic/3812278 + void testEncodeTSpecialsInRFC2231() { + + VASSERT_EQ( + "1", + "filename*=UTF-8''my_file_name_%C3%B6%C3%A4%C3%BC_%281%29.txt", + vmime::make_shared <vmime::parameter>( + "filename", + vmime::word("my_file_name_\xc3\xb6\xc3\xa4\xc3\xbc_(1).txt", "UTF-8") + )->generate() + ); + } + + void testWhitespaceBreaksTheValue() { + + parameterizedHeaderField p; + p.parse("xxx yyy; param1=value1 \r\n"); + + VASSERT_EQ("count", 1, p.getParameterCount()); + VASSERT_EQ("value", "xxx", FIELD_VALUE(p)); + VASSERT_EQ("param1.name", "param1", PARAM_NAME(p, 0)); + VASSERT_EQ("param1.value", "value1", PARAM_VALUE(p, 0)); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/pathTest.cpp b/vmime-master/tests/parser/pathTest.cpp new file mode 100644 index 0000000..1396911 --- /dev/null +++ b/vmime-master/tests/parser/pathTest.cpp @@ -0,0 +1,102 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + + +VMIME_TEST_SUITE_BEGIN(pathTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParse) + VMIME_TEST(testParse2) + VMIME_TEST(testGenerate) + VMIME_TEST_LIST_END + + + void testParse() { + + vmime::path p1; + p1.parse("<>"); + + VASSERT_EQ("1.1", "", p1.getLocalPart()); + VASSERT_EQ("1.2", "", p1.getDomain()); + + vmime::path p2; + p2.parse("<domain>"); + + VASSERT_EQ("2.1", "", p2.getLocalPart()); + VASSERT_EQ("2.2", "domain", p2.getDomain()); + + vmime::path p3; + p3.parse("<local@domain>"); + + VASSERT_EQ("3.1", "local", p3.getLocalPart()); + VASSERT_EQ("3.2", "domain", p3.getDomain()); + } + + void testParse2() { + + // Test some invalid paths (no '<>') + vmime::path p1; + p1.parse(""); + + VASSERT_EQ("1.1", "", p1.getLocalPart()); + VASSERT_EQ("1.2", "", p1.getDomain()); + + vmime::path p2; + p2.parse("domain"); + + VASSERT_EQ("2.1", "", p2.getLocalPart()); + VASSERT_EQ("2.2", "domain", p2.getDomain()); + + vmime::path p3; + p3.parse("local@domain"); + + VASSERT_EQ("3.1", "local", p3.getLocalPart()); + VASSERT_EQ("3.2", "domain", p3.getDomain()); + } + + void testGenerate() { + + vmime::path p1; + + VASSERT_EQ("1", "<>", p1.generate()); + + vmime::path p2; + p2.setLocalPart("local"); + + VASSERT_EQ("2", "<local@>", p2.generate()); + + vmime::path p3; + p3.setDomain("domain"); + + VASSERT_EQ("3", "<@domain>", p3.generate()); + + vmime::path p4; + p4.setLocalPart("local"); + p4.setDomain("domain"); + + VASSERT_EQ("4", "<local@domain>", p4.generate()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/streamContentHandlerTest.cpp b/vmime-master/tests/parser/streamContentHandlerTest.cpp new file mode 100644 index 0000000..399118e --- /dev/null +++ b/vmime-master/tests/parser/streamContentHandlerTest.cpp @@ -0,0 +1,194 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" + + +VMIME_TEST_SUITE_BEGIN(streamContentHandlerTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testIsEmpty) + VMIME_TEST(testGetLength) + VMIME_TEST(testIsEncoded) + VMIME_TEST(testGetLength_Encoded) + VMIME_TEST(testExtract) + VMIME_TEST(testExtract_Encoded) + VMIME_TEST(testExtractRaw_Encoded) + VMIME_TEST(testGenerate) + VMIME_TEST(testGenerate_Encoded) + VMIME_TEST_LIST_END + + + void testIsEmpty() { + + vmime::streamContentHandler cth; + + VASSERT_TRUE("empty", cth.isEmpty()); + } + + void testGetLength() { + + vmime::string data("Test Data"); + vmime::shared_ptr <vmime::utility::inputStream> stream = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(data); + + vmime::streamContentHandler cth(stream, data.length()); + + VASSERT_FALSE("empty", cth.isEmpty()); + VASSERT_EQ("length", 9, cth.getLength()); + } + + void testIsEncoded() { + + vmime::string data("Test Data"); + vmime::shared_ptr <vmime::utility::inputStream> stream = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(data); + + vmime::streamContentHandler cth(stream, data.length()); + + VASSERT_FALSE("encoded", cth.isEncoded()); + VASSERT_EQ("encoding", vmime::contentHandler::NO_ENCODING, cth.getEncoding()); + + + vmime::string data2("Zm9vEjRWYmFy="); + vmime::shared_ptr <vmime::utility::inputStream> stream2 = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(data2); + + vmime::streamContentHandler cth2(stream2, data2.length(), vmime::encoding("base64")); + + VASSERT_TRUE("encoded", cth2.isEncoded()); + VASSERT_EQ("encoding", "base64", cth2.getEncoding().generate()); + } + + void testGetLength_Encoded() { + + vmime::string data("foo=12=34=56bar"); + vmime::shared_ptr <vmime::utility::inputStream> stream = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(data); + + vmime::streamContentHandler cth(stream, data.length(), vmime::encoding("quoted-printable")); + + // Reported length should be the length of encoded data + VASSERT_EQ("length", 15, cth.getLength()); + } + + void testExtract() { + + vmime::string data("Test Data"); + vmime::shared_ptr <vmime::utility::inputStream> stream = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(data); + + vmime::streamContentHandler cth(stream, data.length()); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extract(osa); + + VASSERT_EQ("extract", "Test Data", oss.str()); + } + + void testExtract_Encoded() { + + vmime::string data( + "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODk=" + ); + + vmime::shared_ptr <vmime::utility::inputStream> stream = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(data); + + vmime::streamContentHandler cth(stream, data.length(), vmime::encoding("base64")); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extract(osa); + + // Data should be decoded from B64 + VASSERT_EQ( + "extract", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + oss.str() + ); + } + + void testExtractRaw_Encoded() + { + vmime::string data( + "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODk=" + ); + vmime::shared_ptr <vmime::utility::inputStream> stream = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(data); + + vmime::streamContentHandler cth(stream, data.length(), vmime::encoding("base64")); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extractRaw(osa); + + // Data should not be decoded + VASSERT_EQ( + "extractRaw", + "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODk=", + oss.str() + ); + } + + void testGenerate() { + + vmime::string data("foo\x12\x34\x56 bar"); + vmime::shared_ptr <vmime::utility::inputStream> stream = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(data); + + vmime::streamContentHandler cth(stream, data.length()); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.generate(osa, vmime::encoding("base64")); + + // Data should be encoded to B64 + VASSERT_EQ("generate", "Zm9vEjRWIGJhcg==", oss.str()); + } + + void testGenerate_Encoded() { + + vmime::string data("foo=12=34=56bar"); + vmime::shared_ptr <vmime::utility::inputStream> stream = + vmime::make_shared <vmime::utility::inputStreamStringAdapter>(data); + + vmime::streamContentHandler cth(stream, data.length(), vmime::encoding("quoted-printable")); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.generate(osa, vmime::encoding("base64")); + + // Data should be reencoded from QP to B64 + VASSERT_EQ("generate", "Zm9vEjRWYmFy", oss.str()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/stringContentHandlerTest.cpp b/vmime-master/tests/parser/stringContentHandlerTest.cpp new file mode 100644 index 0000000..c856301 --- /dev/null +++ b/vmime-master/tests/parser/stringContentHandlerTest.cpp @@ -0,0 +1,165 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include "vmime/utility/outputStreamAdapter.hpp" + + +VMIME_TEST_SUITE_BEGIN(stringContentHandlerTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testIsEmpty) + VMIME_TEST(testGetLength) + VMIME_TEST(testIsEncoded) + VMIME_TEST(testGetLength_Encoded) + VMIME_TEST(testExtract) + VMIME_TEST(testExtract_Encoded) + VMIME_TEST(testExtractRaw_Encoded) + VMIME_TEST(testGenerate) + VMIME_TEST(testGenerate_Encoded) + VMIME_TEST_LIST_END + + + void testIsEmpty() { + + vmime::stringContentHandler cth; + + VASSERT_TRUE("empty", cth.isEmpty()); + } + + void testGetLength() { + + vmime::stringContentHandler cth("Test Data"); + + VASSERT_FALSE("empty", cth.isEmpty()); + VASSERT_EQ("length", 9, cth.getLength()); + } + + void testIsEncoded() { + + vmime::stringContentHandler cth("Test Data"); + + VASSERT_FALSE("encoded", cth.isEncoded()); + VASSERT_EQ("encoding", vmime::contentHandler::NO_ENCODING, cth.getEncoding()); + + + vmime::stringContentHandler cth2("Zm9vEjRWYmFy=", vmime::encoding("base64")); + + VASSERT_TRUE("encoded", cth2.isEncoded()); + VASSERT_EQ("encoding", "base64", cth2.getEncoding().generate()); + } + + void testGetLength_Encoded() { + + vmime::stringContentHandler cth( + "foo=12=34=56bar", + vmime::encoding("quoted-printable") + ); + + // Reported length should be the length of encoded data + VASSERT_EQ("length", 15, cth.getLength()); + } + + void testExtract() { + + vmime::stringContentHandler cth("Test Data"); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extract(osa); + + VASSERT_EQ("extract", "Test Data", oss.str()); + } + + void testExtract_Encoded() { + + vmime::stringContentHandler cth( + "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODk=", + vmime::encoding("base64") + ); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extract(osa); + + // Data should be decoded from B64 + VASSERT_EQ( + "extract", + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + oss.str() + ); + } + + void testExtractRaw_Encoded() { + + vmime::stringContentHandler cth( + "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODk=", + vmime::encoding("base64") + ); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.extractRaw(osa); + + // Data should not be decoded + VASSERT_EQ( + "extractRaw", + "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODk=", + oss.str() + ); + } + + void testGenerate() { + + vmime::stringContentHandler cth("foo\x12\x34\x56 bar"); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.generate(osa, vmime::encoding("base64")); + + // Data should be encoded to B64 + VASSERT_EQ("generate", "Zm9vEjRWIGJhcg==", oss.str()); + } + + void testGenerate_Encoded() { + + vmime::stringContentHandler cth( + "foo=12=34=56bar", + vmime::encoding("quoted-printable") + ); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter osa(oss); + + cth.generate(osa, vmime::encoding("base64")); + + // Data should be reencoded from QP to B64 + VASSERT_EQ("generate", "Zm9vEjRWYmFy", oss.str()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/textTest.cpp b/vmime-master/tests/parser/textTest.cpp new file mode 100644 index 0000000..e9ddc26 --- /dev/null +++ b/vmime-master/tests/parser/textTest.cpp @@ -0,0 +1,918 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include <locale> +#include <clocale> + + +VMIME_TEST_SUITE_BEGIN(textTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testConstructors) + VMIME_TEST(testCopy) + VMIME_TEST(testNewFromString) + VMIME_TEST(testDisplayForm) + VMIME_TEST(testParse) + VMIME_TEST(testGenerate) + + VMIME_TEST(testWordConstructors) + VMIME_TEST(testWordParse) + VMIME_TEST(testWordGenerate) + VMIME_TEST(testWordGenerateSpace) + VMIME_TEST(testWordGenerateSpace2) + VMIME_TEST(testWordGenerateMultiBytes) + VMIME_TEST(testWordGenerateQuote) + VMIME_TEST(testWordGenerateSpecialCharsets) + VMIME_TEST(testWordGenerateSpecials) + + VMIME_TEST(testWhitespace) + VMIME_TEST(testWhitespaceMBox) + + VMIME_TEST(testFoldingAscii) + VMIME_TEST(testForcedNonEncoding) + + VMIME_TEST(testBugFix20110511) + + VMIME_TEST(testInternationalizedEmail_specialChars) + VMIME_TEST(testInternationalizedEmail_UTF8) + VMIME_TEST(testInternationalizedEmail_nonUTF8) + VMIME_TEST(testInternationalizedEmail_folding) + VMIME_TEST(testInternationalizedEmail_whitespace) + + VMIME_TEST(testWronglyPaddedB64Words) + VMIME_TEST(testFixBrokenWords) + VMIME_TEST(testUnknownCharset) + VMIME_TEST_LIST_END + + + static const vmime::string getDisplayText(const vmime::text& t) { + + return t.getWholeBuffer(); + } + + static const vmime::string cleanGeneratedWords(const std::string& str) { + + std::istringstream iss(str); + + std::string res; + std::string x; + + while (std::getline(iss, x)) { + res += vmime::utility::stringUtils::trim(x); + } + + return res; + } + + + void setUp() { + + // Set the global C and C++ locale to the user-configured locale. + // The locale should use UTF-8 encoding for these tests to run successfully. + try { + std::locale::global(std::locale("")); + } catch (std::exception &) { + std::setlocale(LC_ALL, ""); + } + } + + void tearDown() { + + // Restore default locale + std::locale::global(std::locale("C")); + } + + + void testConstructors() { + + vmime::text t1; + + VASSERT_EQ("1.1", 0, t1.getWordCount()); + + vmime::text t2("Test\xa9\xc3"); + + VASSERT_EQ("2.1", 1, t2.getWordCount()); + VASSERT_EQ("2.2", "Test\xa9\xc3", t2.getWordAt(0)->getBuffer()); + VASSERT_EQ("2.3", vmime::charset::getLocalCharset(), t2.getWordAt(0)->getCharset()); + + vmime::text t3("Test\xa9\xc3", vmime::charset(vmime::charsets::ISO8859_13)); + + VASSERT_EQ("3.1", 1, t3.getWordCount()); + VASSERT_EQ("3.2", "Test\xa9\xc3", t3.getWordAt(0)->getBuffer()); + VASSERT_EQ("3.3", vmime::charset(vmime::charsets::ISO8859_13), t3.getWordAt(0)->getCharset()); + + vmime::word w1("Test", vmime::charset(vmime::charsets::UTF_8)); + vmime::text t4(w1); + + VASSERT_EQ("4.1", 1, t4.getWordCount()); + VASSERT_EQ("4.2", w1.getBuffer(), t4.getWordAt(0)->getBuffer()); + VASSERT_EQ("4.3", w1.getCharset(), t4.getWordAt(0)->getCharset()); + + vmime::word w2("Other", vmime::charset(vmime::charsets::US_ASCII)); + t4.appendWord(vmime::make_shared <vmime::word>(w2)); + + vmime::text t5(t4); + + VASSERT_EQ("5.1", 2, t5.getWordCount()); + VASSERT_EQ("5.2", w1.getBuffer(), t5.getWordAt(0)->getBuffer()); + VASSERT_EQ("5.3", w1.getCharset(), t5.getWordAt(0)->getCharset()); + VASSERT_EQ("5.4", w2.getBuffer(), t5.getWordAt(1)->getBuffer()); + VASSERT_EQ("5.5", w2.getCharset(), t5.getWordAt(1)->getCharset()); + } + + void testCopy() { + + vmime::text t1("Test: \xa9\xc3"); + + VASSERT("operator==", t1 == t1); + VASSERT("clone", *vmime::clone(t1) == t1); + + vmime::text t2; + t2.copyFrom(t1); + + VASSERT("copyFrom", t1 == t2); + } + + void testNewFromString() { + + vmime::string s1 = "only ASCII characters"; + vmime::charset c1("test"); + vmime::text t1; + + t1.createFromString(s1, c1); + + VASSERT_EQ("1.1", 1, t1.getWordCount()); + VASSERT_EQ("1.2", s1, t1.getWordAt(0)->getBuffer()); + VASSERT_EQ("1.3", vmime::charset(vmime::charsets::US_ASCII), t1.getWordAt(0)->getCharset()); + + vmime::string s2_1 = "some ASCII characters and special chars: "; + vmime::string s2_2 = "\xf1\xf2\xf3\xf4 "; + vmime::string s2_3 = "and then more ASCII chars."; + vmime::string s2 = s2_1 + s2_2 + s2_3; + vmime::charset c2("test"); + vmime::text t2; + + t2.createFromString(s2, c2); + + VASSERT_EQ("2.1", 3, t2.getWordCount()); + VASSERT_EQ("2.2", "some ASCII characters and special chars: ", t2.getWordAt(0)->getBuffer()); + VASSERT_EQ("2.3", vmime::charset(vmime::charsets::US_ASCII), t2.getWordAt(0)->getCharset()); + VASSERT_EQ("2.4", "\xf1\xf2\xf3\xf4", t2.getWordAt(1)->getBuffer()); + VASSERT_EQ("2.5", c2, t2.getWordAt(1)->getCharset()); + VASSERT_EQ("2.6", "and then more ASCII chars.", t2.getWordAt(2)->getBuffer()); + VASSERT_EQ("2.7", vmime::charset(vmime::charsets::US_ASCII), t2.getWordAt(2)->getCharset()); + } + + static const vmime::string parseText(const vmime::string& buffer) { + + vmime::text t; + t.parse(buffer); + + std::ostringstream oss; + oss << t; + + return oss.str(); + } + + void testParse() { + + // From RFC-2047 + VASSERT_EQ( + "1", + "[text: [[word: charset=US-ASCII, buffer=Keith Moore]]]", + parseText("=?US-ASCII?Q?Keith_Moore?=") + ); + + VASSERT_EQ( + "2", + "[text: [[word: charset=ISO-8859-1, buffer=Keld J\xf8rn Simonsen]]]", + parseText("=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?=") + ); + + VASSERT_EQ( + "3", + "[text: [[word: charset=ISO-8859-1, buffer=Andr\xe9]," \ + "[word: charset=us-ascii, buffer= Pirard]]]", + parseText("=?ISO-8859-1?Q?Andr=E9?= Pirard") + ); + + VASSERT_EQ( + "4", + "[text: [[word: charset=ISO-8859-1, buffer=If you can read this yo]," \ + "[word: charset=ISO-8859-2, buffer=u understand the example.]]]", + parseText( + "=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=\r\n " \ + "=?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=" + ) + ); + + // Bugfix: in "=?charset?q?=XX=YY?=", the "?=" finish + // sequence was not correctly found (should be the one + // after '=YY' and not the one after '?q'). + VASSERT_EQ( + "5", + "[text: [[word: charset=abc, buffer=\xe9\xe9]]]", + parseText("=?abc?q?=E9=E9?=") + ); + + // Question marks (?) in the middle of the string + VASSERT_EQ( + "6", + "[text: [[word: charset=iso-8859-1, buffer=Know wh\xe4t? It works!]]]", + parseText("=?iso-8859-1?Q?Know_wh=E4t?_It_works!?=") + ); + + // With language specifier + VASSERT_EQ( + "7", + "[text: [[word: charset=US-ASCII, buffer=Keith Moore, lang=EN]]]", + parseText("=?US-ASCII*EN?Q?Keith_Moore?=") + ); + } + + void testGenerate() { + + // TODO + + // With language specifier + vmime::word wlang1("Émeline", vmime::charset("UTF-8"), "FR"); + VASSERT_EQ("lang1", "=?UTF-8*FR?Q?=C3=89meline?=", wlang1.generate()); + + vmime::word wlang2("Keith Moore", vmime::charset("US-ASCII"), "EN"); + VASSERT_EQ("lang2", "=?US-ASCII*EN?Q?Keith_Moore?=", wlang2.generate()); + } + + void testDisplayForm() { + +#define DISPLAY_FORM(x) getDisplayText(*vmime::text::decodeAndUnfold(x)) + + // From RFC-2047 + VASSERT_EQ("1", "a", DISPLAY_FORM("=?ISO-8859-1?Q?a?=")); + VASSERT_EQ("2", "a b", DISPLAY_FORM("=?ISO-8859-1?Q?a?= b")); + VASSERT_EQ("3", "ab", DISPLAY_FORM("=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=")); + VASSERT_EQ("4", "ab", DISPLAY_FORM("=?ISO-8859-1?Q?a?= \t =?ISO-8859-1?Q?b?=")); + VASSERT_EQ("5", "ab", DISPLAY_FORM("=?ISO-8859-1?Q?a?= \r\n \t =?ISO-8859-1?Q?b?=")); + VASSERT_EQ("6", "a b", DISPLAY_FORM("=?ISO-8859-1?Q?a_b?=")); + VASSERT_EQ("7", "a b", DISPLAY_FORM("=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=")); + + // Some more tests... + VASSERT_EQ("8", "a b", DISPLAY_FORM(" a =?ISO-8859-1?Q?b?= ")); + VASSERT_EQ("9", "a b ", DISPLAY_FORM(" \t =?ISO-8859-1?Q?a?= b ")); + VASSERT_EQ("10", "a b", DISPLAY_FORM(" a\r\n\t b")); + + VASSERT_EQ("11", "a b c", DISPLAY_FORM("a =?ISO-8859-1?Q?b?= c")); + VASSERT_EQ("12", "a b c ", DISPLAY_FORM("a =?ISO-8859-1?Q?b?= c ")); + VASSERT_EQ("13", "a b c ", DISPLAY_FORM(" a =?ISO-8859-1?Q?b?= c ")); + VASSERT_EQ("14", "a b c d", DISPLAY_FORM("a =?ISO-8859-1?Q?b?= c =?ISO-8859-1?Q?d?= ")); + VASSERT_EQ("15", "a b c d e", DISPLAY_FORM("a =?ISO-8859-1?Q?b?= c =?ISO-8859-1?Q?d?= e")); + + // Whitespaces and multiline + VASSERT_EQ("16", "a b c d e", DISPLAY_FORM("=?ISO-8859-1?Q?a_b_?=c\n\t=?ISO-8859-1?Q?d_?=e")); + + // Ignored newlines + VASSERT_EQ("17", "ab", DISPLAY_FORM("=?ISO-8859-1?Q?a?=\r\nb")); + VASSERT_EQ("18", "a b", DISPLAY_FORM("=?ISO-8859-1?Q?a?= \r\nb")); + +#undef DISPLAY_FORM + } + + void testWordConstructors() { + + VASSERT_EQ("1.1", vmime::charset::getLocalCharset(), vmime::word().getCharset()); + VASSERT_EQ("1.2", "", vmime::word().getBuffer()); + + VASSERT_EQ("2.1", vmime::charset::getLocalCharset(), vmime::word("foo").getCharset()); + VASSERT_EQ("2.2", "foo", vmime::word("foo").getBuffer()); + + VASSERT_EQ("3.1", "bar", vmime::word("foo", vmime::charset("bar")).getCharset().getName()); + VASSERT_EQ("3.2", "foo", vmime::word("foo", vmime::charset("bar")).getBuffer()); + } + + void testWordParse() { + + // Simple encoded word + vmime::word w1; + w1.parse("=?foo?q?bar=E9 baz?="); + + VASSERT_EQ("1.1", "foo", w1.getCharset().getName()); + VASSERT_EQ("1.2", "bar\xe9 baz", w1.getBuffer()); + + // Unencoded text + vmime::word w2; + w2.parse(" foo bar \tbaz..."); + + VASSERT_EQ("2.1", vmime::charset(vmime::charsets::US_ASCII), w2.getCharset()); + VASSERT_EQ("2.2", " foo bar \tbaz...", w2.getBuffer()); + + // Malformed word + vmime::word w3; + w3.parse("=?foo bar"); + + VASSERT_EQ("3.1", vmime::charset(vmime::charsets::US_ASCII), w3.getCharset()); + VASSERT_EQ("3.2", "=?foo bar", w3.getBuffer()); + + // Unknown encoding + vmime::word w4; + w4.parse("=?whatever?not_q_or_b?whatever?="); + + VASSERT_EQ("4.1", vmime::charset(vmime::charsets::US_ASCII), w4.getCharset()); + VASSERT_EQ("4.2", "=?whatever?not_q_or_b?whatever?=", w4.getBuffer()); + } + + void testWordGenerate() { + + VASSERT_EQ( + "1", + "=?foo?Q?bar=E9_baz?=", + vmime::word("bar\xe9 baz", vmime::charset("foo")).generate() + ); + + VASSERT_EQ( + "2", + "=?foo?B?8fLz9PU=?=", + vmime::word("\xf1\xf2\xf3\xf4\xf5", vmime::charset("foo")).generate() + ); + } + + void testWordGenerateSpace() { + + // No white-space between an unencoded word and a encoded one + VASSERT_EQ( + "1", + "Bonjour =?utf-8?Q?Fran=C3=A7ois?=", + vmime::text::newFromString( + "Bonjour Fran\xc3\xa7ois", + vmime::charset("utf-8") + )->generate() + ); + + // White-space between two encoded words + vmime::text txt; + txt.appendWord(vmime::make_shared <vmime::word>("\xc3\x89t\xc3\xa9", "utf-8")); + txt.appendWord(vmime::make_shared <vmime::word>("Fran\xc3\xa7ois", "utf-8")); + + const vmime::string decoded = "\xc3\x89t\xc3\xa9""Fran\xc3\xa7ois"; + const vmime::string encoded = "=?utf-8?B?w4l0w6k=?= =?utf-8?Q?Fran=C3=A7ois?="; + + // -- test encoding + VASSERT_EQ("2", encoded, txt.generate()); + + // -- ensure no space is added when decoding + vmime::text txt2; + txt2.parse(encoded, 0, encoded.length()); + + VASSERT_EQ("3", decoded, txt2.getWholeBuffer()); + + // -- test rencoding + VASSERT_EQ("4", encoded, txt2.generate()); + } + + void testWordGenerateSpace2() { + + // White-space between two encoded words (#2) + vmime::text txt; + txt.appendWord(vmime::make_shared <vmime::word>("Facture ", "utf-8")); + txt.appendWord(vmime::make_shared <vmime::word>("\xc3\xa0", "utf-8")); + txt.appendWord(vmime::make_shared <vmime::word>(" envoyer ", "utf-8")); + txt.appendWord(vmime::make_shared <vmime::word>("\xc3\xa0", "utf-8")); + txt.appendWord(vmime::make_shared <vmime::word>(" Martine", "utf-8")); + + const vmime::string decoded = "Facture ""\xc3\xa0"" envoyer ""\xc3\xa0"" Martine"; + const vmime::string encoded = "Facture =?utf-8?B?w6A=?= envoyer =?utf-8?B?w6A=?= Martine"; + + // -- test encoding + VASSERT_EQ("1", encoded, txt.generate()); + + // -- ensure no space is added when decoding + vmime::text txt2; + txt2.parse(encoded, 0, encoded.length()); + + VASSERT_EQ("2", decoded, txt2.getWholeBuffer()); + + // -- test rencoding + VASSERT_EQ("3", encoded, txt2.generate()); + } + + void testWordGenerateMultiBytes() { + + // Ensure we don't encode a non-integral number of characters + VASSERT_EQ( + "1", + "=?utf-8?Q?aaa?==?utf-8?Q?=C3=A9?==?utf-8?Q?zzz?=", + cleanGeneratedWords( + vmime::word("aaa\xc3\xa9zzz", vmime::charset("utf-8")).generate(16) + ) + ); + + VASSERT_EQ( + "2", + "=?utf-8?Q?aaa=C3=A9?==?utf-8?Q?zzz?=", + cleanGeneratedWords( + vmime::word("aaa\xc3\xa9zzz", vmime::charset("utf-8")).generate(17) + ) + ); + } + + void testWordGenerateQuote() { + + std::string str; + vmime::utility::outputStreamStringAdapter os(str); + + vmime::generationContext ctx; + ctx.setMaxLineLength(1000); + + // ASCII-only text is quotable + str.clear(); + vmime::word("Quoted text") + .generate(ctx, os, 0, NULL, vmime::text::QUOTE_IF_POSSIBLE, NULL); + + VASSERT_EQ("1", "\"Quoted text\"", cleanGeneratedWords(str)); + + // Text with CR/LF is not quotable + str.clear(); + vmime::word("Non-quotable\ntext", "us-ascii") + .generate(ctx, os, 0, NULL, vmime::text::QUOTE_IF_POSSIBLE, NULL); + + VASSERT_EQ("2", "=?us-ascii?Q?Non-quotable=0Atext?=", cleanGeneratedWords(str)); + + // Text with non-ASCII chars is not quotable + str.clear(); + vmime::word("Non-quotable text \xc3\xa9") + .generate(ctx, os, 0, NULL, vmime::text::QUOTE_IF_POSSIBLE, NULL); + + VASSERT_EQ("3", "=?UTF-8?Q?Non-quotable_text_=C3=A9?=", cleanGeneratedWords(str)); + } + + void testWordGenerateSpecialCharsets() { + + // ISO-2022-JP only uses 7-bit chars but should be encoded in Base64 + VASSERT_EQ( + "1", + "=?iso-2022-jp?B?XlskQiVRITwlPSVKJWshJiU9JVUlSCUmJSclIl5bKEI=?=", + cleanGeneratedWords( + vmime::word( + "^[$B%Q!<%=%J%k!&%=%U%H%&%'%\"^[(B", + vmime::charset("iso-2022-jp") + ).generate(100) + ) + ); + } + + void testWordGenerateSpecials() { + + // In RFC-2047, quotation marks (ASCII 22h) should be encoded + VASSERT_EQ( + "1", + "=?UTF-8?Q?=22=C3=9Cml=C3=A4ute=22?=", + vmime::word( + "\x22\xC3\x9Cml\xC3\xA4ute\x22", + vmime::charset("UTF-8") + ).generate() + ); + } + + void testWhitespace() { + + // Create + vmime::text text; + text.createFromString("Achim Br\xc3\xa4ndt", vmime::charsets::UTF_8); + + VASSERT_EQ("1", 2, text.getWordCount()); + VASSERT_EQ("2", "Achim ", text.getWordAt(0)->getBuffer()); + VASSERT_EQ("3", "us-ascii", text.getWordAt(0)->getCharset()); + VASSERT_EQ("4", "Br\xc3\xa4ndt", text.getWordAt(1)->getBuffer()); + VASSERT_EQ("5", "utf-8", text.getWordAt(1)->getCharset()); + + // Generate + VASSERT_EQ("6", "Achim =?utf-8?Q?Br=C3=A4ndt?=", text.generate()); + + // Parse + text.parse("=?us-ascii?Q?Achim_?= =?utf-8?Q?Br=C3=A4ndt?="); + + VASSERT_EQ("7", 2, text.getWordCount()); + VASSERT_EQ("8", "Achim ", text.getWordAt(0)->getBuffer()); + VASSERT_EQ("9", "us-ascii", text.getWordAt(0)->getCharset()); + VASSERT_EQ("10", "Br\xc3\xa4ndt", text.getWordAt(1)->getBuffer()); + VASSERT_EQ("11", "utf-8", text.getWordAt(1)->getCharset()); + } + + void testWhitespaceMBox() { + + // Space MUST be encoded inside a word + vmime::mailbox mbox(vmime::text("Achim Br\xc3\xa4ndt", vmime::charsets::UTF_8), "me@vmime.org"); + VASSERT_EQ("generate1", "=?us-ascii?Q?Achim_?= =?utf-8?Q?Br=C3=A4ndt?= <me@vmime.org>", mbox.generate()); + + vmime::text txt; + txt.appendWord(vmime::make_shared <vmime::word>("Achim ", "us-ascii")); + txt.appendWord(vmime::make_shared <vmime::word>("Br\xc3\xa4ndt", "utf-8")); + mbox = vmime::mailbox(txt, "me@vmime.org"); + VASSERT_EQ("generate2", "=?us-ascii?Q?Achim_?= =?utf-8?Q?Br=C3=A4ndt?= <me@vmime.org>", mbox.generate()); + + mbox.parse("=?us-ascii?Q?Achim?= =?utf-8?Q?Br=C3=A4ndt?= <me@vmime.org>"); + VASSERT_EQ("parse.name.count", 2, mbox.getName().getWordCount()); + VASSERT_EQ("parse.name.word1.buffer", "Achim", mbox.getName().getWordAt(0)->getBuffer()); + VASSERT_EQ("parse.name.word1.charset", "us-ascii", mbox.getName().getWordAt(0)->getCharset()); + VASSERT_EQ("parse.name.word2.buffer", "Br\xc3\xa4ndt", mbox.getName().getWordAt(1)->getBuffer()); + VASSERT_EQ("parse.name.word2.charset", "utf-8", mbox.getName().getWordAt(1)->getCharset()); + + VASSERT_EQ("parse.email", "me@vmime.org", mbox.getEmail()); + } + + void testFoldingAscii() { + + // In this test, no encoding is needed, but line should be folded anyway + vmime::word w("01234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789", vmime::charset("us-ascii")); + + VASSERT_EQ( + "fold.ascii", + "=?us-ascii?Q?01234567890123456789012345678901234?=\r\n" + " =?us-ascii?Q?5678901234567890123456789012345678?=\r\n" + " =?us-ascii?Q?9012345678901234567890123456789?=", w.generate(50) + ); + } + + void testForcedNonEncoding() { + + // Testing long unbreakable and unencodable header + vmime::relay r; + r.parse( + " from User (Ee9GMqZQ8t7IQwftfAFHd2KyScCYRrFSJ50tKEoXv2bVCG4HcPU80GGWiFabAvG77FekpGgF1h@[127.0.0.1]) by servername.hostname.com\n\t" + "with esmtp id 1NGTS9-2C0sqG0; Fri, 4 Dec 2009 09:23:49 +0100" + ); + + VASSERT_EQ( + "received.long", + "from User\r\n (Ee9GMqZQ8t7IQwftfAFHd2KyScCYRrFSJ50tKEoXv2bVCG4HcPU80GGWiFabAvG77FekpGgF1h@[127.0.0.1])\r\n by servername.hostname.com with esmtp id 1NGTS9-2C0sqG0; Fri, 4 Dec 2009\r\n 09:23:49 +0100", + r.generate(78) + ); + } + + void testBugFix20110511() { + + /* + + Using the latest version of vmime (0.9.1), encoding the following string: Jean + Gwenaël Dutourd will result in: + Jean =?utf-8?Q?Gwena=C3=ABl_?= Dutourd + However, decoding this will result in Jean Gwenaël Dutourd (notice two spaces + between the last 2 words). The encoder adds a _ after the second word, but + since the last word is not encoded, the space between them is not ignored, and + is decoded into an additional space. + + See: http://sourceforge.net/projects/vmime/forums/forum/237357/topic/4531365 + + */ + + const std::string DECODED_TEXT = "Jean Gwenaël Dutourd"; + const std::string ENCODED_TEXT = "Jean =?utf-8?Q?Gwena=C3=ABl?= Dutourd"; + + // Encode + VASSERT_EQ( + "encode", + ENCODED_TEXT, + vmime::text::newFromString(DECODED_TEXT, vmime::charset("utf-8"))->generate() + ); + + // Decode + vmime::text t; + t.parse(ENCODED_TEXT); + + // -- words + std::ostringstream oss; oss << t; + VASSERT_EQ( + "decode1", + "[text: [[word: charset=us-ascii, buffer=Jean ]," + "[word: charset=utf-8, buffer=Gwenaël]," + "[word: charset=us-ascii, buffer= Dutourd]]]", + oss.str() + ); + + // -- getWholeBuffer + VASSERT_EQ("decode2", DECODED_TEXT, t.getWholeBuffer()); + } + + void testInternationalizedEmail_specialChars() { + + vmime::generationContext ctx(vmime::generationContext::getDefaultContext()); + ctx.setInternationalizedEmailSupport(true); + + vmime::generationContext::switcher <vmime::generationContext> contextSwitcher(ctx); + + // Special sequence/chars should still be encoded + VASSERT_EQ( + "1", + "=?us-ascii?Q?Test=3D=3Frfc2047_sequence?=", + vmime::word("Test=?rfc2047 sequence", vmime::charset("us-ascii")).generate() + ); + + VASSERT_EQ( + "2", + "=?us-ascii?Q?Line_One=0ALine_Two?=", + vmime::word("Line One\nLine Two", vmime::charset("us-ascii")).generate() + ); + } + + void testInternationalizedEmail_UTF8() { + + vmime::generationContext ctx(vmime::generationContext::getDefaultContext()); + ctx.setInternationalizedEmailSupport(true); + + vmime::generationContext::switcher <vmime::generationContext> contextSwitcher(ctx); + + // Already UTF-8 encoded text should be left as is + VASSERT_EQ( + "1", "Achim Br\xc3\xa4ndt", + vmime::word("Achim Br\xc3\xa4ndt", vmime::charset("utf-8")).generate() + ); + } + + void testInternationalizedEmail_nonUTF8() { + + vmime::generationContext ctx(vmime::generationContext::getDefaultContext()); + ctx.setInternationalizedEmailSupport(true); + + vmime::generationContext::switcher <vmime::generationContext> contextSwitcher(ctx); + + // Non UTF-8 encoded text should first be converted to UTF-8 + VASSERT_EQ( + "1", "Achim Br\xc3\xa4ndt", + vmime::word("Achim Br\xe4ndt", vmime::charset("iso-8859-1")).generate() + ); + } + + void testInternationalizedEmail_folding() { + + vmime::generationContext ctx(vmime::generationContext::getDefaultContext()); + ctx.setInternationalizedEmailSupport(true); + + vmime::generationContext::switcher <vmime::generationContext> contextSwitcher(ctx); + + // RFC-2047 encoding must be performed, as line folding is needed + vmime::word w1("01234567890123456789\xc3\xa0x012345678901234567890123456789" + "01234567890123456789\xc3\xa0x012345678901234567890123456789", vmime::charset("utf-8")); + + VASSERT_EQ( + "1", + "=?utf-8?Q?01234567890123456789=C3=A0x01234567890?=\r\n" + " =?utf-8?Q?1234567890123456789012345678901234567?=\r\n" + " =?utf-8?Q?89=C3=A0x0123456789012345678901234567?=\r\n" + " =?utf-8?Q?89?=", + w1.generate(50) + ); + + // RFC-2047 encoding will not be forced, as words can be wrapped in a new line + vmime::word w2("bla bla bla This is some '\xc3\xa0\xc3\xa7' UTF-8 encoded text", vmime::charset("utf-8")); + + VASSERT_EQ( + "2", + "bla bla bla This is\r\n" + " some '\xc3\xa0\xc3\xa7' UTF-8\r\n" + " encoded text", + w2.generate(20) + ); + } + + void testInternationalizedEmail_whitespace() { + + // Sanity checks for running this test + { + vmime::text t; + t.parse("=?utf-8?Q?Adquisi=C3=B3n?= de Laptop y celular"); + + VASSERT_EQ("parse", "Adquisión de Laptop y celular", t.getWholeBuffer()); + } + + { + vmime::text t("Adquisi\xc3\xb3n de Laptop y celular", vmime::charset("UTF-8")); + + VASSERT_EQ("generate", "=?UTF-8?Q?Adquisi=C3=B3n?= de Laptop y celular", t.generate()); + } + + // Ensure a whitespace is added between encoded words in intl email support enabled + { + vmime::text t; + t.parse("=?utf-8?Q?Adquisi=C3=B3n?= de Laptop y celular"); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter ossAdapter(oss); + vmime::generationContext gctx(vmime::generationContext::getDefaultContext()); + gctx.setInternationalizedEmailSupport(true); + t.generate(gctx, ossAdapter); + + VASSERT_EQ("generate", "Adquisi\xc3\xb3n de Laptop y celular", oss.str()); + } + + { + vmime::text t; + t.parse("=?utf-8?Q?Adquisi=C3=B3n?= de Laptop =?utf-8?Q?y?= celular"); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter ossAdapter(oss); + vmime::generationContext gctx(vmime::generationContext::getDefaultContext()); + gctx.setInternationalizedEmailSupport(true); + t.generate(gctx, ossAdapter); + + VASSERT_EQ("generate", "Adquisi\xc3\xb3n de Laptop y celular", oss.str()); + } + + { + vmime::text t; + t.parse("=?utf-8?Q?Adquisi=C3=B3n?= de Laptop =?utf-8?Q?y_celular?="); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter ossAdapter(oss); + vmime::generationContext gctx(vmime::generationContext::getDefaultContext()); + gctx.setInternationalizedEmailSupport(true); + t.generate(gctx, ossAdapter); + + VASSERT_EQ("generate", "Adquisi\xc3\xb3n de Laptop y celular", oss.str()); + } + + // Ensure no whitespace is added with non-encoded words + { + vmime::text t; + t.parse("Laptop y celular"); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter ossAdapter(oss); + vmime::generationContext gctx(vmime::generationContext::getDefaultContext()); + gctx.setInternationalizedEmailSupport(true); + t.generate(gctx, ossAdapter); + + VASSERT_EQ("generate", "Laptop y celular", oss.str()); + } + + { + vmime::text t; + t.parse("=?utf-8?Q?Laptop_y_celular?="); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter ossAdapter(oss); + vmime::generationContext gctx(vmime::generationContext::getDefaultContext()); + gctx.setInternationalizedEmailSupport(true); + t.generate(gctx, ossAdapter); + + VASSERT_EQ("generate", "Laptop y celular", oss.str()); + } + } + + void testWronglyPaddedB64Words() { + + vmime::text outText; + + vmime::text::decodeAndUnfold("=?utf-8?B?5Lit5?=\n =?utf-8?B?paH?=", &outText); + + VASSERT_EQ( + "1", + "\xe4\xb8\xad\xe6\x96\x87", + outText.getConvertedText(vmime::charset("utf-8")) + ); + + vmime::text::decodeAndUnfold("=?utf-8?B?5Lit5p?=\n =?utf-8?B?aH?=", &outText); + + VASSERT_EQ( + "2", + "\xe4\xb8\xad\xe6\x96\x87", + outText.getConvertedText(vmime::charset("utf-8")) + ); + + vmime::text::decodeAndUnfold("=?utf-8?B?5Lit5pa?=\n =?utf-8?B?H?=", &outText); + + VASSERT_EQ( + "3", + "\xe4\xb8\xad\xe6\x96\x87", + outText.getConvertedText(vmime::charset("utf-8")) + ); + } + + // Ensure that words which encode a non-integral number of characters + // are correctly decoded. + void testFixBrokenWords() { + + vmime::text outText; + + vmime::charsetConverterOptions opts; + opts.silentlyReplaceInvalidSequences = false; // just to be sure that broken words are actually fixed + + // Test case 1 + vmime::text::decodeAndUnfold( + "=?utf-8?Q?Gwena=C3?=" + "=?utf-8?Q?=ABl?=", + &outText + ); + + VASSERT_EQ("1.1", 1, outText.getWordCount()); + VASSERT_EQ("1.2", "Gwena\xc3\xabl", outText.getWordAt(0)->getBuffer()); + VASSERT_EQ("1.3", vmime::charset("utf-8"), outText.getWordAt(0)->getCharset()); + + // Test case 2 + vmime::text::decodeAndUnfold( + "=?utf-8?B?5Lit6Yu85qmf5qKw6JGj5LqL5pyDMTAz5bm056ysMDXlsYbn?=" + "=?utf-8?B?rKwwN+asoeitsOeoiw==?=", + &outText + ); + + VASSERT_EQ("2.1", 1, outText.getWordCount()); + VASSERT_EQ("2.2", "\xe4\xb8\xad\xe9\x8b\xbc\xe6\xa9\x9f\xe6\xa2\xb0" + "\xe8\x91\xa3\xe4\xba\x8b\xe6\x9c\x83\x31\x30\x33\xe5\xb9\xb4" + "\xe7\xac\xac\x30\x35\xe5\xb1\x86\xe7\xac\xac\x30\x37\xe6\xac" + "\xa1\xe8\xad\xb0\xe7\xa8\x8b", outText.getWordAt(0)->getBuffer()); + VASSERT_EQ("2.3", vmime::charset("utf-8"), outText.getWordAt(0)->getCharset()); + + // Test case 3 (a character spanning over 3 words: 'を' = E3 82 92) + vmime::text::decodeAndUnfold( + "=?utf-8?Q?abc=E3?=" + "=?utf-8?Q?=82?=" + "=?utf-8?Q?=92xyz?=", + &outText + ); + + VASSERT_EQ("3.1", 1, outText.getWordCount()); + VASSERT_EQ("3.2", "abc\xe3\x82\x92xyz", outText.getWordAt(0)->getBuffer()); + VASSERT_EQ("3.3", vmime::charset("utf-8"), outText.getWordAt(0)->getCharset()); + + // Test case 4 (remains invalid) + vmime::text::decodeAndUnfold( + "=?utf-8?Q?abc=E3?=" + "=?utf-8?Q?=82?=" + "=?utf-8?Q?xy?=" + "=?utf-8?Q?z?=", + &outText + ); + + VASSERT_EQ("4.1", 2, outText.getWordCount()); + VASSERT_EQ("4.2", "abc", outText.getWordAt(0)->getBuffer()); + VASSERT_EQ("4.3", vmime::charset("utf-8"), outText.getWordAt(0)->getCharset()); + VASSERT_EQ("4.4", "\xe3\x82xyz", outText.getWordAt(1)->getBuffer()); + VASSERT_EQ("4.5", vmime::charset("utf-8"), outText.getWordAt(1)->getCharset()); + + // Test case 5 (remains partially invalid) + vmime::text::decodeAndUnfold( + "=?utf-8?Q?abc=E3?=" + "=?utf-8?Q?=82?=" + "=?utf-8?Q?\x92xy?=" + "=?utf-8?Q?z\xc3?=", + &outText + ); + + VASSERT_EQ("5.1", 2, outText.getWordCount()); + VASSERT_EQ("5.2", "abc\xe3\x82\x92xyz", outText.getWordAt(0)->getBuffer()); + VASSERT_EQ("5.3", vmime::charset("utf-8"), outText.getWordAt(0)->getCharset()); + VASSERT_EQ("5.4", "\xc3", outText.getWordAt(1)->getBuffer()); + VASSERT_EQ("5.5", vmime::charset("utf-8"), outText.getWordAt(1)->getCharset()); + } + + void testUnknownCharset() { + + vmime::text t; + vmime::text::decodeAndUnfold("=?gb2312?B?wdaRY8PA?=", &t); + + VASSERT_EQ("1.1", 1, t.getWordCount()); + VASSERT_EQ("1.2", "\xc1\xd6\x91\x63\xc3\xc0", t.getWordAt(0)->getBuffer()); + VASSERT_EQ("1.3", vmime::charset("gb2312"), t.getWordAt(0)->getCharset()); + + + + vmime::parsingContext ctx; + + const vmime::string hfieldBuffer = "From: '=?gb2312?B?wdaRY8PA?=' <a.b@c.de>"; + + vmime::shared_ptr <vmime::headerField> hfield = + vmime::headerField::parseNext(ctx, hfieldBuffer, 0, hfieldBuffer.size()); + + vmime::shared_ptr <vmime::mailbox> hvalue = + hfield->getValue <vmime::mailbox>(); + + VASSERT_EQ("2.1", 3, hvalue->getName().getWordCount()); + VASSERT_EQ("2.2", "'", hvalue->getName().getWordAt(0)->getBuffer()); + VASSERT_EQ("2.3", vmime::charset("us-ascii"), hvalue->getName().getWordAt(0)->getCharset()); + VASSERT_EQ("2.4", "\xc1\xd6\x91\x63\xc3\xc0", hvalue->getName().getWordAt(1)->getBuffer()); + VASSERT_EQ("2.5", vmime::charset("gb2312"), hvalue->getName().getWordAt(1)->getCharset()); + VASSERT_EQ("2.6", "'", hvalue->getName().getWordAt(2)->getBuffer()); + VASSERT_EQ("2.7", vmime::charset("us-ascii"), hvalue->getName().getWordAt(2)->getCharset()); + } + +VMIME_TEST_SUITE_END diff --git a/vmime-master/tests/parser/wordEncoderTest.cpp b/vmime-master/tests/parser/wordEncoderTest.cpp new file mode 100644 index 0000000..08d34aa --- /dev/null +++ b/vmime-master/tests/parser/wordEncoderTest.cpp @@ -0,0 +1,174 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "tests/testUtils.hpp" + +#include "vmime/wordEncoder.hpp" + + +VMIME_TEST_SUITE_BEGIN(wordEncoderTest) + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testGetNextChunk) + VMIME_TEST(testGetNextChunk_integral) + VMIME_TEST(testIsEncodingNeeded_ascii) + VMIME_TEST(testIsEncodingNeeded_withLanguage) + VMIME_TEST(testIsEncodingNeeded_specialChars) + VMIME_TEST(testGuessBestEncoding_QP) + VMIME_TEST(testGuessBestEncoding_B64) + VMIME_TEST(testEncodeQP_RFC2047) + VMIME_TEST_LIST_END + + + void testGetNextChunk() { + + // An integral number of characters should be encoded + vmime::wordEncoder we( + "bufferfoobarbaz", + vmime::charset("utf-8"), + vmime::wordEncoder::ENCODING_AUTO + ); + + VASSERT_EQ("1", "buffer", we.getNextChunk(6)); + VASSERT_EQ("2", "foo", we.getNextChunk(3)); + VASSERT_EQ("3", "barbaz", we.getNextChunk(10)); + } + + void testGetNextChunk_integral() { + + // An integral number of characters should be encoded + vmime::wordEncoder we( + "buffer\xc3\xa0plop", + vmime::charset("utf-8"), + vmime::wordEncoder::ENCODING_AUTO + ); + + VASSERT_EQ("1", "buffer=C3=A0", we.getNextChunk(7)); + VASSERT_EQ("2", "plop", we.getNextChunk(10)); + } + + void testIsEncodingNeeded_ascii() { + + vmime::generationContext ctx(vmime::generationContext::getDefaultContext()); + ctx.setInternationalizedEmailSupport(false); + + VASSERT_FALSE( + "ascii", + vmime::wordEncoder::isEncodingNeeded( + ctx, "ASCII-only buffer", vmime::charset("utf-8"), "" + ) + ); + + VASSERT_TRUE( + "non-ascii", + vmime::wordEncoder::isEncodingNeeded( + ctx, "Buffer with some UTF-8 '\xc3\xa0'", vmime::charset("utf-8"), "" + ) + ); + } + + void testIsEncodingNeeded_withLanguage() { + + VASSERT_TRUE( + "ascii", + vmime::wordEncoder::isEncodingNeeded( + vmime::generationContext::getDefaultContext(), + "ASCII-only buffer", + vmime::charset("utf-8"), + "en" + ) + ); + } + + void testIsEncodingNeeded_specialChars() { + + VASSERT_TRUE( + "rfc2047", + vmime::wordEncoder::isEncodingNeeded( + vmime::generationContext::getDefaultContext(), + "foo bar =? foo bar", + vmime::charset("us-ascii"), + "" + ) + ); + + VASSERT_TRUE( + "new line 1", + vmime::wordEncoder::isEncodingNeeded( + vmime::generationContext::getDefaultContext(), + "foo bar \n foo bar", + vmime::charset("us-ascii"), + "" + ) + ); + + VASSERT_TRUE( + "new line 2", + vmime::wordEncoder::isEncodingNeeded( + vmime::generationContext::getDefaultContext(), + "foo bar \r foo bar", + vmime::charset("us-ascii"), + "" + ) + ); + } + + void testGuessBestEncoding_QP() { + + VASSERT_EQ( + "1", + vmime::wordEncoder::ENCODING_QP, + vmime::wordEncoder::guessBestEncoding("ASCII only buffer", vmime::charset("us-ascii")) + ); + } + + void testGuessBestEncoding_B64() { + + // >= 40% non-ASCII => Base64... + VASSERT_EQ( + "1", + vmime::wordEncoder::ENCODING_B64, + vmime::wordEncoder::guessBestEncoding("xxxxx\xc3\xa0\xc3\xa0", vmime::charset("utf-8")) + ); + + // ...else Quoted-Printable + VASSERT_EQ( + "2", + vmime::wordEncoder::ENCODING_QP, + vmime::wordEncoder::guessBestEncoding("xxxxxx\xc3\xa0\xc3\xa0", vmime::charset("utf-8")) + ); + } + + void testEncodeQP_RFC2047() { + + // When Quoted-Printable is used, it should be RFC-2047 QP encoding + vmime::wordEncoder we( + "buffer\xc3\xa0 foo_bar", + vmime::charset("utf-8"), + vmime::wordEncoder::ENCODING_AUTO + ); + + VASSERT_EQ("1", "buffer=C3=A0_foo=5Fbar", we.getNextChunk(100)); + } + +VMIME_TEST_SUITE_END |