Fixed bugs in MHTML code + unit tests.

This commit is contained in:
Vincent Richard 2006-07-13 16:32:39 +00:00
parent edca17af10
commit 9a42c8bca4
11 changed files with 367 additions and 88 deletions

View File

@ -2,6 +2,11 @@
VERSION 0.8.1cvs
================
2006-07-13 Vincent Richard <vincent@vincent-richard.net>
* 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 <vincent@vincent-richard.net>
* Added vmime::net::folder::destroy() to delete folders on IMAP and

View File

@ -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',

View File

@ -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 <bodyPart> /* message */, ref <bodyPart> parent) const
{
// Plain text
if (!m_plainText->isEmpty())
{
// -- Create a new part
ref <bodyPart> part = vmime::create <bodyPart>();
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 <bodyPart> relPart = vmime::create <bodyPart>();
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 <const bodyPart> 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 <const bodyPart> message, ref <const bodyPart> parent, ref <const bodyPart> textPart)
{
// Search for possible embedded objects in the _whole_ message.
std::vector <ref <const bodyPart> > cidParts;
std::vector <ref <const bodyPart> > 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 <const contentTypeField> ctf =
textPart.getHeader()->findField(fields::CONTENT_TYPE).dynamicCast <contentTypeField>();
textPart->getHeader()->findField(fields::CONTENT_TYPE).dynamicCast <contentTypeField>();
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 messageId>();
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 <emptyContentHandler>();
}
@ -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 <const contentHandler> htmlTextPart::getPlainText() const
{
return (m_plainText);
return m_plainText;
}
@ -367,7 +372,7 @@ void htmlTextPart::setPlainText(ref <contentHandler> plainText)
const ref <const contentHandler> htmlTextPart::getText() const
{
return (m_text);
return m_text;
}
@ -379,39 +384,43 @@ void htmlTextPart::setText(ref <contentHandler> text)
const int htmlTextPart::getObjectCount() const
{
return (m_objects.size());
return m_objects.size();
}
const ref <const htmlTextPart::embeddedObject> htmlTextPart::getObjectAt(const int pos) const
{
return (m_objects[pos]);
return m_objects[pos];
}
const ref <const htmlTextPart::embeddedObject> htmlTextPart::findObject(const string& id) const
const ref <const htmlTextPart::embeddedObject> htmlTextPart::findObject(const string& id_) const
{
const string id = cleanId(id_);
for (std::vector <ref <embeddedObject> >::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 <ref <embeddedObject> >::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 <contentHandler> 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 <embeddedObject>(data, enc, id, type));
return (id);
return "CID:" + id;
}
const string htmlTextPart::addObject(ref <contentHandler> 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 <stringContentHandler> cts = vmime::create <stringContentHandler>(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 <const contentHandler> 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;
}

View File

@ -105,12 +105,12 @@ ref <message> 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())

View File

@ -100,7 +100,7 @@ void messageParser::parse(ref <const message> msg)
findAttachments(msg);
// Text parts
findTextParts(*msg, *msg);
findTextParts(msg, msg);
}
@ -110,11 +110,11 @@ void messageParser::findAttachments(ref <const message> msg)
}
void messageParser::findTextParts(const bodyPart& msg, const bodyPart& part)
void messageParser::findTextParts(ref <const bodyPart> msg, ref <const bodyPart> 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<contentTypeField&>
(*msg.getHeader()->findField(fields::CONTENT_TYPE));
(*msg->getHeader()->findField(fields::CONTENT_TYPE));
const mediaType ctfType =
*ctf.getValue().dynamicCast <const mediaType>();
@ -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 <const bodyPart> msg, ref <const bodyPart> 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 <ref <const bodyPart> > textParts;
for (int i = 0 ; i < part.getBody()->getPartCount() ; ++i)
for (int i = 0 ; i < part->getBody()->getPartCount() ; ++i)
{
const ref <const bodyPart> p = part.getBody()->getPartAt(i);
const ref <const bodyPart> p = part->getBody()->getPartAt(i);
try
{
@ -200,7 +200,7 @@ bool messageParser::findSubTextParts(const bodyPart& msg, const bodyPart& part)
try
{
ref <textPart> 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;
}

View File

@ -57,11 +57,11 @@ const int plainTextPart::getPartCount() const
}
void plainTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const
void plainTextPart::generateIn(ref <bodyPart> /* message */, ref <bodyPart> parent) const
{
// Create a new part
ref <bodyPart> part = vmime::create <bodyPart>();
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 <const bodyPart> /* message */,
ref <const bodyPart> /* parent */, ref <const bodyPart> textPart)
{
m_text = textPart.getBody()->getContents()->clone().dynamicCast <contentHandler>();
m_text = textPart->getBody()->getContents()->clone().dynamicCast <contentHandler>();
try
{
const contentTypeField& ctf = dynamic_cast<contentTypeField&>
(*textPart.getHeader()->findField(fields::CONTENT_TYPE));
(*textPart->getHeader()->findField(fields::CONTENT_TYPE));
m_charset = ctf.getCharset();
}

View File

@ -0,0 +1,226 @@
//
// VMime library (http://www.vmime.org)
// Copyright (C) 2002-2006 Vincent Richard <vincent@vincent-richard.net>
//
// 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 <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::ref <vmime::message> msg = vmime::create <vmime::message>();
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", "<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::ref <vmime::message> msg = vmime::create <vmime::message>();
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 <const vmime::htmlTextPart::embeddedObject> 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: <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::ref <vmime::message> msg = vmime::create <vmime::message>();
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 <const vmime::htmlTextPart::embeddedObject> 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

View File

@ -131,6 +131,8 @@ public:
const ref <const embeddedObject> 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 <img> 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 <img> tag.
*
* @param data object data
* @param type data type
@ -152,6 +156,8 @@ public:
const string addObject(ref <contentHandler> 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 <img> tag.
*
* @param data object data
* @param enc data encoding
@ -161,6 +167,12 @@ public:
*/
const string addObject(ref <contentHandler> data, const encoding& enc, const mediaType& type);
const int getPartCount() const;
void generateIn(ref <bodyPart> message, ref <bodyPart> parent) const;
void parse(ref <const bodyPart> message, ref <const bodyPart> parent, ref <const bodyPart> textPart);
private:
ref <contentHandler> 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);
};

View File

@ -148,8 +148,8 @@ private:
void findAttachments(ref <const message> msg);
void findTextParts(const bodyPart& msg, const bodyPart& part);
bool findSubTextParts(const bodyPart& msg, const bodyPart& part);
void findTextParts(ref <const bodyPart> msg, ref <const bodyPart> part);
bool findSubTextParts(ref <const bodyPart> msg, ref <const bodyPart> part);
};

View File

@ -50,15 +50,15 @@ public:
const ref <const contentHandler> getText() const;
void setText(ref <contentHandler> text);
const int getPartCount() const;
void generateIn(ref <bodyPart> message, ref <bodyPart> parent) const;
void parse(ref <const bodyPart> message, ref <const bodyPart> parent, ref <const bodyPart> textPart);
private:
ref <contentHandler> 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);
};

View File

@ -81,12 +81,28 @@ public:
*/
virtual void setText(ref <contentHandler> 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 <bodyPart> message, ref <bodyPart> 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 <const bodyPart> message, ref <const bodyPart> parent, ref <const bodyPart> textPart) = 0;
};