aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/htmlTextPart.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/htmlTextPart.cpp')
-rw-r--r--vmime-master/src/vmime/htmlTextPart.cpp568
1 files changed, 568 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/htmlTextPart.cpp b/vmime-master/src/vmime/htmlTextPart.cpp
new file mode 100644
index 0000000..a30023c
--- /dev/null
+++ b/vmime-master/src/vmime/htmlTextPart.cpp
@@ -0,0 +1,568 @@
+//
+// VMime library (http://www.vmime.org)
+// Copyright (C) 2002 Vincent Richard <vincent@vmime.org>
+//
+// 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 3 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/contentTypeField.hpp"
+#include "vmime/contentDisposition.hpp"
+#include "vmime/text.hpp"
+
+#include "vmime/emptyContentHandler.hpp"
+#include "vmime/stringContentHandler.hpp"
+
+#include "vmime/utility/outputStreamAdapter.hpp"
+
+
+namespace vmime {
+
+
+htmlTextPart::htmlTextPart()
+ : m_plainText(make_shared <emptyContentHandler>()),
+ m_text(make_shared <emptyContentHandler>()) {
+
+}
+
+
+htmlTextPart::~htmlTextPart() {
+
+}
+
+
+const mediaType htmlTextPart::getType() const {
+
+ return mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML);
+}
+
+
+size_t htmlTextPart::getPartCount() const {
+
+ return m_plainText->isEmpty() ? 1 : 2;
+}
+
+
+void htmlTextPart::generateIn(
+ const shared_ptr <bodyPart>& /* message */,
+ const shared_ptr <bodyPart>& parent
+) const {
+
+ // Plain text
+ if (!m_plainText->isEmpty()) {
+
+ // -- Create a new part
+ shared_ptr <bodyPart> part = make_shared <bodyPart>();
+ parent->getBody()->appendPart(part);
+
+ // -- Set contents
+ part->getBody()->setContents(
+ m_plainText,
+ mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN),
+ m_charset,
+ encoding::decide(m_plainText, m_charset, encoding::USAGE_TEXT)
+ );
+ }
+
+ // HTML text
+ // -- Create a new part
+ shared_ptr <bodyPart> htmlPart = make_shared <bodyPart>();
+
+ // -- Set contents
+ htmlPart->getBody()->setContents(
+ m_text,
+ mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML),
+ m_charset,
+ encoding::decide(m_text, m_charset, encoding::USAGE_TEXT)
+ );
+
+ // Handle the case we have embedded objects
+ if (!m_objects.empty()) {
+
+ // Create a "multipart/related" body part
+ shared_ptr <bodyPart> relPart = make_shared <bodyPart>();
+ 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 <shared_ptr <embeddedObject> >::const_iterator it = m_objects.begin() ;
+ it != m_objects.end() ; ++it) {
+
+ shared_ptr <bodyPart> objPart = make_shared <bodyPart>();
+ relPart->getBody()->appendPart(objPart);
+
+ string id = (*it)->getId();
+
+ if (id.length() >= 4 &&
+ (id[0] == 'c' || id[0] == 'C') &&
+ (id[1] == 'i' || id[1] == 'I') &&
+ (id[2] == 'd' || id[2] == 'D') &&
+ id[3] == ':') {
+
+ 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 <shared_ptr <const bodyPart> >& cidParts,
+ std::vector <shared_ptr <const bodyPart> >& locParts
+) {
+
+ for (size_t i = 0 ; i < part.getBody()->getPartCount() ; ++i) {
+
+ shared_ptr <const bodyPart> p = part.getBody()->getPartAt(i);
+
+ // For a part to be an embedded object, it must have either a
+ // Content-Id field or a Content-Location field.
+ if (p->getHeader()->hasField(fields::CONTENT_ID)) {
+ cidParts.push_back(p);
+ }
+
+ if (p->getHeader()->hasField(fields::CONTENT_LOCATION)) {
+ locParts.push_back(p);
+ }
+
+ findEmbeddedParts(*p, cidParts, locParts);
+ }
+}
+
+
+void htmlTextPart::addEmbeddedObject(
+ const bodyPart& part,
+ const string& id,
+ const embeddedObject::ReferenceType refType
+) {
+
+ // 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;
+
+ shared_ptr <const headerField> ctf =
+ part.getHeader()->findField(fields::CONTENT_TYPE);
+
+ if (ctf) {
+
+ type = *ctf->getValue <mediaType>();
+
+ } else {
+
+ // No "Content-type" field: assume "application/octet-stream".
+ }
+
+ m_objects.push_back(
+ make_shared <embeddedObject>(
+ vmime::clone(part.getBody()->getContents()),
+ part.getBody()->getEncoding(),
+ id,
+ type,
+ refType
+ )
+ );
+}
+
+
+void htmlTextPart::parse(
+ const shared_ptr <const bodyPart>& message,
+ const shared_ptr <const bodyPart>& parent,
+ const shared_ptr <const bodyPart>& textPart
+) {
+
+ // Search for possible embedded objects in the _whole_ message.
+ std::vector <shared_ptr <const bodyPart> > cidParts;
+ std::vector <shared_ptr <const bodyPart> > 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();
+
+ // Find charset
+ shared_ptr <const contentTypeField> ctf =
+ textPart->getHeader()->findField <contentTypeField>(fields::CONTENT_TYPE);
+
+ if (ctf && ctf->hasCharset()) {
+ m_charset = ctf->getCharset();
+ } else {
+ m_charset = charset();
+ }
+
+ // 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 <shared_ptr <const bodyPart> >::const_iterator p = cidParts.begin() ;
+ p != cidParts.end() ; ++p) {
+
+ const shared_ptr <const headerField> midField =
+ (*p)->getHeader()->findField(fields::CONTENT_ID);
+
+ const messageId mid = *midField->getValue <messageId>();
+
+ 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, mid.getId(), embeddedObject::REFERENCED_BY_ID);
+ }
+ }
+
+ for (std::vector <shared_ptr <const bodyPart> >::const_iterator p = locParts.begin() ;
+ p != locParts.end() ; ++p) {
+
+ const shared_ptr <const headerField> locField =
+ (*p)->getHeader()->findField(fields::CONTENT_LOCATION);
+
+ const text loc = *locField->getValue <text>();
+ const string locStr = loc.getWholeBuffer();
+
+ if (data.find(locStr) != string::npos) {
+
+ // This part is referenced in the HTML text.
+ // Add it to the embedded object list.
+ addEmbeddedObject(**p, locStr, embeddedObject::REFERENCED_BY_LOCATION);
+ }
+ }
+
+ // Extract plain text, if any.
+ if (!findPlainTextPart(*message, *parent, *textPart)) {
+
+ m_plainText = make_shared <emptyContentHandler>();
+ }
+}
+
+
+bool htmlTextPart::findPlainTextPart(
+ const bodyPart& part,
+ const bodyPart& parent,
+ const bodyPart& textPart
+) {
+
+ // We search for the nearest "multipart/alternative" part.
+ const shared_ptr <const headerField> ctf =
+ part.getHeader()->findField(fields::CONTENT_TYPE);
+
+ if (ctf) {
+
+ const mediaType type = *ctf->getValue <mediaType>();
+
+ if (type.getType() == mediaTypes::MULTIPART &&
+ type.getSubType() == mediaTypes::MULTIPART_ALTERNATIVE) {
+
+ shared_ptr <const bodyPart> foundPart;
+
+ for (size_t i = 0 ; i < part.getBody()->getPartCount() ; ++i) {
+
+ const shared_ptr <const bodyPart> p = part.getBody()->getPartAt(i);
+
+ if (p.get() == &parent || // if "text/html" is in "multipart/related"
+ p.get() == &textPart) { // if not...
+
+ foundPart = p;
+ }
+ }
+
+ if (foundPart) {
+
+ bool found = false;
+
+ // Now, search for the alternative plain text part
+ for (size_t i = 0 ; !found && i < part.getBody()->getPartCount() ; ++i) {
+
+ const shared_ptr <const bodyPart> p = part.getBody()->getPartAt(i);
+
+ const shared_ptr <const headerField> ctf =
+ p->getHeader()->findField(fields::CONTENT_TYPE);
+
+ if (ctf) {
+
+ const mediaType type = *ctf->getValue <mediaType>();
+
+ if (type.getType() == mediaTypes::TEXT &&
+ type.getSubType() == mediaTypes::TEXT_PLAIN) {
+
+ m_plainText = p->getBody()->getContents()->clone();
+ found = true;
+ }
+
+ } else {
+
+ // 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;
+ }
+ }
+
+ } else {
+
+ // No "Content-type" field.
+ }
+
+ bool found = false;
+
+ for (size_t 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;
+}
+
+
+shared_ptr <const contentHandler> htmlTextPart::getPlainText() const {
+
+ return m_plainText;
+}
+
+
+void htmlTextPart::setPlainText(const shared_ptr <contentHandler>& plainText) {
+
+ m_plainText = plainText->clone();
+}
+
+
+const shared_ptr <const contentHandler> htmlTextPart::getText() const {
+
+ return m_text;
+}
+
+
+void htmlTextPart::setText(const shared_ptr <contentHandler>& text) {
+
+ m_text = text->clone();
+}
+
+
+size_t htmlTextPart::getObjectCount() const {
+
+ return m_objects.size();
+}
+
+
+shared_ptr <const htmlTextPart::embeddedObject> htmlTextPart::getObjectAt(const size_t pos) const {
+
+ return m_objects[pos];
+}
+
+
+shared_ptr <const htmlTextPart::embeddedObject> htmlTextPart::findObject(const string& id) const
+{
+ for (std::vector <shared_ptr <embeddedObject> >::const_iterator o = m_objects.begin() ;
+ o != m_objects.end() ; ++o) {
+
+ if ((*o)->matchesId(id)) {
+ return *o;
+ }
+ }
+
+ return null;
+}
+
+
+bool htmlTextPart::hasObject(const string& id) const {
+
+ for (std::vector <shared_ptr <embeddedObject> >::const_iterator o = m_objects.begin() ;
+ o != m_objects.end() ; ++o) {
+
+ if ((*o)->matchesId(id)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+shared_ptr <const htmlTextPart::embeddedObject> htmlTextPart::addObject(
+ const shared_ptr <contentHandler>& data,
+ const vmime::encoding& enc,
+ const mediaType& type
+) {
+
+ const messageId mid(messageId::generateId());
+
+ shared_ptr <embeddedObject> obj = make_shared <embeddedObject>(
+ data, enc, mid.getId(), type, embeddedObject::REFERENCED_BY_ID
+ );
+
+ m_objects.push_back(obj);
+
+ return obj;
+}
+
+
+shared_ptr <const htmlTextPart::embeddedObject> htmlTextPart::addObject(
+ const shared_ptr <contentHandler>& data,
+ const mediaType& type
+) {
+
+ return addObject(data, encoding::decide(data), type);
+}
+
+
+shared_ptr <const htmlTextPart::embeddedObject> htmlTextPart::addObject(
+ const string& data,
+ const mediaType& type
+) {
+
+ shared_ptr <stringContentHandler> cts = make_shared <stringContentHandler>(data);
+ return addObject(cts, encoding::decide(cts), type);
+}
+
+
+
+//
+// htmlTextPart::embeddedObject
+//
+
+htmlTextPart::embeddedObject::embeddedObject(
+ const shared_ptr <contentHandler>& data,
+ const encoding& enc,
+ const string& id,
+ const mediaType& type,
+ const ReferenceType refType
+)
+ : m_data(vmime::clone(data)),
+ m_encoding(enc),
+ m_id(id),
+ m_type(type),
+ m_refType(refType) {
+
+}
+
+
+shared_ptr <const contentHandler> 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 string htmlTextPart::embeddedObject::getReferenceId() const {
+
+ if (m_refType == REFERENCED_BY_ID) {
+ return string("cid:") + m_id;
+ } else {
+ return m_id;
+ }
+}
+
+
+const mediaType htmlTextPart::embeddedObject::getType() const {
+
+ return m_type;
+}
+
+
+htmlTextPart::embeddedObject::ReferenceType htmlTextPart::embeddedObject::getReferenceType() const {
+
+ return m_refType;
+}
+
+
+bool htmlTextPart::embeddedObject::matchesId(const string& id) const {
+
+ if (m_refType == REFERENCED_BY_ID) {
+ return m_id == cleanId(id);
+ } else {
+ return m_id == id;
+ }
+}
+
+
+// static
+const string htmlTextPart::embeddedObject::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;
+ }
+}
+
+
+} // vmime