Fixed bugs in MHTML code + unit tests.
This commit is contained in:
parent
edca17af10
commit
9a42c8bca4
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
226
tests/parser/htmlTextPartTest.cpp
Normal file
226
tests/parser/htmlTextPartTest.cpp
Normal 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
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user