#include "bodyparser.hh"
|
|
#include "pEpMIME_internal.hh"
|
|
#include "rules.hh"
|
|
#include "base64.hxx"
|
|
#include "quoted_printable.hxx"
|
|
#include <pEp/pEp_string.h>
|
|
|
|
#include <boost/spirit/include/qi.hpp>
|
|
#include <boost/spirit/include/phoenix.hpp>
|
|
#include <boost/fusion/include/adapt_struct.hpp>
|
|
|
|
namespace qi = boost::spirit::qi;
|
|
|
|
struct ContentType
|
|
{
|
|
std::string type;
|
|
std::string subtype;
|
|
std::vector<pEpMIME::NameValue> params;
|
|
void tolower(); // only for ASCII chars, but that's sufficient here.
|
|
};
|
|
|
|
BOOST_FUSION_ADAPT_STRUCT(
|
|
ContentType,
|
|
(std::string, type)
|
|
(std::string, subtype)
|
|
(std::vector<pEpMIME::NameValue>, params)
|
|
)
|
|
|
|
// that boost::fusion magic seems to work only in the actual TU
|
|
// so it has to be defined here, instead of at the end of pEpMIME_internal.cc *sigh*
|
|
BOOST_FUSION_ADAPT_STRUCT(
|
|
pEpMIME::NameValue,
|
|
(std::string, name)
|
|
(std::string, value)
|
|
)
|
|
|
|
|
|
void ascii_tolower(std::string& s)
|
|
{
|
|
for(char& c : s)
|
|
{
|
|
if(c>='A' && c<='Z')
|
|
{
|
|
c += 32;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContentType::tolower()
|
|
{
|
|
ascii_tolower(type);
|
|
ascii_tolower(subtype);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& o, const ContentType& ct)
|
|
{
|
|
return o << "CT:{" << ct.type << "/" << ct.subtype << ". params=" << ct.params << " } ";
|
|
}
|
|
|
|
|
|
namespace pEpMIME
|
|
{
|
|
|
|
char* base64_decode(const BodyLines& bl, size_t& output_size)
|
|
{
|
|
size_t out_size = 0;
|
|
for(const auto& line : bl)
|
|
{
|
|
out_size += (line.size()+3)/4 * 3;
|
|
}
|
|
|
|
char* out_string = new_string(nullptr, out_size);
|
|
char* out_begin = out_string;
|
|
char* out_end = out_string + out_size;
|
|
|
|
base64::decode_iter( BodyIterator{bl}, BodyIterator{}, out_begin, out_end);
|
|
output_size = out_begin - out_string;
|
|
return out_string;
|
|
}
|
|
|
|
char* qp_decode(const BodyLines& bl, size_t& output_size)
|
|
{
|
|
size_t out_size = 0;
|
|
for(const auto& line : bl)
|
|
{
|
|
out_size += line.size();
|
|
}
|
|
|
|
char* out_string = new_string(nullptr, out_size);
|
|
char* out_begin = out_string;
|
|
char* out_end = out_string + out_size;
|
|
|
|
qp::decode_iter( BodyIterator{bl}, BodyIterator{}, out_begin, out_end);
|
|
output_size = out_begin - out_string;
|
|
return out_string;
|
|
}
|
|
|
|
|
|
|
|
// Tokens from RFC 2045
|
|
Rule token = +( vchar - qi::char_("]()<>@,;:\\\"/?=["));
|
|
|
|
TRule<NameValue> parameter = token >> '=' >> (token | quoted_string.alias());
|
|
TRule<ContentType> content_type = token >> '/' >> token >> *( qi::omit[*cfws] >> ';' >> qi::omit[*cfws] >> parameter);
|
|
|
|
|
|
// parses the header and fill the parts in msg
|
|
void parse_body(message* msg, const HeaderSection& headers, const std::deque<sv>& body)
|
|
{
|
|
if( header_value(headers, "MIME-Version") == "1.0" ) // TODO: According to RFC 2048 there can be comments in the header field value. -.-
|
|
{
|
|
// TODO: for whatever reason "string_view cts" does not work with qi::parse(). WTF!
|
|
const std::string cts = header_value(headers, "Content-Type").to_string();
|
|
ContentType ct;
|
|
auto begin = cts.cbegin();
|
|
|
|
const bool okay = qi::parse(begin, cts.cend(), content_type, ct);
|
|
if(!okay)
|
|
{
|
|
throw std::runtime_error( "Cannot parse \"" + std::string{cts} + "\" as ContentType");
|
|
}
|
|
ct.tolower();
|
|
std::cerr << ct << std::endl;
|
|
|
|
}else{ // Non-MIME mail
|
|
std::cerr << "<<< NO_MIME_MAIL >>>\n";
|
|
}
|
|
}
|
|
|
|
|
|
} // end of namespace pEpMIME
|