aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/body.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/body.cpp')
-rw-r--r--vmime-master/src/vmime/body.cpp1111
1 files changed, 1111 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/body.cpp b/vmime-master/src/vmime/body.cpp
new file mode 100644
index 0000000..a3875b9
--- /dev/null
+++ b/vmime-master/src/vmime/body.cpp
@@ -0,0 +1,1111 @@
+//
+// 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/bodyPart.hpp"
+#include "vmime/body.hpp"
+
+#include "vmime/contentTypeField.hpp"
+#include "vmime/text.hpp"
+
+#include "vmime/utility/random.hpp"
+
+#include "vmime/utility/seekableInputStreamRegionAdapter.hpp"
+#include "vmime/utility/outputStreamAdapter.hpp"
+
+#include "vmime/parserHelpers.hpp"
+
+#include "vmime/emptyContentHandler.hpp"
+#include "vmime/stringContentHandler.hpp"
+#include "vmime/streamContentHandler.hpp"
+
+
+namespace vmime {
+
+
+body::body()
+ : m_contents(make_shared <emptyContentHandler>()) {
+
+}
+
+
+body::~body() {
+
+}
+
+
+/*
+ * boundaryStart: will become the index for "\r\n--marker"
+ * boundaryEnd: will become the index after "marker", i.e. index for potential trailing "--", "\r\n", etc.
+ * return value: index for "marker"
+ */
+// static
+size_t body::findNextBoundaryPosition(
+ const shared_ptr <utility::parserInputStreamAdapter>& parser,
+ const string& boundary,
+ const size_t position,
+ const size_t end,
+ size_t* boundaryStart,
+ size_t* boundaryEnd
+) {
+
+ size_t pos = position;
+
+ for (; pos != npos && pos < end; ++pos) {
+
+ pos = parser->findNext(boundary, pos);
+
+ if (pos == npos) {
+ break; // not found
+ }
+
+ if (pos == 0) {
+ // Boundary is a prefix of another, continue the search (same for the other "continue"s)
+ continue;
+ }
+
+ // Ensure the bytes before boundary are "[LF]--": boundary should be
+ // at the beginning of a line, and should start with "--"
+ if (pos < 3) {
+ continue;
+ }
+ parser->seek(pos - 3);
+ if (!parser->matchBytes("\n--", 3)) {
+ continue;
+ }
+
+ parser->seek(pos + boundary.length());
+
+ const byte_t next = parser->peekByte();
+
+ // Boundary should be followed by a new line or a dash
+ if (!isspace(next) && next != '-') {
+ continue;
+ }
+
+ // Get rid of the "[CR]" just before "[LF]--", if any
+ size_t backwards = 0;
+ if (pos >= 4) {
+ parser->seek(pos - 4);
+ if (parser->peekByte() == '\r') {
+ ++backwards;
+ }
+ }
+
+ *boundaryStart = pos - backwards - 3;
+ *boundaryEnd = pos + boundary.length();
+
+ return pos;
+ }
+
+ return pos;
+}
+
+
+void body::parseImpl(
+ const parsingContext& ctx,
+ const shared_ptr <utility::parserInputStreamAdapter>& parser,
+ const size_t position,
+ const size_t end,
+ size_t* newPosition
+) {
+
+ removeAllParts();
+
+ m_prologText.clear();
+ m_epilogText.clear();
+
+ if (end == position) {
+
+ setParsedBounds(position, end);
+
+ if (newPosition) {
+ *newPosition = end;
+ }
+
+ return;
+ }
+
+ // Check whether the body is a MIME-multipart.
+ // If it is, also get (or try to guess) the boundary separator.
+ bool isMultipart = false;
+ string boundary;
+
+ shared_ptr <const contentTypeField> ctf =
+ m_part->getHeader()->findField <contentTypeField>(fields::CONTENT_TYPE);
+
+ if (ctf) {
+
+ const mediaType type = *ctf->getValue <mediaType>();
+
+ if (type.getType() == mediaTypes::MULTIPART) {
+
+ isMultipart = true;
+
+ if (ctf->hasBoundary()) {
+
+ boundary = ctf->getBoundary();
+
+ } else {
+
+ // No "boundary" parameter specified: we can try to
+ // guess it by scanning the body contents...
+ size_t pos = position;
+
+ parser->seek(pos);
+
+ if (pos + 2 < end && parser->matchBytes("--", 2)) {
+
+ pos += 2;
+
+ } else {
+
+ pos = parser->findNext("\n--", position);
+
+ if ((pos != npos) && (pos + 3 < end)) {
+ pos += 3; // skip \n--
+ }
+ }
+
+ if ((pos != npos) && (pos < end)) {
+
+ parser->seek(pos);
+
+ // Read some bytes after boundary separator
+ byte_t buffer[256];
+ const size_t bufferLen =
+ parser->read(buffer, std::min(end - pos, sizeof(buffer) / sizeof(buffer[0])));
+
+ buffer[sizeof(buffer) / sizeof(buffer[0]) - 1] = '\0';
+
+ // Skip transport padding bytes (SPACE or HTAB), if any
+ size_t boundarySkip = 0;
+
+ while (boundarySkip < bufferLen && parserHelpers::isSpace(buffer[boundarySkip])) {
+ ++boundarySkip;
+ }
+
+ // Extract boundary from buffer (stop at first CR or LF).
+ // We have to stop after a reasonnably long boundary length (100)
+ // not to take the whole body contents for a boundary...
+ byte_t boundaryBytes[100];
+ size_t boundaryLen = 0;
+
+ for (byte_t c = buffer[boundarySkip] ;
+ boundaryLen < bufferLen && boundaryLen < 100 && !(c == '\r' || c == '\n') ;
+ ++boundaryLen, c = buffer[boundarySkip + boundaryLen]) {
+
+ boundaryBytes[boundaryLen] = c;
+ }
+
+ if (boundaryLen >= 1 && boundaryLen < 100) {
+
+ // RFC #1521, Page 31:
+ // "...the boundary parameter, which consists of 1 to 70
+ // characters from a set of characters known to be very
+ // robust through email gateways, and NOT ending with
+ // white space..."
+ while (boundaryLen != 0 &&
+ parserHelpers::isSpace(boundaryBytes[boundaryLen - 1])) {
+
+ boundaryLen--;
+ }
+
+ if (boundaryLen >= 1) {
+ boundary = string(boundaryBytes, boundaryBytes + boundaryLen);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // This is a multi-part body
+ if (isMultipart && !boundary.empty()) {
+
+ size_t partStart = position;
+ size_t pos = position;
+
+ bool lastPart = false;
+
+ // Find the first boundary
+ size_t boundaryStart, boundaryEnd;
+ pos = findNextBoundaryPosition(parser, boundary, pos, end, &boundaryStart, &boundaryEnd);
+
+ for (int index = 0 ; !lastPart && (pos != npos) && (pos < end) ; ++index) {
+
+ size_t partEnd = boundaryStart;
+
+ // Check whether it is the last part (boundary terminated by "--")
+ parser->seek(boundaryEnd);
+
+ if (boundaryEnd + 1 < end && parser->matchBytes("--", 2)) {
+
+ lastPart = true;
+ boundaryEnd += 2;
+ }
+
+ // RFC 2046 §5.1.1 page 22: """If a boundary delimiter
+ // line appears to end with white space, the white
+ // space must be presumed to have been added by a
+ // gateway, and must be deleted."""
+ parser->seek(boundaryEnd);
+ boundaryEnd += parser->skipIf(parserHelpers::isSpaceOrTab, end);
+
+ // End of boundary line
+ if (boundaryEnd + 1 < end && parser->matchBytes("\r\n", 2)) {
+ boundaryEnd += 2;
+ } else if (boundaryEnd < end && parser->peekByte() == '\n') {
+ ++boundaryEnd;
+ } else if (boundaryEnd == end) {
+ } else {
+ /*
+ * RFC 2046 §5.1.1 page 19: """[...] optional
+ * linear whitespace, and a terminating
+ * CRLF.""" — junk handling is left
+ * unspecified, so we might as well skip it to
+ * facilitate broken mails.
+ */
+ boundaryEnd += parser->skipIf([](char_t c) { return c != '\n'; }, end);
+ pos = findNextBoundaryPosition(parser, boundary, boundaryEnd, end, &boundaryStart, &boundaryEnd);
+ --index;
+ continue;
+ }
+
+ if (index == 0) {
+
+ if (partEnd > partStart) {
+
+ vmime::text text;
+ text.parse(ctx, parser, partStart, partEnd);
+
+ m_prologText = text.getWholeBuffer();
+
+ } else {
+
+ m_prologText = "";
+ }
+
+ } else { // index > 0
+
+ shared_ptr <bodyPart> part = m_part->createChildPart();
+
+ // End before start may happen on empty bodyparts (directly
+ // successive boundaries without even a line-break)
+ if (partEnd < partStart) {
+ std::swap(partStart, partEnd);
+ }
+
+ part->parse(ctx, parser, partStart, partEnd, NULL);
+
+ m_parts.push_back(part);
+ }
+
+ partStart = boundaryEnd;
+
+ // Find the next boundary
+ pos = findNextBoundaryPosition(
+ parser, boundary, boundaryEnd, end, &boundaryStart, &boundaryEnd
+ );
+ }
+
+ m_contents = make_shared <emptyContentHandler>();
+
+ // Last part was not found: recover from missing boundary
+ if (!lastPart && pos == npos) {
+
+ shared_ptr <bodyPart> part = m_part->createChildPart();
+
+ try {
+ part->parse(ctx, parser, partStart, end);
+ } catch (std::exception&) {
+ throw;
+ }
+
+ m_parts.push_back(part);
+
+ // Treat remaining text as epilog
+ } else if (partStart < end) {
+
+ vmime::text text;
+ text.parse(ctx, parser, partStart, end);
+
+ m_epilogText = text.getWholeBuffer();
+ }
+
+ // Treat the contents as 'simple' data
+ } else {
+
+ encoding enc;
+
+ shared_ptr <const headerField> cef =
+ m_part->getHeader()->findField(fields::CONTENT_TRANSFER_ENCODING);
+
+ if (cef) {
+
+ enc = *cef->getValue <encoding>();
+
+ } else {
+
+ // Defaults to "7bit" (RFC-1521)
+ enc = vmime::encoding(encodingTypes::SEVEN_BIT);
+ }
+
+ // Extract the (encoded) contents
+ const size_t length = end - position;
+
+ shared_ptr <utility::inputStream> contentStream =
+ make_shared <utility::seekableInputStreamRegionAdapter>(
+ parser->getUnderlyingStream(), position, length
+ );
+
+ m_contents = make_shared <streamContentHandler>(contentStream, length, enc);
+ }
+
+ setParsedBounds(position, end);
+
+ if (newPosition) {
+ *newPosition = end;
+ }
+}
+
+
+text body::getActualPrologText(const generationContext& ctx) const {
+
+ const string& prologText =
+ m_prologText.empty()
+ ? (isRootPart()
+ ? ctx.getPrologText()
+ : NULL_STRING
+ )
+ : m_prologText;
+
+ if (prologText.empty()) {
+ return text();
+ } else {
+ return text(prologText, vmime::charset("us-ascii"));
+ }
+}
+
+
+text body::getActualEpilogText(const generationContext& ctx) const {
+
+ const string& epilogText =
+ m_epilogText.empty()
+ ? (isRootPart()
+ ? ctx.getEpilogText()
+ : NULL_STRING
+ )
+ : m_epilogText;
+
+ if (epilogText.empty()) {
+ return text();
+ } else {
+ return text(epilogText, vmime::charset("us-ascii"));
+ }
+}
+
+
+void body::generateImpl(
+ const generationContext& ctx,
+ utility::outputStream& os,
+ const size_t /* curLinePos */,
+ size_t* newLinePos
+) const {
+
+ // MIME-Multipart
+ if (getPartCount() != 0) {
+
+ string boundary;
+
+ if (!m_part) {
+
+ boundary = generateRandomBoundaryString();
+
+ } else {
+
+ // Use current boundary string, if specified. If no "Content-Type" field is
+ // present, or the boundary is not specified, generate a random one
+ shared_ptr <contentTypeField> ctf =
+ m_part->getHeader()->findField <contentTypeField>(fields::CONTENT_TYPE);
+
+ if (ctf) {
+
+ if (ctf->hasBoundary()) {
+
+ boundary = ctf->getBoundary();
+
+ } else {
+
+ // No boundary string specified
+ boundary = generateRandomBoundaryString();
+ }
+
+ } else {
+
+ // No Content-Type (and no boundary string specified)
+ boundary = generateRandomBoundaryString();
+ }
+ }
+
+ const text prologText = getActualPrologText(ctx);
+ const text epilogText = getActualEpilogText(ctx);
+
+ if (!prologText.isEmpty()) {
+
+ prologText.encodeAndFold(
+ ctx, os, 0, NULL,
+ text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE
+ );
+
+ os << CRLF;
+ }
+
+ os << "--" << boundary;
+
+ for (size_t p = 0 ; p < getPartCount() ; ++p) {
+
+ os << CRLF;
+
+ getPartAt(p)->generate(ctx, os, 0);
+
+ os << CRLF << "--" << boundary;
+ }
+
+ os << "--" << CRLF;
+
+ if (!epilogText.isEmpty()) {
+
+ epilogText.encodeAndFold(
+ ctx, os, 0, NULL,
+ text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE
+ );
+
+ os << CRLF;
+ }
+
+ if (newLinePos) {
+ *newLinePos = 0;
+ }
+
+ // Simple body
+ } else {
+
+ // Generate the contents
+ shared_ptr <contentHandler> contents = m_contents->clone();
+ contents->setContentTypeHint(getContentType());
+
+ contents->generate(os, getEncoding(), ctx.getMaxLineLength());
+ }
+}
+
+
+size_t body::getGeneratedSize(const generationContext& ctx) {
+
+ // MIME-Multipart
+ if (getPartCount() != 0) {
+
+ size_t size = 0;
+
+ // Size of parts and boundaries
+ for (size_t p = 0 ; p < getPartCount() ; ++p) {
+ size += 100; // boundary, CRLF...
+ size += getPartAt(p)->getGeneratedSize(ctx);
+ }
+
+ // Size of prolog/epilog text
+ const text prologText = getActualPrologText(ctx);
+
+ if (!prologText.isEmpty()) {
+
+ std::ostringstream oss;
+ utility::outputStreamAdapter osa(oss);
+
+ prologText.encodeAndFold(
+ ctx, osa, 0, NULL,
+ text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE
+ );
+
+ size += oss.str().size();
+ }
+
+ const text epilogText = getActualEpilogText(ctx);
+
+ if (!epilogText.isEmpty()) {
+
+ std::ostringstream oss;
+ utility::outputStreamAdapter osa(oss);
+
+ epilogText.encodeAndFold(
+ ctx, osa, 0, NULL,
+ text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE
+ );
+
+ size += oss.str().size();
+ }
+
+ return size;
+
+ // Simple body
+ } else {
+
+ if (getEncoding() == m_contents->getEncoding()) {
+
+ // No re-encoding has to be performed
+ return m_contents->getLength();
+
+ } else {
+
+ shared_ptr <utility::encoder::encoder> srcEncoder = m_contents->getEncoding().getEncoder();
+ shared_ptr <utility::encoder::encoder> dstEncoder = getEncoding().getEncoder();
+
+ return dstEncoder->getEncodedSize(srcEncoder->getDecodedSize(m_contents->getLength()));
+ }
+ }
+}
+
+
+/*
+ RFC #1521, Page 32:
+ 7.2.1. Multipart: The common syntax
+
+ "...Encapsulation boundaries must not appear within the
+ encapsulations, and must be no longer than 70 characters..."
+
+
+ boundary := 0*69<bchars> bcharsnospace
+
+ bchars := bcharsnospace / " "
+
+ bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / "+" /"_"
+ / "," / "-" / "." / "/" / ":" / "=" / "?"
+*/
+
+const string body::generateRandomBoundaryString() {
+
+ // 64 characters that can be _safely_ used in a boundary string
+ static const char bchars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+";
+
+ /*
+ RFC #1521, Page 19:
+
+ Since the hyphen character ("-") is represented as itself in the
+ Quoted-Printable encoding, care must be taken, when encapsulating a
+ quoted-printable encoded body in a multipart entity, to ensure that
+ the encapsulation boundary does not appear anywhere in the encoded
+ body. (A good strategy is to choose a boundary that includes a
+ character sequence such as "=_" which can never appear in a quoted-
+ printable body. See the definition of multipart messages later in
+ this document.)
+ */
+
+ char boundary[2 + 48 + 1] = { 0 };
+
+ boundary[0] = '=';
+ boundary[1] = '_';
+
+ // Generate a string of random characters
+ unsigned int r = utility::random::getTime();
+ unsigned int m = static_cast <unsigned int>(sizeof(unsigned int));
+
+ for (size_t i = 2 ; i < (sizeof(boundary) / sizeof(boundary[0]) - 1) ; ++i) {
+
+ boundary[i] = bchars[r & 63];
+ r >>= 6;
+
+ if (--m == 0) {
+ r = utility::random::getNext();
+ m = static_cast <unsigned int>(sizeof(unsigned int));
+ }
+ }
+
+ return string(boundary);
+}
+
+
+bool body::isValidBoundary(const string& boundary) {
+
+ static const string validChars("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'()+_,-./:=?");
+
+ const string::const_iterator end = boundary.end();
+ bool valid = false;
+
+ if (boundary.length() > 0 && boundary.length() < 70) {
+
+ const char last = *(end - 1);
+
+ if (!(last == ' ' || last == '\t' || last == '\n')) {
+
+ valid = true;
+
+ for (string::const_iterator i = boundary.begin() ; valid && i != end ; ++i) {
+ valid = (validChars.find_first_of(*i) != string::npos);
+ }
+ }
+ }
+
+ return valid;
+}
+
+
+//
+// Quick-access functions
+//
+
+
+void body::setContentType(const mediaType& type, const charset& chset) {
+
+ shared_ptr <contentTypeField> ctf =
+ dynamicCast <contentTypeField>(m_part->getHeader()->ContentType());
+
+ ctf->setValue(type);
+ ctf->setCharset(chset);
+}
+
+
+void body::setContentType(const mediaType& type) {
+
+ m_part->getHeader()->ContentType()->setValue(type);
+}
+
+
+const mediaType body::getContentType() const {
+
+ shared_ptr <const contentTypeField> ctf =
+ m_part->getHeader()->findField <contentTypeField>(fields::CONTENT_TYPE);
+
+ if (ctf) {
+
+ return *ctf->getValue <mediaType>();
+
+ } else {
+
+ // Defaults to "text/plain" (RFC-1521)
+ return mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN);
+ }
+}
+
+
+void body::setCharset(const charset& chset) {
+
+ shared_ptr <contentTypeField> ctf =
+ m_part->getHeader()->findField <contentTypeField>(fields::CONTENT_TYPE);
+
+ // If a Content-Type field exists, set charset
+ if (ctf) {
+
+ ctf->setCharset(chset);
+
+ // Else, create a new Content-Type field of default type "text/plain"
+ // and set charset on it
+ } else {
+
+ setContentType(mediaType(mediaTypes::TEXT, mediaTypes::TEXT_PLAIN), chset);
+ }
+}
+
+
+const charset body::getCharset() const {
+
+ const shared_ptr <const contentTypeField> ctf =
+ m_part->getHeader()->findField <contentTypeField>(fields::CONTENT_TYPE);
+
+ if (ctf) {
+
+ if (ctf->hasCharset()) {
+
+ return ctf->getCharset();
+
+ } else {
+
+ // Defaults to "us-ascii" (RFC-1521)
+ return vmime::charset(charsets::US_ASCII);
+ }
+
+ } else {
+
+ // Defaults to "us-ascii" (RFC-1521)
+ return vmime::charset(charsets::US_ASCII);
+ }
+}
+
+
+void body::setEncoding(const encoding& enc) {
+
+ m_part->getHeader()->ContentTransferEncoding()->setValue(enc);
+}
+
+
+const encoding body::getEncoding() const {
+
+ shared_ptr <const headerField> cef =
+ m_part->getHeader()->findField(fields::CONTENT_TRANSFER_ENCODING);
+
+ if (cef) {
+
+ return *cef->getValue <encoding>();
+
+ } else {
+
+ if (m_contents->isEncoded()) {
+ return m_contents->getEncoding();
+ }
+ }
+
+ // Defaults to "7bit" (RFC-1521)
+ return vmime::encoding(encodingTypes::SEVEN_BIT);
+}
+
+
+void body::setParentPart(bodyPart* parent) {
+
+ m_part = parent;
+
+ for (std::vector <shared_ptr <bodyPart> >::iterator it = m_parts.begin() ;
+ it != m_parts.end() ; ++it) {
+
+ shared_ptr <bodyPart> childPart = *it;
+ parent->importChildPart(childPart);
+ }
+}
+
+
+bool body::isRootPart() const {
+
+ return !m_part || !m_part->getParentPart();
+}
+
+
+shared_ptr <component> body::clone() const {
+
+ shared_ptr <body> bdy = make_shared <body>();
+
+ bdy->copyFrom(*this);
+
+ return bdy;
+}
+
+
+void body::copyFrom(const component& other) {
+
+ const body& bdy = dynamic_cast <const body&>(other);
+
+ m_prologText = bdy.m_prologText;
+ m_epilogText = bdy.m_epilogText;
+
+ m_contents = bdy.m_contents;
+
+ removeAllParts();
+
+ for (size_t p = 0 ; p < bdy.getPartCount() ; ++p) {
+
+ shared_ptr <bodyPart> part = m_part->createChildPart();
+
+ part->copyFrom(*bdy.getPartAt(p));
+
+ m_parts.push_back(part);
+ }
+}
+
+
+body& body::operator=(const body& other) {
+
+ copyFrom(other);
+ return *this;
+}
+
+
+const string& body::getPrologText() const {
+
+ return m_prologText;
+}
+
+
+void body::setPrologText(const string& prologText) {
+
+ m_prologText = prologText;
+}
+
+
+const string& body::getEpilogText() const {
+
+ return m_epilogText;
+}
+
+
+void body::setEpilogText(const string& epilogText) {
+
+ m_epilogText = epilogText;
+}
+
+
+const shared_ptr <const contentHandler> body::getContents() const {
+
+ return m_contents;
+}
+
+
+void body::setContents(const shared_ptr <const contentHandler>& contents) {
+
+ m_contents = contents;
+}
+
+
+void body::setContents(
+ const shared_ptr <const contentHandler>& contents,
+ const mediaType& type
+) {
+
+ m_contents = contents;
+
+ setContentType(type);
+}
+
+
+void body::setContents(
+ const shared_ptr <const contentHandler>& contents,
+ const mediaType& type,
+ const charset& chset
+) {
+
+ m_contents = contents;
+
+ setContentType(type, chset);
+}
+
+
+void body::setContents(
+ const shared_ptr <const contentHandler>& contents,
+ const mediaType& type,
+ const charset& chset,
+ const encoding& enc
+) {
+
+ m_contents = contents;
+
+ setContentType(type, chset);
+ setEncoding(enc);
+}
+
+
+void body::initNewPart(const shared_ptr <bodyPart>& part) {
+
+ // A part can be in only one body at the same time: if part is
+ // already attached to a parent part, remove it from the current
+ // parent part
+ if (part->getParentPart()) {
+ part->getParentPart()->getBody()->removePart(part);
+ }
+
+ if (m_part) {
+
+ m_part->importChildPart(part);
+
+ shared_ptr <header> hdr = m_part->getHeader();
+
+ // Check whether we have a boundary string
+ shared_ptr <contentTypeField> ctf =
+ hdr->findField <contentTypeField>(fields::CONTENT_TYPE);
+
+ if (ctf) {
+
+ if (ctf->hasBoundary()) {
+
+ const string boundary = ctf->getBoundary();
+
+ if (boundary.empty() || !isValidBoundary(boundary)) {
+ ctf->setBoundary(generateRandomBoundaryString());
+ }
+
+ } else {
+
+ // No "boundary" parameter: generate a random one.
+ ctf->setBoundary(generateRandomBoundaryString());
+ }
+
+ if (ctf->getValue <mediaType>()->getType() != mediaTypes::MULTIPART) {
+
+ // Warning: multi-part body but the Content-Type is
+ // not specified as "multipart/..."
+ }
+
+ } else {
+
+ // No "Content-Type" field: create a new one and generate
+ // a random boundary string.
+ ctf = hdr->getField <contentTypeField>(fields::CONTENT_TYPE);
+
+ ctf->setValue(mediaType(mediaTypes::MULTIPART, mediaTypes::MULTIPART_MIXED));
+ ctf->setBoundary(generateRandomBoundaryString());
+ }
+ }
+}
+
+
+void body::appendPart(const shared_ptr <bodyPart>& part) {
+
+ initNewPart(part);
+
+ m_parts.push_back(part);
+}
+
+
+void body::insertPartBefore(
+ const shared_ptr <bodyPart>& beforePart,
+ const shared_ptr <bodyPart>& part
+) {
+
+ initNewPart(part);
+
+ const std::vector <shared_ptr <bodyPart> >::iterator it = std::find(
+ m_parts.begin(), m_parts.end(), beforePart
+ );
+
+ if (it == m_parts.end()) {
+ throw exceptions::no_such_part();
+ }
+
+ m_parts.insert(it, part);
+}
+
+
+void body::insertPartBefore(
+ const size_t pos,
+ const shared_ptr <bodyPart>& part
+) {
+
+ initNewPart(part);
+
+ m_parts.insert(m_parts.begin() + pos, part);
+}
+
+
+void body::insertPartAfter(
+ const shared_ptr <bodyPart>& afterPart,
+ const shared_ptr <bodyPart>& part
+) {
+
+ initNewPart(part);
+
+ const std::vector <shared_ptr <bodyPart> >::iterator it = std::find(
+ m_parts.begin(), m_parts.end(), afterPart
+ );
+
+ if (it == m_parts.end()) {
+ throw exceptions::no_such_part();
+ }
+
+ m_parts.insert(it + 1, part);
+}
+
+
+void body::insertPartAfter(const size_t pos, const shared_ptr <bodyPart>& part) {
+
+ initNewPart(part);
+
+ m_parts.insert(m_parts.begin() + pos + 1, part);
+}
+
+
+void body::removePart(const shared_ptr <bodyPart>& part) {
+
+ const std::vector <shared_ptr <bodyPart> >::iterator it = std::find(
+ m_parts.begin(), m_parts.end(), part
+ );
+
+ if (it == m_parts.end()) {
+ throw exceptions::no_such_part();
+ }
+
+ m_parts.erase(it);
+}
+
+
+void body::removePart(const size_t pos) {
+
+ m_parts.erase(m_parts.begin() + pos);
+}
+
+
+void body::removeAllParts() {
+
+ m_parts.clear();
+}
+
+
+size_t body::getPartCount() const {
+
+ return m_parts.size();
+}
+
+
+bool body::isEmpty() const {
+
+ return m_parts.size() == 0;
+}
+
+
+shared_ptr <bodyPart> body::getPartAt(const size_t pos) {
+
+ return m_parts[pos];
+}
+
+
+const shared_ptr <const bodyPart> body::getPartAt(const size_t pos) const {
+
+ return m_parts[pos];
+}
+
+
+const std::vector <shared_ptr <const bodyPart> > body::getPartList() const {
+
+ std::vector <shared_ptr <const bodyPart> > list;
+
+ list.reserve(m_parts.size());
+
+ for (std::vector <shared_ptr <bodyPart> >::const_iterator it = m_parts.begin() ;
+ it != m_parts.end() ; ++it) {
+
+ list.push_back(*it);
+ }
+
+ return list;
+}
+
+
+const std::vector <shared_ptr <bodyPart> > body::getPartList() {
+
+ return m_parts;
+}
+
+
+const std::vector <shared_ptr <component> > body::getChildComponents() {
+
+ std::vector <shared_ptr <component> > list;
+
+ copy_vector(m_parts, list);
+
+ return list;
+}
+
+
+} // vmime