aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--SConstruct1
-rw-r--r--src/htmlTextPart.cpp119
-rw-r--r--src/messageBuilder.cpp4
-rw-r--r--src/messageParser.cpp33
-rw-r--r--src/plainTextPart.cpp12
-rw-r--r--tests/parser/htmlTextPartTest.cpp226
-rw-r--r--vmime/htmlTextPart.hpp17
-rw-r--r--vmime/messageParser.hpp4
-rw-r--r--vmime/plainTextPart.hpp10
-rw-r--r--vmime/textPart.hpp24
11 files changed, 367 insertions, 88 deletions
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 <[email protected]>
+
+ * 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 <[email protected]>
* 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 <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;
}
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 <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())
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 <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;
+ bool found = false;
- for (int i = 0 ; !found && (i < part.getBody()->getPartCount()) ; ++i)
- {
- found = findSubTextParts(msg, *part.getBody()->getPartAt(i));
- }
-
- return found;
+ for (int i = 0 ; !found && (i < part->getBody()->getPartCount()) ; ++i)
+ {
+ 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 <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();
}
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 <[email protected]>
+//
+// 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
+
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 <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);
};
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 <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);
};
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 <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);
};
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 <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;
};