From 9a42c8bca4af312b2506a8ae409f9c533946a5c7 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 13 Jul 2006 16:32:39 +0000 Subject: [PATCH] Fixed bugs in MHTML code + unit tests. --- ChangeLog | 5 + SConstruct | 1 + src/htmlTextPart.cpp | 119 ++++++++++------ src/messageBuilder.cpp | 4 +- src/messageParser.cpp | 33 ++--- src/plainTextPart.cpp | 12 +- tests/parser/htmlTextPartTest.cpp | 226 ++++++++++++++++++++++++++++++ vmime/htmlTextPart.hpp | 17 ++- vmime/messageParser.hpp | 4 +- vmime/plainTextPart.hpp | 10 +- vmime/textPart.hpp | 24 +++- 11 files changed, 367 insertions(+), 88 deletions(-) create mode 100644 tests/parser/htmlTextPartTest.cpp diff --git a/ChangeLog b/ChangeLog index e7a0ebc9..85763a65 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,11 @@ VERSION 0.8.1cvs ================ +2006-07-13 Vincent Richard + + * Fixed bugs in MHTML code: 'CID' prefix should not be case-sensitive; + fixed detection of parts identified by a 'Content-Location'. + 2006-04-23 Vincent Richard * Added vmime::net::folder::destroy() to delete folders on IMAP and diff --git a/SConstruct b/SConstruct index d85d36ab..c719c292 100644 --- a/SConstruct +++ b/SConstruct @@ -342,6 +342,7 @@ libvmimetest_sources = [ 'tests/parser/dispositionTest.cpp', 'tests/parser/encoderTest.cpp', 'tests/parser/headerTest.cpp', + 'tests/parser/htmlTextPartTest.cpp', 'tests/parser/mailboxTest.cpp', 'tests/parser/mediaTypeTest.cpp', 'tests/parser/messageIdTest.cpp', diff --git a/src/htmlTextPart.cpp b/src/htmlTextPart.cpp index 6c1f3ce1..0c64350e 100644 --- a/src/htmlTextPart.cpp +++ b/src/htmlTextPart.cpp @@ -50,7 +50,7 @@ htmlTextPart::~htmlTextPart() const mediaType htmlTextPart::getType() const { - return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML)); + return mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML); } @@ -60,14 +60,14 @@ const int htmlTextPart::getPartCount() const } -void htmlTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const +void htmlTextPart::generateIn(ref /* message */, ref parent) const { // Plain text if (!m_plainText->isEmpty()) { // -- Create a new part ref part = vmime::create (); - parent.getBody()->appendPart(part); + parent->getBody()->appendPart(part); // -- Set header fields part->getHeader()->ContentType()->setValue @@ -96,7 +96,7 @@ void htmlTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const { // Create a "multipart/related" body part ref relPart = vmime::create (); - parent.getBody()->appendPart(relPart); + parent->getBody()->appendPart(relPart); relPart->getHeader()->ContentType()-> setValue(mediaType(mediaTypes::MULTIPART, mediaTypes::MULTIPART_RELATED)); @@ -128,7 +128,7 @@ void htmlTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const else { // Add the HTML part into the parent part - parent.getBody()->appendPart(htmlPart); + parent->getBody()->appendPart(htmlPart); } } @@ -140,6 +140,8 @@ void htmlTextPart::findEmbeddedParts(const bodyPart& part, { ref p = part.getBody()->getPartAt(i); + // For a part to be an embedded object, it must have a + // Content-Id field or a Content-Location field. try { p->getHeader()->findField(fields::CONTENT_ID); @@ -148,17 +150,16 @@ void htmlTextPart::findEmbeddedParts(const bodyPart& part, catch (exceptions::no_such_field) { // No "Content-id" field. - // Maybe there is a "Content-Location" field... - try - { - p->getHeader()->findField(fields::CONTENT_ID); - locParts.push_back(p); - } - catch (exceptions::no_such_field) - { - // No "Content-Location" field. - // Cannot be an embedded object since it cannot be referenced in HTML text. - } + } + + try + { + p->getHeader()->findField(fields::CONTENT_LOCATION); + locParts.push_back(p); + } + catch (exceptions::no_such_field) + { + // No "Content-Location" field. } findEmbeddedParts(*p, cidParts, locParts); @@ -168,6 +169,11 @@ void htmlTextPart::findEmbeddedParts(const bodyPart& part, void htmlTextPart::addEmbeddedObject(const bodyPart& part, const string& id) { + // The object may already exists. This can happen if an object is + // identified by both a Content-Id and a Content-Location. In this + // case, there will be two embedded objects with two different IDs + // but referencing the same content. + mediaType type; try @@ -186,28 +192,28 @@ void htmlTextPart::addEmbeddedObject(const bodyPart& part, const string& id) } -void htmlTextPart::parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart) +void htmlTextPart::parse(ref message, ref parent, ref textPart) { // Search for possible embedded objects in the _whole_ message. std::vector > cidParts; std::vector > locParts; - findEmbeddedParts(message, cidParts, locParts); + findEmbeddedParts(*message, cidParts, locParts); // Extract HTML text std::ostringstream oss; utility::outputStreamAdapter adapter(oss); - textPart.getBody()->getContents()->extract(adapter); + textPart->getBody()->getContents()->extract(adapter); const string data = oss.str(); - m_text = textPart.getBody()->getContents()->clone(); + m_text = textPart->getBody()->getContents()->clone(); try { const ref ctf = - textPart.getHeader()->findField(fields::CONTENT_TYPE).dynamicCast (); + textPart->getHeader()->findField(fields::CONTENT_TYPE).dynamicCast (); m_charset = ctf->getCharset(); } @@ -229,13 +235,12 @@ void htmlTextPart::parse(const bodyPart& message, const bodyPart& parent, const const messageId mid = *midField->getValue().dynamicCast (); - const string searchFor("CID:" + mid.getId()); - - if (data.find(searchFor) != string::npos) + if (data.find("CID:" + mid.getId()) != string::npos || + data.find("cid:" + mid.getId()) != string::npos) { // This part is referenced in the HTML text. // Add it to the embedded object list. - addEmbeddedObject(**p, "CID:" + mid.getId()); + addEmbeddedObject(**p, mid.getId()); } } @@ -256,7 +261,7 @@ void htmlTextPart::parse(const bodyPart& message, const bodyPart& parent, const } // Extract plain text, if any. - if (!findPlainTextPart(message, parent, textPart)) + if (!findPlainTextPart(*message, *parent, *textPart)) { m_plainText = vmime::create (); } @@ -321,7 +326,7 @@ bool htmlTextPart::findPlainTextPart(const bodyPart& part, const bodyPart& paren // If we don't have found the plain text part here, it means that // it does not exists (the MUA which built this message probably // did not include it...). - return (found); + return found; } } } @@ -337,13 +342,13 @@ bool htmlTextPart::findPlainTextPart(const bodyPart& part, const bodyPart& paren found = findPlainTextPart(*part.getBody()->getPartAt(i), parent, textPart); } - return (found); + return found; } const charset& htmlTextPart::getCharset() const { - return (m_charset); + return m_charset; } @@ -355,7 +360,7 @@ void htmlTextPart::setCharset(const charset& ch) const ref htmlTextPart::getPlainText() const { - return (m_plainText); + return m_plainText; } @@ -367,7 +372,7 @@ void htmlTextPart::setPlainText(ref plainText) const ref htmlTextPart::getText() const { - return (m_text); + return m_text; } @@ -379,39 +384,43 @@ void htmlTextPart::setText(ref text) const int htmlTextPart::getObjectCount() const { - return (m_objects.size()); + return m_objects.size(); } const ref htmlTextPart::getObjectAt(const int pos) const { - return (m_objects[pos]); + return m_objects[pos]; } -const ref htmlTextPart::findObject(const string& id) const +const ref htmlTextPart::findObject(const string& id_) const { + const string id = cleanId(id_); + for (std::vector >::const_iterator o = m_objects.begin() ; o != m_objects.end() ; ++o) { if ((*o)->getId() == id) - return (*o); + return *o; } throw exceptions::no_object_found(); } -const bool htmlTextPart::hasObject(const string& id) const +const bool htmlTextPart::hasObject(const string& id_) const { + const string id = cleanId(id_); + for (std::vector >::const_iterator o = m_objects.begin() ; o != m_objects.end() ; ++o) { if ((*o)->getId() == id) - return (true); + return true; } - return (false); + return false; } @@ -419,24 +428,42 @@ const string htmlTextPart::addObject(ref data, const vmime::encoding& enc, const mediaType& type) { const messageId mid(messageId::generateId()); - const string id = "CID:" + mid.getId(); + const string id = mid.getId(); m_objects.push_back(vmime::create (data, enc, id, type)); - return (id); + return "CID:" + id; } const string htmlTextPart::addObject(ref data, const mediaType& type) { - return (addObject(data, encoding::decide(data), type)); + return addObject(data, encoding::decide(data), type); } const string htmlTextPart::addObject(const string& data, const mediaType& type) { ref cts = vmime::create (data); - return (addObject(cts, encoding::decide(cts), type)); + return addObject(cts, encoding::decide(cts), type); +} + + +// static +const string htmlTextPart::cleanId(const string& id) +{ + if (id.length() >= 4 && + (id[0] == 'c' || id[0] == 'C') && + (id[1] == 'i' || id[1] == 'I') && + (id[2] == 'd' || id[2] == 'D') && + id[3] == ':') + { + return id.substr(4); + } + else + { + return id; + } } @@ -456,25 +483,25 @@ htmlTextPart::embeddedObject::embeddedObject const ref htmlTextPart::embeddedObject::getData() const { - return (m_data); + return m_data; } const vmime::encoding& htmlTextPart::embeddedObject::getEncoding() const { - return (m_encoding); + return m_encoding; } const string& htmlTextPart::embeddedObject::getId() const { - return (m_id); + return m_id; } const mediaType& htmlTextPart::embeddedObject::getType() const { - return (m_type); + return m_type; } diff --git a/src/messageBuilder.cpp b/src/messageBuilder.cpp index 6bf36d6f..d22d31ef 100644 --- a/src/messageBuilder.cpp +++ b/src/messageBuilder.cpp @@ -105,12 +105,12 @@ ref messageBuilder::construct() const // Generate the text parts into this sub-part (normally, this // sub-part will have the "multipart/alternative" content-type...) - m_textPart->generateIn(*msg, *subPart); + m_textPart->generateIn(msg, subPart); } else { // Generate the text part(s) directly into the message - m_textPart->generateIn(*msg, *msg); + m_textPart->generateIn(msg, msg); // If any attachment, set message content-type to "multipart/mixed" if (!m_attach.empty()) diff --git a/src/messageParser.cpp b/src/messageParser.cpp index 018a5ed0..18809e82 100644 --- a/src/messageParser.cpp +++ b/src/messageParser.cpp @@ -100,7 +100,7 @@ void messageParser::parse(ref msg) findAttachments(msg); // Text parts - findTextParts(*msg, *msg); + findTextParts(msg, msg); } @@ -110,11 +110,11 @@ void messageParser::findAttachments(ref msg) } -void messageParser::findTextParts(const bodyPart& msg, const bodyPart& part) +void messageParser::findTextParts(ref msg, ref part) { // Handle the case in which the message is not multipart: if the body part is // "text/*", take this part. - if (part.getBody()->getPartCount() == 0) + if (part->getBody()->getPartCount() == 0) { mediaType type(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN); bool accept = false; @@ -122,7 +122,7 @@ void messageParser::findTextParts(const bodyPart& msg, const bodyPart& part) try { const contentTypeField& ctf = dynamic_cast - (*msg.getHeader()->findField(fields::CONTENT_TYPE)); + (*msg->getHeader()->findField(fields::CONTENT_TYPE)); const mediaType ctfType = *ctf.getValue().dynamicCast (); @@ -155,7 +155,7 @@ void messageParser::findTextParts(const bodyPart& msg, const bodyPart& part) } -bool messageParser::findSubTextParts(const bodyPart& msg, const bodyPart& part) +bool messageParser::findSubTextParts(ref msg, ref part) { // In general, all the text parts are contained in parallel in the same // parent part (or message). @@ -164,9 +164,9 @@ bool messageParser::findSubTextParts(const bodyPart& msg, const bodyPart& part) std::vector > textParts; - for (int i = 0 ; i < part.getBody()->getPartCount() ; ++i) + for (int i = 0 ; i < part->getBody()->getPartCount() ; ++i) { - const ref p = part.getBody()->getPartAt(i); + const ref p = part->getBody()->getPartAt(i); try { @@ -200,7 +200,7 @@ bool messageParser::findSubTextParts(const bodyPart& msg, const bodyPart& part) try { ref txtPart = textPartFactory::getInstance()->create(type); - txtPart->parse(msg, part, **p); + txtPart->parse(msg, part, *p); m_textParts.push_back(txtPart); } @@ -209,21 +209,16 @@ bool messageParser::findSubTextParts(const bodyPart& msg, const bodyPart& part) // Content-type not recognized. } } - - //return true; } - //else + bool found = false; + + for (int i = 0 ; !found && (i < part->getBody()->getPartCount()) ; ++i) { - bool found = false; - - for (int i = 0 ; !found && (i < part.getBody()->getPartCount()) ; ++i) - { - found = findSubTextParts(msg, *part.getBody()->getPartAt(i)); - } - - return found; + found = findSubTextParts(msg, part->getBody()->getPartAt(i)); } + + return found; } diff --git a/src/plainTextPart.cpp b/src/plainTextPart.cpp index 8ad24a51..5d82c981 100644 --- a/src/plainTextPart.cpp +++ b/src/plainTextPart.cpp @@ -57,11 +57,11 @@ const int plainTextPart::getPartCount() const } -void plainTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const +void plainTextPart::generateIn(ref /* message */, ref parent) const { // Create a new part ref part = vmime::create (); - parent.getBody()->appendPart(part); + parent->getBody()->appendPart(part); // Set header fields part->getHeader()->ContentType()->setValue(mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); @@ -73,15 +73,15 @@ void plainTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const } -void plainTextPart::parse(const bodyPart& /* message */, - const bodyPart& /* parent */, const bodyPart& textPart) +void plainTextPart::parse(ref /* message */, + ref /* parent */, ref textPart) { - m_text = textPart.getBody()->getContents()->clone().dynamicCast (); + m_text = textPart->getBody()->getContents()->clone().dynamicCast (); try { const contentTypeField& ctf = dynamic_cast - (*textPart.getHeader()->findField(fields::CONTENT_TYPE)); + (*textPart->getHeader()->findField(fields::CONTENT_TYPE)); m_charset = ctf.getCharset(); } diff --git a/tests/parser/htmlTextPartTest.cpp b/tests/parser/htmlTextPartTest.cpp new file mode 100644 index 00000000..35963a56 --- /dev/null +++ b/tests/parser/htmlTextPartTest.cpp @@ -0,0 +1,226 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2006 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 2 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" + + +#define VMIME_TEST_SUITE htmlTextPartTest +#define VMIME_TEST_SUITE_MODULE "Parser" + + +VMIME_TEST_SUITE_BEGIN + + VMIME_TEST_LIST_BEGIN + VMIME_TEST(testParseText) + VMIME_TEST(testParseEmbeddedObjectsCID) + VMIME_TEST(testParseEmbeddedObjectsLocation) + VMIME_TEST_LIST_END + + + static const vmime::string extractContent + (vmime::ref 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" +"\r\n" +"--LEVEL2\r\n" +"Content-Type: image/png; name=\"image.png\"\r\n" +"Content-Disposition: inline; filename=\"image.png\"\r\n" +"Content-ID: \r\n" +"\r\n" +"Image\r\n" +"--LEVEL2--\r\n" +"\r\n" +"--LEVEL1--\r\n" +""; + + vmime::ref msg = vmime::create (); + msg->parse(msgString); + + 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", "", 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: \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" +"\r\n" +"\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: \r\n" +"\r\n" +"Image2\r\n" +"--LEVEL2--\r\n" +"\r\n" +"--LEVEL1--\r\n" +""; + + vmime::ref msg = vmime::create (); + msg->parse(msgString); + + 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::ref obj; + + obj = htmlPart.findObject("image1@test"); + + 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("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: \r\n" +"\r\n" +"Image1\r\n" +"--LEVEL2\r\n" +"Content-Type: text/html; charset=\"x-ch2\"\r\n" +"\r\n" +"\r\n" +"--LEVEL2--\r\n" +"\r\n" +"--LEVEL1--\r\n" +""; + + vmime::ref msg = vmime::create (); + msg->parse(msgString); + + 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::ref obj; + + obj = htmlPart.findObject("http://www.vmime.org/test/image1.png"); + + 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/htmlTextPart.hpp b/vmime/htmlTextPart.hpp index 65f05a34..daee8376 100644 --- a/vmime/htmlTextPart.hpp +++ b/vmime/htmlTextPart.hpp @@ -131,6 +131,8 @@ public: const ref getObjectAt(const int pos) const; /** Embed an object and returns a string which identifies it. + * The returned identifier is suitable for use in the 'src' attribute + * of an tag. * * \deprecated Use the addObject() methods which take a 'contentHandler' * parameter type instead. @@ -143,6 +145,8 @@ public: const string addObject(const string& data, const mediaType& type); /** Embed an object and returns a string which identifies it. + * The returned identifier is suitable for use in the 'src' attribute + * of an tag. * * @param data object data * @param type data type @@ -152,6 +156,8 @@ public: const string addObject(ref data, const mediaType& type); /** Embed an object and returns a string which identifies it. + * The returned identifier is suitable for use in the 'src' attribute + * of an tag. * * @param data object data * @param enc data encoding @@ -161,6 +167,12 @@ public: */ const string addObject(ref data, const encoding& enc, const mediaType& type); + + const int getPartCount() const; + + void generateIn(ref message, ref parent) const; + void parse(ref message, ref parent, ref textPart); + private: ref m_plainText; @@ -174,10 +186,7 @@ private: bool findPlainTextPart(const bodyPart& part, const bodyPart& parent, const bodyPart& textPart); - const int getPartCount() const; - - void generateIn(bodyPart& message, bodyPart& parent) const; - void parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart); + static const string cleanId(const string& id); }; diff --git a/vmime/messageParser.hpp b/vmime/messageParser.hpp index 56161f89..b87edf42 100644 --- a/vmime/messageParser.hpp +++ b/vmime/messageParser.hpp @@ -148,8 +148,8 @@ private: void findAttachments(ref msg); - void findTextParts(const bodyPart& msg, const bodyPart& part); - bool findSubTextParts(const bodyPart& msg, const bodyPart& part); + void findTextParts(ref msg, ref part); + bool findSubTextParts(ref msg, ref part); }; diff --git a/vmime/plainTextPart.hpp b/vmime/plainTextPart.hpp index 45d03d67..1e3f7000 100644 --- a/vmime/plainTextPart.hpp +++ b/vmime/plainTextPart.hpp @@ -50,15 +50,15 @@ public: const ref getText() const; void setText(ref text); + const int getPartCount() const; + + void generateIn(ref message, ref parent) const; + void parse(ref message, ref parent, ref textPart); + private: ref m_text; charset m_charset; - - const int getPartCount() const; - - void generateIn(bodyPart& message, bodyPart& parent) const; - void parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart); }; diff --git a/vmime/textPart.hpp b/vmime/textPart.hpp index 6ec6a659..ecba481d 100644 --- a/vmime/textPart.hpp +++ b/vmime/textPart.hpp @@ -81,12 +81,28 @@ public: */ virtual void setText(ref text) = 0; -protected: - + /** Return the actual body parts this text part is composed of. + * For example, HTML parts are composed of two parts: one "text/html" + * part, and the plain text part "text/plain". + * + * @return number of body parts + */ virtual const int getPartCount() const = 0; - virtual void generateIn(bodyPart& message, bodyPart& parent) const = 0; - virtual void parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart) = 0; + /** Generate the text part(s) into the specified message. + * + * @param message the message + * @param parent body part into which generate this part + */ + virtual void generateIn(ref message, ref parent) const = 0; + + /** Parse the text part(s) from the specified message. + * + * @param message message containing the text part + * @param parent part containing the text part + * @param textPart actual text part + */ + virtual void parse(ref message, ref parent, ref textPart) = 0; };