diff options
Diffstat (limited to 'vmime-master/src/vmime/body.cpp')
-rw-r--r-- | vmime-master/src/vmime/body.cpp | 1111 |
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 |