// // VMime library (http://www.vmime.org) // Copyright (C) 2002-2005 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 "vmime/htmlTextPart.hpp" #include "vmime/exception.hpp" #include "vmime/emptyContentHandler.hpp" #include "vmime/stringContentHandler.hpp" namespace vmime { htmlTextPart::htmlTextPart() : m_plainText(vmime::create ()), m_text(vmime::create ()) { } htmlTextPart::~htmlTextPart() { } const mediaType htmlTextPart::getType() const { return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML)); } const int htmlTextPart::getPartCount() const { return (m_plainText->isEmpty() ? 1 : 2); } void htmlTextPart::generateIn(bodyPart& /* message */, bodyPart& parent) const { // Plain text if (!m_plainText->isEmpty()) { // -- Create a new part ref part = vmime::create (); parent.getBody()->appendPart(part); // -- Set header fields part->getHeader()->ContentType()->setValue(mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN)); part->getHeader()->ContentType()->setCharset(m_charset); part->getHeader()->ContentTransferEncoding()->setValue(encoding(encodingTypes::QUOTED_PRINTABLE)); // -- Set contents part->getBody()->setContents(m_plainText); } // HTML text // -- Create a new part ref htmlPart = vmime::create (); // -- Set header fields htmlPart->getHeader()->ContentType()->setValue(mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML)); htmlPart->getHeader()->ContentType()->setCharset(m_charset); htmlPart->getHeader()->ContentTransferEncoding()->setValue(encoding(encodingTypes::QUOTED_PRINTABLE)); // -- Set contents htmlPart->getBody()->setContents(m_text); // Handle the case we have embedded objects if (!m_objects.empty()) { // Create a "multipart/related" body part ref relPart = vmime::create (); parent.getBody()->appendPart(relPart); relPart->getHeader()->ContentType()-> setValue(mediaType(mediaTypes::MULTIPART, mediaTypes::MULTIPART_RELATED)); // Add the HTML part into this part relPart->getBody()->appendPart(htmlPart); // Also add objects into this part for (std::vector >::const_iterator it = m_objects.begin() ; it != m_objects.end() ; ++it) { ref objPart = vmime::create (); relPart->getBody()->appendPart(objPart); string id = (*it)->getId(); if (id.substr(0, 4) == "CID:") id = id.substr(4); objPart->getHeader()->ContentType()->setValue((*it)->getType()); objPart->getHeader()->ContentId()->setValue(messageId("<" + id + ">")); objPart->getHeader()->ContentDisposition()->setValue(contentDisposition(contentDispositionTypes::INLINE)); objPart->getHeader()->ContentTransferEncoding()->setValue((*it)->getEncoding()); //encoding(encodingTypes::BASE64); objPart->getBody()->setContents((*it)->getData()->clone()); } } else { // Add the HTML part into the parent part parent.getBody()->appendPart(htmlPart); } } void htmlTextPart::findEmbeddedParts(const bodyPart& part, std::vector >& cidParts, std::vector >& locParts) { for (int i = 0 ; i < part.getBody()->getPartCount() ; ++i) { ref p = part.getBody()->getPartAt(i); try { p->getHeader()->findField(fields::CONTENT_ID); cidParts.push_back(p); } 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. } } findEmbeddedParts(*p, cidParts, locParts); } } void htmlTextPart::addEmbeddedObject(const bodyPart& part, const string& id) { mediaType type; try { const ref ctf = part.getHeader()->ContentType(); type = ctf->getValue(); } catch (exceptions::no_such_field) { // No "Content-type" field: assume "application/octet-stream". } m_objects.push_back(vmime::create (part.getBody()->getContents()->clone().dynamicCast (), part.getBody()->getEncoding(), id, type)); } void htmlTextPart::parse(const bodyPart& message, const bodyPart& parent, const bodyPart& textPart) { // Search for possible embedded objects in the _whole_ message. std::vector > cidParts; std::vector > locParts; findEmbeddedParts(message, cidParts, locParts); // Extract HTML text std::ostringstream oss; utility::outputStreamAdapter adapter(oss); textPart.getBody()->getContents()->extract(adapter); const string data = oss.str(); m_text = textPart.getBody()->getContents()->clone(); try { const ref ctf = textPart.getHeader()->findField(fields::CONTENT_TYPE).dynamicCast (); m_charset = ctf->getCharset(); } catch (exceptions::no_such_field) { // No "Content-type" field. } catch (exceptions::no_such_parameter) { // No "charset" parameter. } // Extract embedded objects. The algorithm is quite simple: for each previously // found inline part, we check if its CID/Location is contained in the HTML text. for (std::vector >::const_iterator p = cidParts.begin() ; p != cidParts.end() ; ++p) { const ref midField = (*p)->getHeader()->findField(fields::CONTENT_ID).dynamicCast (); const string searchFor("CID:" + midField->getValue().getId()); if (data.find(searchFor) != string::npos) { // This part is referenced in the HTML text. // Add it to the embedded object list. addEmbeddedObject(**p, "CID:" + midField->getValue().getId()); } } for (std::vector >::const_iterator p = locParts.begin() ; p != locParts.end() ; ++p) { const ref locField = (*p)->getHeader()->findField(fields::CONTENT_LOCATION).dynamicCast (); if (data.find(locField->getValue()) != string::npos) { // This part is referenced in the HTML text. // Add it to the embedded object list. addEmbeddedObject(**p, locField->getValue()); } } // Extract plain text, if any. if (!findPlainTextPart(message, parent, textPart)) { m_plainText = vmime::create (); } } bool htmlTextPart::findPlainTextPart(const bodyPart& part, const bodyPart& parent, const bodyPart& textPart) { // We search for the nearest "multipart/alternative" part. try { const ref ctf = part.getHeader()->findField(fields::CONTENT_TYPE).dynamicCast (); if (ctf->getValue().getType() == mediaTypes::MULTIPART && ctf->getValue().getSubType() == mediaTypes::MULTIPART_ALTERNATIVE) { ref foundPart = NULL; for (int i = 0 ; i < part.getBody()->getPartCount() ; ++i) { const ref p = part.getBody()->getPartAt(i); if (p == &parent || // if "text/html" is in "multipart/related" p == &textPart) // if not... { foundPart = p; } } if (foundPart) { bool found = false; // Now, search for the alternative plain text part for (int i = 0 ; !found && i < part.getBody()->getPartCount() ; ++i) { const ref p = part.getBody()->getPartAt(i); try { const ref ctf = p->getHeader()->findField(fields::CONTENT_TYPE).dynamicCast (); if (ctf->getValue().getType() == mediaTypes::TEXT && ctf->getValue().getSubType() == mediaTypes::TEXT_PLAIN) { m_plainText = p->getBody()->getContents()->clone(); found = true; } } catch (exceptions::no_such_field) { // No "Content-type" field. } } // 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); } } } catch (exceptions::no_such_field) { // No "Content-type" field. } bool found = false; for (int i = 0 ; !found && i < part.getBody()->getPartCount() ; ++i) { found = findPlainTextPart(*part.getBody()->getPartAt(i), parent, textPart); } return (found); } const charset& htmlTextPart::getCharset() const { return (m_charset); } void htmlTextPart::setCharset(const charset& ch) { m_charset = ch; } const ref htmlTextPart::getPlainText() const { return (m_plainText); } void htmlTextPart::setPlainText(ref plainText) { m_plainText = plainText->clone(); } const ref htmlTextPart::getText() const { return (m_text); } void htmlTextPart::setText(ref text) { m_text = text->clone(); } const int htmlTextPart::getObjectCount() const { return (m_objects.size()); } const ref htmlTextPart::getObjectAt(const int pos) const { return (m_objects[pos]); } const ref htmlTextPart::findObject(const string& id) const { for (std::vector >::const_iterator o = m_objects.begin() ; o != m_objects.end() ; ++o) { if ((*o)->getId() == id) return (*o); } throw exceptions::no_object_found(); } const bool htmlTextPart::hasObject(const string& id) const { for (std::vector >::const_iterator o = m_objects.begin() ; o != m_objects.end() ; ++o) { if ((*o)->getId() == id) return (true); } return (false); } const string htmlTextPart::addObject(ref data, const vmime::encoding& enc, const mediaType& type) { const messageId mid(messageId::generateId()); const string id = "CID:" + mid.getId(); m_objects.push_back(vmime::create (data, enc, id, type)); return (id); } const string htmlTextPart::addObject(ref data, const mediaType& 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)); } // // htmlTextPart::embeddedObject // htmlTextPart::embeddedObject::embeddedObject (ref data, const encoding& enc, const string& id, const mediaType& type) : m_data(data->clone().dynamicCast ()), m_encoding(enc), m_id(id), m_type(type) { } const ref htmlTextPart::embeddedObject::getData() const { return (m_data); } const vmime::encoding& htmlTextPart::embeddedObject::getEncoding() const { return (m_encoding); } const string& htmlTextPart::embeddedObject::getId() const { return (m_id); } const mediaType& htmlTextPart::embeddedObject::getType() const { return (m_type); } } // vmime