Files
2026-06-01 12:46:52 +02:00

776 lines
28 KiB
C++

//
// Copyright Marshall Clow 2009-2010
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
//
#ifndef _BOOST_MIME_HPP
#define _BOOST_MIME_HPP
#include <list>
#include <string>
#include <vector>
#include <iosfwd>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/spirit/include/phoenix.hpp> // pulls in all of Phoenix
#include <boost/spirit/include/support_istream_iterator.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/format.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string.hpp>
// #define DUMP_MIME_DATA 1
namespace boost { namespace mime {
// Errors are reported using this exception class
class mime_parsing_error : public std::runtime_error {
public:
explicit mime_parsing_error ( const std::string & msg ) : std::runtime_error ( msg ) {}
};
template <class traits> class basic_mime;
namespace detail {
static const char *k_crlf = "\015\012";
static const char *k_package_name = "Proposed.Boost.Mime";
static const char *k_package_version = "0.1";
static const char *k_content_type_header = "Content-Type";
static const char *k_mime_version_header = "Mime-Version";
struct default_types {
typedef std::string string_type;
// typedef std::pair < std::string, string_type > header_type;
typedef std::vector<char> body_type;
};
template<typename string_type>
struct find_mime_header {
find_mime_header ( const char *str ) : searchFor ( str ) {}
bool operator () ( const std::pair<std::string, string_type> &val ) const { return boost::iequals ( val.first, searchFor ); }
private:
const char *searchFor;
};
#ifdef DUMP_MIME_DATA
struct tracer {
tracer ( const char *fn ) : fn_ (fn) { std::cout << "->" << fn_ << std::endl; }
~tracer () { std::cout << "<-" << fn_ << std::endl; }
const char *fn_;
};
#else
struct tracer {
tracer ( const char * ) {}
~tracer () {}
};
#endif
// Parsing a Content-Type header
typedef std::pair<std::string, std::string> phrase_t;
typedef std::vector < phrase_t > phrase_container_t;
struct mime_content_type {
std::string type;
std::string sub_type;
phrase_container_t phrases;
};
namespace qi = boost::spirit::qi;
namespace phx = boost::phoenix;
using boost::spirit::_val;
using boost::spirit::_1;
template <typename Iterator, typename Container>
struct mime_header_parser : qi::grammar<Iterator, Container()>
{
mime_header_parser() : mime_header_parser::base_type(mime_headerList)
{
mime_headerList = *(mime_header) >> crlf;
mime_header = token >> qi::lit ( ':' ) >> value >> crlf;
token = qi::char_("a-zA-Z") >> *qi::char_("a-zA-Z_0-9\\-");
// In Classifieds/000001, a header begins with a CRLF
value = ( valuePart [ _val = _1 ] | qi::eps ) >> *(valueCont [ _val += "\015\012" + _1 ]);
valueCont = crlf >> contWS [ _val += _1 ] >> valuePart [ _val += _1 ];
valuePart = +qi::char_("\t -~");
contWS = +qi::char_( " \t");
crlf = qi::lit ( k_crlf );
/* mime_headerList.name("mime-header-list");
mime_header.name ("mime-header");
token.name ("mime-token");
valuePart.name ("mime-value-part");
value.name ("mime-value");
qi::on_error<qi::fail> ( mime_headerList,
std::cout
<< phoenix::val("Error! Expecting ")
<< qi::labels::_4
<< phoenix::val(" here: \"")
<< phoenix::construct<std::string>(qi::labels::_3, qi::labels::_2)
<< phoenix::val("\"")
<< std::endl
);
*/
}
qi::rule<Iterator, Container()> mime_headerList ;
qi::rule<Iterator, typename Container::value_type()> mime_header;
qi::rule<Iterator, std::string()> token, value, valueCont, valuePart, contWS;
qi::rule<Iterator> crlf;
};
template<typename Container, typename Iterator>
static Container read_headers ( Iterator &begin, Iterator end ) {
tracer t ( __func__ );
Container retVal;
mime_header_parser<Iterator, Container> mh_parser;
bool b = qi::parse ( begin, end, mh_parser, retVal );
if ( !b )
throw mime_parsing_error ( "Failed to parse headers" );
#ifdef DUMP_MIME_DATA
std::cout << "******Headers*******" << std::endl;
for ( typename Container::const_iterator iter = retVal.begin (); iter != retVal.end (); ++iter ) {
std::string val = iter->second;
size_t idx;
while ( std::string::npos != ( idx = val.find ( k_crlf )))
val.replace ( idx, std::strlen ( k_crlf ), "\n" );
std::cout << iter->first << ": " << val << std::endl;
}
std::cout << std::endl << "******Headers*******" << std::endl;
#endif
return retVal;
}
// The structure of a Content-Type mime header is taken from RFC 2045
// http://www.ietf.org/rfc/rfc2045.txt, section 5.1
template <typename Iterator>
struct mime_content_type_parser : qi::grammar<Iterator, mime_content_type()>
{
mime_content_type_parser() : mime_content_type_parser::base_type(content_type_header)
{
content_type_header = *qi::lit(' ') >> part >> '/' >> sub_part >> *phrase ;
part = token | extension_token;
sub_part = token | extension_token;
phrase = qi::lit ( ';' ) >> +ws >> attribute >> '=' >> value >> *ws;
ws = qi::char_( " \t") | line_sep | comment;
line_sep = qi::lexeme[ qi::lit ( k_crlf ) ];
attribute = token.alias();
value = token | quoted_string;
token = +(qi::char_( " -~" ) - qi::char_( " ()<>@,;:\\\"/[]?=" ));
comment = qi::lit ('(') >> +(qi::char_(" -~" ) - ')' ) >> qi::lit(')');
quoted_string = qi::lit ('"') >> +(qi::char_(" -~" ) - '"' ) >> qi::lit('"');
extension_token = qi::char_ ( "Xx" ) >> qi::lit ( '-' ) >> token;
}
qi::rule<Iterator, mime_content_type()> content_type_header ;
qi::rule<Iterator, phrase_t()> phrase ;
qi::rule<Iterator, std::string()> part, sub_part, token, attribute, value, quoted_string, extension_token;
qi::rule<Iterator> ws, line_sep, comment;
};
template<typename string_type>
mime_content_type parse_content_type ( const string_type &theHeader ) {
tracer t ( __func__ );
mime_content_type retVal;
typename string_type::const_iterator first = theHeader.begin ();
mime_content_type_parser<typename string_type::const_iterator> ct_parser;
bool b = qi::parse ( first, theHeader.end (), ct_parser, retVal );
if (!b)
throw mime_parsing_error ( "Failed to parse the 'Content-Type' header" );
return retVal;
}
template<typename string_type>
static string_type get_ct_value ( const string_type &ctString, const char *key ) {
tracer t ( __func__ );
mime_content_type mc = parse_content_type ( ctString );
for ( phrase_container_t::const_iterator iter = mc.phrases.begin (); iter != mc.phrases.end (); ++iter )
if ( boost::iequals ( iter->first, key ))
return iter->second;
throw std::runtime_error ( str ( boost::format ( "Couldn't find Content-Type phrase (%s)" ) % key ));
}
// Replace this with a spirit thing later.
// we're looking for '; boundary="<somevalue>".*'
std::string get_boundary ( const std::string &ctString ) {
tracer t ( __func__ );
return get_ct_value ( ctString, "boundary" );
}
// Read the body of a multipart
// Return a Container of containers, where the first is the actual body,
// and the rest are the sub-parts.
// Note that the body of the multipart can be empty.
// If this is the case, then the first separator need not have a crlf
// if the marker is "abcde", we could have:
// Note that the separators are really CRLF--abcdeCRLF and CRLF--abcde--CRLF
//
// multipart body
// --abcde
// sub part #1
// --abcde
// sub part #2
// --abcde--
//
// ** or **
// In this case, the first separator is --abcdeCRLF
//
// --abcde (no multipart body!)
// sub part #1
// --abcde
// sub part #2
// --abcde--
typedef std::vector<char> sub_part_t;
typedef std::vector<sub_part_t> sub_parts_t;
template<typename bodyContainer>
struct multipart_body_type {
bool prolog_is_missing;
bodyContainer body_prolog;
sub_parts_t sub_parts;
bodyContainer body_epilog;
};
// Parse a mulitpart body.
// Either "--boundaryCRLF" -- in which case the body is empty
// or <some sequence of chars> "CRLF--boundaryCRLF" -- in which case we return the sequence
//
// I am deliberately not checking for a termination separator here
template <typename Iterator, typename Container>
struct multipart_body_parser : qi::grammar<Iterator, Container()> {
multipart_body_parser( const std::string &boundary, bool &isMissing ) : multipart_body_parser::base_type(mimeBody), m_is_missing ( isMissing ) {
m_is_missing = false;
// Thanks to Michael Caisse for the hint to get this working
mimeBody %= bareSep [ phx::ref ( m_is_missing ) = true ] | (+(qi::char_ - sep) >> sep ) ;
bareSep = qi::lit("--") >> boundary >> crlf;
sep = crlf >> bareSep;
crlf = qi::lit ( k_crlf );
}
bool &m_is_missing;
qi::rule<Iterator, Container()> mimeBody;
qi::rule<Iterator> bareSep, sep, crlf;
};
// Break up a multi-part into its' constituent sub parts.
template <typename Iterator, typename Container>
struct multipart_part_parser : qi::grammar<Iterator, Container()> {
multipart_part_parser( const std::string &boundary ) : multipart_part_parser::base_type(mimeParts) {
mimeParts = (+(qi::char_ - sep) % (sep >> crlf)) > terminator ;
sep = crlf >> qi::lit("--") >> boundary ;
terminator = sep >> qi::lit("--") >> crlf ;
crlf = qi::lit ( k_crlf );
}
qi::rule<Iterator, Container()> mimeParts;
qi::rule<Iterator> sep, terminator, crlf;
};
template<typename Iterator, typename bodyContainer>
static void read_multipart_body ( Iterator &begin, Iterator end, multipart_body_type<bodyContainer> &mp_body, const std::string &separator ) {
tracer t ( __func__ );
typedef bodyContainer innerC;
innerC mpBody;
multipart_body_parser <Iterator, innerC> mb_parser (separator, mp_body.prolog_is_missing );
if ( !qi::parse ( begin, end, mb_parser, mp_body.body_prolog ))
throw mime_parsing_error ("Failed to parse mime body(1)");
multipart_part_parser <Iterator, sub_parts_t> mp_parser ( separator );
if ( !qi::parse ( begin, end, mp_parser, mp_body.sub_parts ))
throw mime_parsing_error ( "Failed to parse mime body(2)");
std::copy ( begin, end, std::back_inserter ( mp_body.body_epilog ));
#ifdef DUMP_MIME_DATA
std::cout << std::endl << ">>****Multipart Body*******" << std::endl;
std::cout << str ( boost::format ( "Body size %d, sub part count = %d, trailer size = %d %s" ) % mp_body.body_prolog.size () % mp_body.sub_parts.size () % mp_body.body_epilog.size () % ( mp_body.prolog_is_missing ? "(missing)" : "" )) << std::endl;
std::cout << std::endl << "****** Multipart Body Prolog *******" << std::endl;
std::copy ( mp_body.body_prolog.begin (), mp_body.body_prolog.end(), std::ostream_iterator<char> ( std::cout ));
std::cout << std::endl << "****** Multipart Body Epilog *******" << std::endl;
std::copy ( mp_body.body_epilog.begin (), mp_body.body_epilog.end(), std::ostream_iterator<char> ( std::cout ));
std::cout << std::endl << "<<****Multipart Body*******" << std::endl;
#endif
}
template<typename Container, typename Iterator>
static Container read_simplepart_body ( Iterator &begin, Iterator end ) {
tracer t ( __func__ );
Container retVal;
std::copy ( begin, end, std::back_inserter(retVal));
#ifdef DUMP_MIME_DATA
std::cout << std::endl << ">>****SinglePart Body*******" << std::endl;
std::cout << str ( boost::format ( "Body size %d" ) % retVal.size ()) << std::endl;
std::copy ( retVal.begin (), retVal.end(), std::ostream_iterator<char> ( std::cout ));
std::cout << std::endl << "<<****SinglePart Body*******" << std::endl;
#endif
return retVal;
}
// FIXME: Need to break the headers at 80 chars...
template<typename headerList>
void write_headers ( std::ostream &out, const headerList &headers ) {
if ( headers.size () > 0 ) {
for ( typename headerList::const_iterator iter = headers.begin (); iter != headers.end (); ++iter )
out << iter->first << ':' << iter->second << detail::k_crlf;
}
out << detail::k_crlf;
}
template <typename bodyContainer>
void write_body ( std::ostream &out, const bodyContainer &body ) {
std::copy ( body.begin (), body.end (), std::ostream_iterator<char> ( out ));
}
inline void write_boundary ( std::ostream &out, std::string boundary, bool isLast, bool leadingCR = true ) {
if ( leadingCR )
out << detail::k_crlf;
out << "--" << boundary;
if ( isLast )
out << "--";
out << detail::k_crlf;
}
template<typename Iterator, typename traits>
static boost::shared_ptr< basic_mime<traits> > parse_mime ( Iterator &begin, Iterator end, const char *default_content_type = "text/plain" );
}
template <class traits = detail::default_types>
class basic_mime {
public:
typedef enum { simple_part, multi_part, message_part } part_kind;
// Types for headers
typedef typename traits::string_type string_type;
typedef std::pair< std::string, string_type> headerEntry;
typedef std::list<headerEntry> headerList;
typedef typename headerList::iterator headerIter;
typedef typename headerList::const_iterator constHeaderIter;
// Types for the parts
typedef boost::shared_ptr<basic_mime> mimePtr;
typedef std::vector<mimePtr> partList;
typedef typename partList::iterator partIter;
typedef typename partList::const_iterator constPartIter;
// Type for the body
typedef typename traits::body_type bodyContainer;
typedef boost::shared_ptr<bodyContainer> mimeBody;
// -----------------------------------------------------------
// Constructors, destructor, assignment, and swap
// -----------------------------------------------------------
basic_mime ( const char *type, const char *subtype )
: m_body_prolog_is_missing ( false ), m_body ( new bodyContainer ), m_body_epilog ( new bodyContainer ) {
if ( NULL == type || NULL == subtype || 0 == std::strlen ( type ) || 0 == std::strlen ( subtype ))
throw std::runtime_error ( "Can't create a mime part w/o a type or subtype" );
// We start with just two headers, "Content-Type:" and "Mime-Version"
// Everything else is optional.
m_part_kind = part_kind_from_string_pair ( type, subtype );
std::string ctString = str ( boost::format ( "%s/%s" ) % type % subtype );
set_header_value ( detail::k_content_type_header, ctString );
set_header_value ( detail::k_mime_version_header, str ( boost::format ( "1.0 (%s %s)" ) % detail::k_package_name % detail::k_package_version ));
}
basic_mime ( const headerList &theHeaders, const string_type &default_content_type )
: m_body_prolog_is_missing ( false ), m_body ( new bodyContainer ), m_body_epilog ( new bodyContainer ),
m_default_content_type ( default_content_type ) {
string_type ct = m_default_content_type;
constHeaderIter found = std::find_if ( theHeaders.begin (), theHeaders.end (),
detail::find_mime_header<string_type> ( detail::k_content_type_header ));
if ( found != theHeaders.end ())
ct = found->second;
detail::mime_content_type mct = detail::parse_content_type ( ct );
m_part_kind = part_kind_from_string_pair ( mct.type, mct.sub_type );
m_headers = theHeaders;
}
basic_mime ( const basic_mime &rhs )
: m_part_kind ( rhs.m_part_kind ), m_headers ( rhs.m_headers ), m_body_prolog_is_missing ( rhs.m_body_prolog_is_missing ),
m_body ( new bodyContainer ( *rhs.m_body )), m_body_epilog ( new bodyContainer ( *rhs.m_body_epilog )),
/* m_subparts ( rhs.m_subparts ), */ m_default_content_type ( rhs.m_default_content_type )
{
// Copy the parts -- not just the shared pointers
for ( typename partList::const_iterator iter = rhs.subpart_begin (); iter != rhs.subpart_end (); ++iter )
m_subparts.push_back ( mimePtr ( new basic_mime ( **iter )));
}
// Simple, copy constructor-based assignment
// If this is not efficient enough, then I can optimize it later
basic_mime & operator = ( const basic_mime &rhs ) {
basic_mime temp ( rhs );
this->swap ( temp );
return *this;
}
void swap ( basic_mime &rhs ) throw () {
std::swap ( m_part_kind, rhs.m_part_kind );
std::swap ( m_headers, rhs.m_headers );
std::swap ( m_body_prolog_is_missing, rhs.m_body_prolog_is_missing );
std::swap ( m_body, rhs.m_body );
std::swap ( m_body_epilog, rhs.m_body_epilog );
std::swap ( m_subparts, rhs.m_subparts );
std::swap ( m_default_content_type, rhs.m_default_content_type );
}
~basic_mime () {}
// What kind of part is this (simple, multi, message)
part_kind get_part_kind () const { return m_part_kind; }
// Sub-part information
// FIXME: Need some error checking here
// No sub-parts for simple parts, for example.
size_t part_count () const { return m_subparts.size (); }
boost::shared_ptr <basic_mime> operator [] ( std::size_t idx ) const {
check_subpart_index ( idx );
return m_subparts [ idx ];
}
void append_part ( boost::shared_ptr<basic_mime> newPart ) {
check_subpart_append ();
m_subparts.push_back ( newPart );
}
partIter subpart_begin () { return m_subparts.begin (); }
partIter subpart_end () { return m_subparts.end (); }
constPartIter subpart_begin () const { return m_subparts.begin (); }
constPartIter subpart_end () const { return m_subparts.end (); }
// Reading the raw headers
headerIter header_begin () { return m_headers.begin (); }
headerIter header_end () { return m_headers.end (); }
constHeaderIter header_begin () const { return m_headers.begin (); }
constHeaderIter header_end () const { return m_headers.end (); }
// -----------------------------------------------------------
// Header manipulation
// -----------------------------------------------------------
// The 'tag' part of the header is still a std::string
bool header_exists ( const char *key ) const {
return header_end () != find_header ( key );
}
string_type header_value ( const char *key ) const {
constHeaderIter found = find_header ( key );
if ( found == header_end ())
throw std::runtime_error ( "'header_value' not found" );
return found->second;
}
void set_header_value ( const char *key, const string_type &value, bool replace = false ) {
if ( !replace )
m_headers.push_back ( std::make_pair ( std::string ( key ), value ));
else {
headerIter found = find_header ( key );
if ( found == m_headers.end ())
throw std::runtime_error ( "'header_value' not found - can't replace" );
found->second = value;
}
}
string_type get_content_type_header () const {
constHeaderIter found = find_header ( detail::k_content_type_header );
return found != header_end () ? found->second : m_default_content_type;
}
string_type get_content_type () const {
detail::mime_content_type mct = detail::parse_content_type ( get_content_type_header ());
return string_type ( mct.type ) + '/' + mct.sub_type;
}
// Special purpose helper routine
void append_phrase_to_content_type ( const char *key, const string_type &value ) {
headerIter found = find_header ( detail::k_content_type_header );
// Create a Content-Type header if there isn't one
if ( m_headers.end () == found ) {
m_headers.push_back ( std::make_pair ( std::string ( detail::k_content_type_header ), m_default_content_type ));
found = find_header ( detail::k_content_type_header );
}
detail::mime_content_type mct = detail::parse_content_type ( found->second );
detail::phrase_container_t::const_iterator p_found =
std::find_if ( mct.phrases.begin (), mct.phrases.end (),
detail::find_mime_header<std::string> ( key ));
if ( p_found != mct.phrases.end ())
throw std::runtime_error ( "phrase already exists" );
found->second += str ( boost::format ( "; %s=\"%s\"" ) % key % value );
}
// Body get/set methods
mimeBody body () const { return m_body; }
mimeBody body_prolog () const { return m_body; }
mimeBody body_epilog () const { return m_body_epilog; }
std::size_t body_size () const { return m_body->size (); }
template <typename Iterator>
void set_body ( Iterator begin, Iterator end ) {
bodyContainer temp;
std::copy ( begin, end, std::back_inserter ( temp ));
m_body->swap ( temp );
}
void set_body ( const char *contents, size_t sz ) { set_body ( contents, contents + sz ); }
void set_body ( std::istream &in ) { set_body ( std::istream_iterator<char> ( in ), std::istream_iterator<char> ()); }
void set_body ( const bodyContainer &new_body ) { *m_body = new_body; }
void set_multipart_prolog_is_missing ( bool isMissing ) { m_body_prolog_is_missing = isMissing; }
void set_body_prolog ( const bodyContainer &new_body_prolog ) { *m_body = new_body_prolog; }
void set_body_epilog ( const bodyContainer &new_body_epilog ) { *m_body_epilog = new_body_epilog; }
// -----------------------------------------------------------
// Output
// -----------------------------------------------------------
void stream_out ( std::ostream &out ) { // called by operator <<
if ( m_part_kind == simple_part ) {
detail::write_headers ( out, m_headers );
detail::write_body ( out, *m_body );
}
else if ( m_part_kind == message_part ) {
if ( m_subparts.size () != 1 )
throw std::runtime_error ( "message part w/wrong number of sub-parts - should be 1" );
detail::write_headers ( out, m_headers );
m_subparts [ 0 ]->stream_out ( out );
}
else { // multi-part
// Find or invent a boundary string
std::string boundary;
try { boundary = detail::get_boundary ( get_content_type_header ()); }
catch ( std::runtime_error & ) {
// FIXME: Make boundary strings (more?) unique
boundary = str ( boost::format ( "------=_NextPart-%s.%08ld" ) % detail::k_package_name % std::clock ());
append_phrase_to_content_type ( "boundary", boundary );
}
// If the body prolog is missing, we don't want a CRLF on the front of the first sub-part.
// Note that there's a (subtle) difference between an zero length body and a missing one.
// See the comments in the parser code for more information.
detail::write_headers ( out, m_headers );
bool writeCR = body_prolog ()->size () > 0 || !m_body_prolog_is_missing;
detail::write_body ( out, *body_prolog ());
for ( typename partList::const_iterator iter = m_subparts.begin (); iter != m_subparts.end (); ++iter ) {
detail::write_boundary ( out, boundary, false, writeCR );
(*iter)->stream_out ( out );
writeCR = true;
}
detail::write_boundary ( out, boundary, true );
detail::write_body ( out, *body_epilog ());
}
// out << detail::k_crlf;
}
// Build a simple mime part
template <typename Iterator>
static basic_mime make_simple_part ( const char *type, const char *subtype, Iterator begin, Iterator end ) {
basic_mime retval ( type, subtype );
retval.set_body ( begin, end );
return retval;
}
// Build a mime part from a pair of iterators
template <typename Iterator>
static boost::shared_ptr< basic_mime<traits> > parse_mime ( Iterator &begin, Iterator end ) {
return detail::parse_mime<Iterator, traits> ( begin, end );
}
// Build a mime part from a stream
static boost::shared_ptr < basic_mime > parse_mime ( std::istream &in ) {
boost::spirit::istream_iterator first (in);
boost::spirit::istream_iterator last;
return parse_mime ( first, last );
}
private:
basic_mime (); // Can't create a part w/o a type
headerIter find_header ( const char *key ) {
return std::find_if ( header_begin (), header_end (), detail::find_mime_header<string_type> ( key ));
}
constHeaderIter find_header ( const char *key ) const {
return std::find_if ( header_begin (), header_end (), detail::find_mime_header<string_type> ( key ));
}
static part_kind part_kind_from_string_pair ( const std::string &type, const std::string &sub_type ) {
if ( boost::iequals ( type, "multipart" ))
return multi_part;
part_kind retVal = simple_part;
// I expect that this will get more complicated as time goes on....
//
// message/delivery-status is a simple type.
// RFC 3464 defines message/delivery-status <http://www.faqs.org/rfcs/rfc3464.html>
// The body of a message/delivery-status consists of one or more
// "fields" formatted according to the ABNF of RFC 822 header "fields"
// (see [RFC822]).
if ( boost::iequals ( type, "message" ))
if ( !boost::iequals ( sub_type, "delivery-status" ))
retVal = message_part;
return retVal;
}
void check_subpart_index ( size_t idx ) const {
if ( get_part_kind () == simple_part )
throw std::runtime_error ( "Simple Mime parts don't have sub-parts" );
else if ( get_part_kind () == multi_part ) {
if ( idx >= m_subparts.size ())
throw std::runtime_error (
str ( boost::format ( "Trying to access part %d (of %d) sub-part to a multipart/xxx mime part" ) % idx % m_subparts.size ()));
}
else { // message-part
if ( get_part_kind () == message_part )
if ( m_subparts.size () > 1 )
throw std::runtime_error ( "How did a message/xxx mime parts get more than one sub-part?" );
if ( idx >= m_subparts.size ())
throw std::runtime_error (
str ( boost::format ( "Trying to access part %d (of %d) sub-part to a message/xxx mime part" ) % idx % m_subparts.size ()));
}
}
void check_subpart_append () const {
if ( get_part_kind () == simple_part )
throw std::runtime_error ( "Simple Mime parts don't have sub-parts" );
else if ( get_part_kind () == message_part ) {
if ( m_subparts.size () > 0 )
throw std::runtime_error ( "Can't add a second sub-part to a message/xxx mime part" );
}
// else { /* Multi-part */ } // We can always add to a multi-part
}
part_kind m_part_kind;
headerList m_headers;
bool m_body_prolog_is_missing; // only for multiparts
mimeBody m_body;
mimeBody m_body_epilog; // only for multiparts
partList m_subparts; // only for multiparts or message
string_type m_default_content_type;
};
namespace detail {
template<typename Iterator, typename traits>
static boost::shared_ptr< basic_mime<traits> > parse_mime ( Iterator &begin, Iterator end, const char *default_content_type ) {
tracer t ( __func__ );
typedef typename boost::mime::basic_mime<traits> mime_part;
shared_ptr < mime_part > retVal (
new mime_part ( detail::read_headers<typename mime_part::headerList> ( begin, end ), default_content_type ));
std::string content_type = retVal->get_content_type ();
#ifdef DUMP_MIME_DATA
std::cout << "Content-Type: " << content_type << std::endl;
std::cout << str ( boost::format ( "retVal->get_part_kind () = %d" ) % ((int) retVal->get_part_kind ())) << std::endl;
#endif
if ( retVal->get_part_kind () == mime_part::simple_part )
retVal->set_body ( detail::read_simplepart_body<typename mime_part::bodyContainer, Iterator> ( begin, end ));
else if ( retVal->get_part_kind () == mime_part::message_part ) {
// If we've got a message/xxxx, then there is no body, and we have a single
// embedded mime_part (which, of course, could be a multipart)
retVal->append_part ( parse_mime<Iterator, traits> ( begin, end ));
}
else /* multi_part */ {
// Find or invent a boundary string
std::string part_separator = detail::get_boundary ( retVal->get_content_type_header ());
const char *cont_type = boost::iequals ( content_type, "multipart/digest" ) ? "message/rfc822" : "text/plain";
detail::multipart_body_type<typename traits::body_type> body_and_subParts;
detail::read_multipart_body ( begin, end, body_and_subParts, part_separator );
retVal->set_body_prolog ( body_and_subParts.body_prolog );
retVal->set_multipart_prolog_is_missing ( body_and_subParts.prolog_is_missing );
for ( typename sub_parts_t::const_iterator iter = body_and_subParts.sub_parts.begin ();
iter != body_and_subParts.sub_parts.end (); ++iter ) {
typedef typename sub_part_t::const_iterator iter_type;
iter_type b = iter->begin ();
iter_type e = iter->end ();
retVal->append_part ( parse_mime<iter_type, traits> ( b, e, cont_type ));
}
retVal->set_body_epilog ( body_and_subParts.body_epilog );
}
return retVal;
}
}
// -----------------------------------------------------------
//
// Streaming
//
// -----------------------------------------------------------
template<typename traits>
inline std::ostream & operator << ( std::ostream &stream, basic_mime<traits> &part ) {
part.stream_out ( stream );
return stream;
}
template<typename traits>
inline std::ostream & operator << ( std::ostream &stream, boost::shared_ptr <basic_mime<traits> > part ) {
return stream << *part;
}
}}
BOOST_FUSION_ADAPT_STRUCT(
boost::mime::detail::mime_content_type,
(std::string, type)
(std::string, sub_type)
(boost::mime::detail::phrase_container_t, phrases)
)
#endif // _BOOST_MIME_HPP