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 | 
