diff --git a/SConstruct b/SConstruct index 06b61cdd..4cb82020 100644 --- a/SConstruct +++ b/SConstruct @@ -363,6 +363,7 @@ libvmimetest_sources = [ # ============================== Parser ============================== 'tests/parser/attachmentHelperTest.cpp', 'tests/parser/bodyPartTest.cpp', + 'tests/parser/bodyTest.cpp', 'tests/parser/charsetTest.cpp', 'tests/parser/charsetFilteredOutputStreamTest.cpp', 'tests/parser/datetimeTest.cpp', diff --git a/src/body.cpp b/src/body.cpp index d68254c7..f1a8cc0f 100644 --- a/src/body.cpp +++ b/src/body.cpp @@ -494,7 +494,10 @@ void body::generateImpl else { // Generate the contents - m_contents->generate(os, getEncoding(), ctx.getMaxLineLength()); + ref contents = m_contents->clone(); + contents->setContentTypeHint(getContentType()); + + contents->generate(os, getEncoding(), ctx.getMaxLineLength()); } } diff --git a/src/emptyContentHandler.cpp b/src/emptyContentHandler.cpp index d672ae24..07b53391 100644 --- a/src/emptyContentHandler.cpp +++ b/src/emptyContentHandler.cpp @@ -102,4 +102,16 @@ bool emptyContentHandler::isBuffered() const } +void emptyContentHandler::setContentTypeHint(const mediaType& type) +{ + m_contentType = type; +} + + +const mediaType emptyContentHandler::getContentTypeHint() const +{ + return m_contentType; +} + + } // vmime diff --git a/src/net/imap/IMAPMessagePartContentHandler.cpp b/src/net/imap/IMAPMessagePartContentHandler.cpp index 277ca579..c34dc076 100644 --- a/src/net/imap/IMAPMessagePartContentHandler.cpp +++ b/src/net/imap/IMAPMessagePartContentHandler.cpp @@ -90,6 +90,8 @@ void IMAPMessagePartContentHandler::generate ref theEncoder = enc.getEncoder(); theEncoder->getProperties()["maxlinelength"] = maxLineLength; + theEncoder->getProperties()["text"] = (m_contentType.getType() == mediaTypes::TEXT); + theEncoder->encode(tempIn, os); } // No encoding to perform @@ -110,6 +112,7 @@ void IMAPMessagePartContentHandler::generate // Encode temporary buffer to output stream ref theEncoder = enc.getEncoder(); theEncoder->getProperties()["maxlinelength"] = maxLineLength; + theEncoder->getProperties()["text"] = (m_contentType.getType() == mediaTypes::TEXT); utility::inputStreamStringAdapter is(oss.str()); @@ -188,6 +191,18 @@ bool IMAPMessagePartContentHandler::isBuffered() const } +void IMAPMessagePartContentHandler::setContentTypeHint(const mediaType& type) +{ + m_contentType = type; +} + + +const mediaType IMAPMessagePartContentHandler::getContentTypeHint() const +{ + return m_contentType; +} + + } // imap } // net } // vmime diff --git a/src/streamContentHandler.cpp b/src/streamContentHandler.cpp index 711ac4dc..d6444833 100644 --- a/src/streamContentHandler.cpp +++ b/src/streamContentHandler.cpp @@ -52,7 +52,7 @@ streamContentHandler::~streamContentHandler() streamContentHandler::streamContentHandler(const streamContentHandler& cts) - : contentHandler(), m_encoding(cts.m_encoding), + : contentHandler(), m_encoding(cts.m_encoding), m_contentType(cts.m_contentType), m_stream(cts.m_stream), m_length(cts.m_length) { } @@ -66,6 +66,7 @@ ref streamContentHandler::clone() const streamContentHandler& streamContentHandler::operator=(const streamContentHandler& cts) { + m_contentType = cts.m_contentType; m_encoding = cts.m_encoding; m_stream = cts.m_stream; @@ -103,6 +104,7 @@ void streamContentHandler::generate(utility::outputStream& os, const vmime::enco ref theEncoder = enc.getEncoder(); theEncoder->getProperties()["maxlinelength"] = maxLineLength; + theEncoder->getProperties()["text"] = (m_contentType.getType() == mediaTypes::TEXT); m_stream->reset(); // may not work... @@ -129,6 +131,7 @@ void streamContentHandler::generate(utility::outputStream& os, const vmime::enco { ref theEncoder = enc.getEncoder(); theEncoder->getProperties()["maxlinelength"] = maxLineLength; + theEncoder->getProperties()["text"] = (m_contentType.getType() == mediaTypes::TEXT); m_stream->reset(); // may not work... @@ -216,4 +219,16 @@ bool streamContentHandler::isBuffered() const } +void streamContentHandler::setContentTypeHint(const mediaType& type) +{ + m_contentType = type; +} + + +const mediaType streamContentHandler::getContentTypeHint() const +{ + return m_contentType; +} + + } // vmime diff --git a/src/stringContentHandler.cpp b/src/stringContentHandler.cpp index 52bbd230..e4762dce 100644 --- a/src/stringContentHandler.cpp +++ b/src/stringContentHandler.cpp @@ -44,7 +44,8 @@ stringContentHandler::stringContentHandler(const string& buffer, const vmime::en stringContentHandler::stringContentHandler(const stringContentHandler& cts) - : contentHandler(), m_encoding(cts.m_encoding), m_string(cts.m_string) + : contentHandler(), m_contentType(cts.m_contentType), + m_encoding(cts.m_encoding), m_string(cts.m_string) { } @@ -75,6 +76,7 @@ ref stringContentHandler::clone() const stringContentHandler& stringContentHandler::operator=(const stringContentHandler& cts) { + m_contentType = cts.m_contentType; m_encoding = cts.m_encoding; m_string = cts.m_string; @@ -127,6 +129,7 @@ void stringContentHandler::generate(utility::outputStream& os, ref theEncoder = enc.getEncoder(); theEncoder->getProperties()["maxlinelength"] = maxLineLength; + theEncoder->getProperties()["text"] = (m_contentType.getType() == mediaTypes::TEXT); utility::inputStreamStringProxyAdapter in(m_string); @@ -151,6 +154,7 @@ void stringContentHandler::generate(utility::outputStream& os, { ref theEncoder = enc.getEncoder(); theEncoder->getProperties()["maxlinelength"] = maxLineLength; + theEncoder->getProperties()["text"] = (m_contentType.getType() == mediaTypes::TEXT); utility::inputStreamStringProxyAdapter in(m_string); @@ -217,4 +221,16 @@ bool stringContentHandler::isBuffered() const } +void stringContentHandler::setContentTypeHint(const mediaType& type) +{ + m_contentType = type; +} + + +const mediaType stringContentHandler::getContentTypeHint() const +{ + return m_contentType; +} + + } // vmime diff --git a/tests/parser/bodyTest.cpp b/tests/parser/bodyTest.cpp new file mode 100644 index 00000000..bb4d966b --- /dev/null +++ b/tests/parser/bodyTest.cpp @@ -0,0 +1,66 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard +// +// 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::create + ("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::create + ("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/contentHandler.hpp b/vmime/contentHandler.hpp index 05e356e4..ae20e892 100644 --- a/vmime/contentHandler.hpp +++ b/vmime/contentHandler.hpp @@ -32,6 +32,7 @@ #include "vmime/utility/smartPtr.hpp" #include "vmime/utility/progressListener.hpp" #include "vmime/encoding.hpp" +#include "vmime/mediaType.hpp" namespace vmime @@ -118,6 +119,18 @@ public: * if not (ie. streamed data from socket) */ virtual bool isBuffered() const = 0; + + /** Gives a hint about the kind of data managed by this object. + * + * @param type content media type + */ + virtual void setContentTypeHint(const mediaType& type) = 0; + + /** Returns a hint about the kind of data managed by this object. + * + * @return type content media type + */ + virtual const mediaType getContentTypeHint() const = 0; }; diff --git a/vmime/emptyContentHandler.hpp b/vmime/emptyContentHandler.hpp index e4f8597e..829e8898 100644 --- a/vmime/emptyContentHandler.hpp +++ b/vmime/emptyContentHandler.hpp @@ -54,6 +54,13 @@ public: bool isEmpty() const; bool isBuffered() const; + + void setContentTypeHint(const mediaType& type); + const mediaType getContentTypeHint() const; + +private: + + mediaType m_contentType; }; diff --git a/vmime/net/imap/IMAPMessagePartContentHandler.hpp b/vmime/net/imap/IMAPMessagePartContentHandler.hpp index 45629e10..602b0879 100644 --- a/vmime/net/imap/IMAPMessagePartContentHandler.hpp +++ b/vmime/net/imap/IMAPMessagePartContentHandler.hpp @@ -63,12 +63,16 @@ public: bool isBuffered() const; + void setContentTypeHint(const mediaType& type); + const mediaType getContentTypeHint() const; + private: weak_ref m_message; weak_ref m_part; vmime::encoding m_encoding; + vmime::mediaType m_contentType; }; diff --git a/vmime/streamContentHandler.hpp b/vmime/streamContentHandler.hpp index eb79ba76..bf2eb449 100644 --- a/vmime/streamContentHandler.hpp +++ b/vmime/streamContentHandler.hpp @@ -99,8 +99,13 @@ public: bool isBuffered() const; + void setContentTypeHint(const mediaType& type); + const mediaType getContentTypeHint() const; + private: + mediaType m_contentType; + // Equals to NO_ENCODING if data is not encoded, otherwise this // specifies the encoding that have been used to encode the data. vmime::encoding m_encoding; diff --git a/vmime/stringContentHandler.hpp b/vmime/stringContentHandler.hpp index 20a29157..4565ceb8 100644 --- a/vmime/stringContentHandler.hpp +++ b/vmime/stringContentHandler.hpp @@ -79,8 +79,13 @@ public: bool isBuffered() const; + void setContentTypeHint(const mediaType& type); + const mediaType getContentTypeHint() const; + private: + mediaType m_contentType; + // Equals to NO_ENCODING if data is not encoded, otherwise this // specifies the encoding that have been used to encode the data. vmime::encoding m_encoding;