123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- // based on htmlTextPart, but with additions
- // we cannot use a class derived from htmlTextPart, since that class has alot of privates
- //
- // VMime library (http://www.vmime.org)
- // Copyright (C) 2002-2009 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 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 <memory>
- #include "mapiTextPart.h"
- #include <vmime/exception.hpp>
- #include <vmime/contentTypeField.hpp>
- #include <vmime/contentDisposition.hpp>
- #include <vmime/contentDispositionField.hpp>
- #include <vmime/text.hpp>
- #include <vmime/emptyContentHandler.hpp>
- #include <vmime/stringContentHandler.hpp>
- #include <vmime/utility/outputStreamAdapter.hpp>
- namespace vmime {
- mapiTextPart::mapiTextPart()
- : m_plainText(vmime::make_shared<emptyContentHandler>()),
- m_text(vmime::make_shared<emptyContentHandler>()),
- m_otherText(vmime::make_shared<emptyContentHandler>())
- {
- m_bHaveOtherCharset = false;
- }
- const mediaType mapiTextPart::getType() const
- {
- // TODO: fixme?
- if (m_text->isEmpty())
- return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN));
- else
- return (mediaType(mediaTypes::TEXT, mediaTypes::TEXT_HTML));
- }
- size_t mapiTextPart::getPartCount() const
- {
- int count = 0;
- if (!m_plainText->isEmpty())
- ++count;
- if (!m_text->isEmpty())
- ++count;
- if (!m_otherText->isEmpty())
- ++count;
- return count;
- }
- void mapiTextPart::generateIn(vmime::shared_ptr<bodyPart> /* message */,
- vmime::shared_ptr<bodyPart> parent) const
- {
- // Plain text
- if (!m_plainText->isEmpty())
- {
- // -- Create a new part
- auto part = vmime::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
- if (!m_text->isEmpty())
- {
- auto htmlPart = vmime::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
- auto relPart = vmime::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 (auto obj : m_objects) {
- auto objPart = vmime::make_shared<bodyPart>();
- relPart->getBody()->appendPart(objPart);
- std::string id = obj->getId();
- std::string name = obj->getName();
- if (id.substr(0, 4) == "CID:")
- id = id.substr(4);
- // throw an error when id and location are empty?
- objPart->getHeader()->ContentType()->setValue(obj->getType());
- if (!id.empty())
- objPart->getHeader()->ContentId()->setValue(messageId("<" + id + ">"));
- objPart->getHeader()->ContentDisposition()->setValue(contentDisposition(contentDispositionTypes::INLINE));
- objPart->getHeader()->ContentTransferEncoding()->setValue(obj->getEncoding());
- if (!obj->getLocation().empty())
- objPart->getHeader()->ContentLocation()->setValue(obj->getLocation());
- //encoding(encodingTypes::BASE64);
- if (!name.empty())
- vmime::dynamicCast<vmime::contentDispositionField>(objPart->getHeader()->ContentDisposition())->setFilename(vmime::word(name));
- objPart->getBody()->setContents(obj->getData()->clone());
- }
- }
- else
- {
- // Add the HTML part into the parent part
- parent->getBody()->appendPart(htmlPart);
- }
- } // if (html)
- // Other text
- if (!m_otherText->isEmpty())
- {
- // -- Create a new part
- auto otherPart = vmime::make_shared<bodyPart>();
- parent->getBody()->appendPart(otherPart);
- // used by ical
- if (!m_otherMethod.empty())
- {
- auto p = vmime::make_shared<vmime::parameter>("method");
- p->component::parse(m_otherMethod);
- vmime::dynamicCast<contentTypeField>(otherPart->getHeader()->ContentType())->appendParameter(p);
- }
- // -- Set contents
- otherPart->getBody()->setContents(m_otherText, m_otherMediaType, m_bHaveOtherCharset ? m_otherCharset : m_charset, m_otherEncoding);
- }
- }
- void mapiTextPart::findEmbeddedParts(const bodyPart& part,
- std::vector<vmime::shared_ptr<const bodyPart> > &cidParts,
- std::vector<vmime::shared_ptr<const bodyPart> > &locParts)
- {
- for (size_t i = 0 ; i < part.getBody()->getPartCount() ; ++i)
- {
- auto 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.
- 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 mapiTextPart::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;
- if (part.getHeader()->hasField(fields::CONTENT_TYPE)) {
- auto ctf = part.getHeader()->ContentType();
- type = *vmime::dynamicCast<const vmime::mediaType>(ctf->getValue());
- }
- // Else assume "application/octet-stream".
- m_objects.push_back(vmime::make_shared<embeddedObject>(
- vmime::dynamicCast<vmime::contentHandler>(part.getBody()->getContents()->clone()),
- part.getBody()->getEncoding(), id, type, string(), string()));
- }
- void mapiTextPart::parse(vmime::shared_ptr<const vmime::bodyPart> message,
- vmime::shared_ptr<const vmime::bodyPart> parent,
- vmime::shared_ptr<const vmime::bodyPart> textPart)
- {
- // Search for possible embedded objects in the _whole_ message.
- std::vector<vmime::shared_ptr<const vmime::bodyPart> > cidParts;
- std::vector<vmime::shared_ptr<const vmime::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();
- if (textPart->getHeader()->hasField(fields::CONTENT_TYPE)) {
- auto ctf = vmime::dynamicCast<vmime::contentTypeField>(textPart->getHeader()->findField(fields::CONTENT_TYPE));
- m_charset = ctf->getCharset();
- }
- // 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 (const auto &part : cidParts) {
- auto midField = part->getHeader()->findField(fields::CONTENT_ID);
- auto mid = *vmime::dynamicCast<const vmime::messageId>(midField->getValue());
- 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(*part, mid.getId());
- }
- for (const auto &part : locParts) {
- auto locField = part->getHeader()->findField(fields::CONTENT_LOCATION);
- auto loc = *vmime::dynamicCast<const vmime::text>(locField->getValue());
- 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(*part, locStr);
- }
- // Extract plain text, if any.
- if (!findPlainTextPart(*message, *parent, *textPart))
- {
- m_plainText = vmime::make_shared<vmime::emptyContentHandler>();
- }
- }
- bool mapiTextPart::findPlainTextPart(const bodyPart& part, const bodyPart& parent, const bodyPart& textPart)
- {
- // We search for the nearest "multipart/alternative" part.
- if (part.getHeader()->hasField(fields::CONTENT_TYPE)) {
- auto ctf = part.getHeader()->findField(fields::CONTENT_TYPE);
- auto type = *vmime::dynamicCast<const vmime::mediaType>(ctf->getValue());
- if (type.getType() == mediaTypes::MULTIPART &&
- type.getSubType() == mediaTypes::MULTIPART_ALTERNATIVE)
- {
- vmime::shared_ptr<const vmime::bodyPart> foundPart;
- for (size_t i = 0; i < part.getBody()->getPartCount(); ++i) {
- auto 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) {
- auto p = part.getBody()->getPartAt(i);
- if (p->getHeader()->hasField(fields::CONTENT_TYPE)) {
- auto ctf = p->getHeader()->findField(fields::CONTENT_TYPE);
- auto type = *vmime::dynamicCast<const vmime::mediaType>(ctf->getValue());
- if (type.getType() == mediaTypes::TEXT &&
- type.getSubType() == mediaTypes::TEXT_PLAIN)
- {
- m_plainText = p->getBody()->getContents()->clone();
- found = true;
- }
- }
- }
- // If we don't have found the plain text part here, it means that
- // it does not exist (the MUA which built this message probably
- // did not include it...).
- return found;
- }
- }
- }
- bool found = false;
- for (size_t i = 0; !found && i < part.getBody()->getPartCount(); ++i)
- found = findPlainTextPart(*part.getBody()->getPartAt(i), parent, textPart);
- return found;
- }
- void mapiTextPart::setCharset(const charset& ch)
- {
- m_charset = ch;
- }
- void mapiTextPart::setPlainText(vmime::shared_ptr<vmime::contentHandler> plainText)
- {
- m_plainText = plainText->clone();
- }
- void mapiTextPart::setOtherText(vmime::shared_ptr<vmime::contentHandler> otherText)
- {
- m_otherText = otherText->clone();
- }
- void mapiTextPart::setOtherContentType(const mediaType& type)
- {
- m_otherMediaType = type;
- }
- void mapiTextPart::setOtherContentEncoding(const encoding& enc)
- {
- m_otherEncoding = enc;
- }
- void mapiTextPart::setOtherMethod(const string& method)
- {
- m_otherMethod = method;
- }
- void mapiTextPart::setOtherCharset(const charset& ch)
- {
- m_otherCharset = ch;
- m_bHaveOtherCharset = true;
- }
- void mapiTextPart::setText(vmime::shared_ptr<vmime::contentHandler> text)
- {
- m_text = text->clone();
- }
- vmime::shared_ptr<const mapiTextPart::embeddedObject>
- mapiTextPart::findObject(const std::string &id_) const
- {
- const string id = cleanId(id_);
- for (const auto &obj : m_objects)
- if (obj->getId() == id)
- return obj;
- return vmime::null;
- }
- bool mapiTextPart::hasObject(const string& id_) const
- {
- const string id = cleanId(id_);
- for (const auto &obj : m_objects)
- if (obj->getId() == id)
- return true;
- return false;
- }
- const std::string
- mapiTextPart::addObject(vmime::shared_ptr<vmime::contentHandler> data,
- const vmime::encoding &enc, const vmime::mediaType &type)
- {
- const messageId mid(messageId::generateId());
- const string id = mid.getId();
- return "CID:" + addObject(data, enc, type, id);
- }
- const string mapiTextPart::addObject(vmime::shared_ptr<vmime::contentHandler> data,
- const vmime::mediaType &type)
- {
- return addObject(data, encoding::decide(data), type);
- }
- const string mapiTextPart::addObject(const string& data, const mediaType& type)
- {
- auto cts = vmime::make_shared<vmime::stringContentHandler>(data);
- return addObject(cts, encoding::decide(cts), type);
- }
- const string mapiTextPart::addObject(vmime::shared_ptr<vmime::contentHandler> data,
- const vmime::encoding &enc, const vmime::mediaType &type,
- const std::string &id, const std::string &name, const std::string &loc)
- {
- m_objects.push_back(vmime::make_shared<embeddedObject>(data, enc, id, type, name, loc));
- return (id);
- }
- // static
- const string mapiTextPart::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;
- }
- }
- //
- // mapiTextPart::embeddedObject
- //
- mapiTextPart::embeddedObject::embeddedObject(vmime::shared_ptr<vmime::contentHandler> data,
- const vmime::encoding &enc, const std::string &id,
- const vmime::mediaType &type, const std::string &name,
- const std::string &loc) :
- m_data(vmime::dynamicCast<vmime::contentHandler>(data->clone())),
- m_encoding(enc), m_id(id), m_type(type), m_name(name), m_loc(loc)
- {
- }
- } // vmime
|