Browse Source

separate struct Message into separate TU to solve cyclic dependencies

afl-fuzzing
Roker 3 years ago
parent
commit
02694c5205
7 changed files with 230 additions and 71 deletions
  1. +2
    -1
      src/Makefile
  2. +87
    -3
      src/bodyparser.cc
  3. +109
    -0
      src/message.cc
  4. +22
    -0
      src/message.hh
  5. +1
    -0
      src/mime_headers.cc
  6. +5
    -1
      src/mime_headers.hh
  7. +4
    -66
      src/pEpMIME.cc

+ 2
- 1
src/Makefile View File

@ -17,7 +17,8 @@ all: libpEpMIME.a unittests fuzz
libpEpMIME.a: pEpMIME.o pEpMIME_internal.o rules.o bodyparser.o \
headerparser.o parse_timestamp.o parse_address.o nulllogger.o \
base64.o nfc.o mime_headers.o nfc_sets.o to_utf8.o quoted_printable.o
base64.o nfc.o mime_headers.o nfc_sets.o to_utf8.o quoted_printable.o \
message.o
${AR} rcs $@ $^
unittests: unittest_mime.o unittest_nfc.o unittest_timestamp.o \


+ 87
- 3
src/bodyparser.cc View File

@ -1,6 +1,7 @@
#include "bodyparser.hh"
#include "pEpMIME_internal.hh"
#include "mime_headers.hh"
#include "message.hh"
#include "rules.hh"
#include "base64.hxx"
#include "quoted_printable.hxx"
@ -17,14 +18,53 @@
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
using qi::_val;
using qi::_1;
namespace pEpMIME
{
std::vector<Message> parse_multipart(const BodyLines& body, const sv& boundary)
{
bool is_last = false;
qi::rule<const char*, qi::unused_type()> is_delimiter_parser = qi::lit("--") >> qi::lit(boundary.data())
>> -qi::lit("--")[ px::ref(is_last) = true] >> qi::omit[*qi::char_(" \t")];
std::vector<Message> vm;
bool after_preamble = false;
BodyLines part;
LOG << "Parse_Multipart: " << body.size() << " body lines. bounardy=“" << boundary << "”. \n";
for(const auto& line : body)
{
is_last = false;
auto begin = line.cbegin();
const bool is_delimiter = qi::parse( begin, line.cend(), is_delimiter_parser );
if(is_delimiter)
{
LOG << "\t Line “" << line << "” is " << (is_last ? "LAST ":"") << "delimiter!\n";
if(after_preamble)
{
vm.emplace_back( part );
part.clear();
}else{
after_preamble = true;
}
if(is_last == true)
{
break;
}
}else{
if(after_preamble)
{
part.emplace_back(line);
}
}
}
return vm;
}
char* create_string(const BodyLines& body, const sv& charset, MimeHeaders::Decoder decoder)
{
size_t decoded_size = 0;
@ -60,6 +100,21 @@ void add_attachment(message* msg, const BodyLines& body, const MimeHeaders& mh)
}
struct has_mimetype
{
has_mimetype(const char* _mime_type)
: mt(_mime_type)
{}
bool operator()(const Message& m) const
{
MimeHeaders mh{m.headers};
return mh.mime_type() == mt;
}
const char* mt;
};
// parses the header and fill the parts in msg
void parse_body(message* msg, const HeaderSection& headers, const BodyLines& body)
{
@ -85,6 +140,35 @@ void parse_body(message* msg, const HeaderSection& headers, const BodyLines& bod
// add it as attachment
add_attachment(msg, body, mh);
}
}else if(mh.type == "multipart")
{
const sv boundary = header_value(mh.tparams, "boundary");
std::vector<Message> vm = parse_multipart( body, boundary );
LOG << "MULTIPART: " << vm.size() << " parts. Boundary = “" << boundary << "” :\n";
for(const auto& m : vm)
{
LOG << "####\n" << m << "\n";
}
if(mh.subtype == "alternative")
{
auto first_text = std::find_if(vm.begin(), vm.end(), has_mimetype("text/plain") );
auto first_html = std::find_if(vm.begin(), vm.end(), has_mimetype("text/html") );
}else if(mh.subtype == "mixed")
{
for(const auto& m : vm)
{
MimeHeaders mh{m.headers};
add_attachment(msg, m.body, mh);
}
}
}else if(mh.type == "message")
{
}else{
// all other MIME types
add_attachment(msg, body, mh);
}
}else{ // Non-MIME mail


+ 109
- 0
src/message.cc View File

@ -0,0 +1,109 @@
#include "message.hh"
#include <algorithm>
#include <boost/fusion/include/adapt_struct.hpp>
namespace pEpMIME
{
// -1 = continuation line
// -2 = illegal line
// >0 = position of COLON, if starts with printable ASCII. :-)
int header_line_type(sv line)
{
if(line.empty())
return -2;
if(line[0] == ':')
return -2;
if(line[0] == ' ' || line[0] == '\t')
return -1;
if(line[0] < 33 || line[0] > 126)
return -2;
// okay, only printable ASCII characters without ':' are left.
return line.find(':');
}
Message::Message(const BodyLines& lines)
: headers{}
, mh{headers}
{
int headersize = -1;
for(unsigned u=0; u<lines.size(); ++u)
{
if(lines[u].empty())
{
headersize = u;
break;
}
}
if(headersize<0)
{
headersize = lines.size();
}
// collect and unfold the header lines
NameValue nv;
for(int i=0; i<headersize; ++i)
{
sv line = lines.at(i);
const int lineType = header_line_type(line);
LOG << "Line #" << i << ": \"" << line << "\". type=" << lineType << ". \n";
switch(lineType)
{
case -2: // line is bogus. But be robust: just skip that line and hope for the best...
//throw std::runtime_error("Cannot handle line \"" + std::string(line) + "\""); return nullptr; // TODO: C'mon, don't give up, yet! :-)
break;
case -1: nv.value += std::string(line);
break;
default:
{
if(!nv.name.empty())
{
headers.push_back(nv);
}
nv.name = std::string( line.substr(0, lineType) );
ascii_tolower(nv.name);
const int firstBody = lineType + 1; // 1st index _after_ the ':'
if(firstBody < line.size()) // there is something after the ':'
{
const char firstBodyChar = line.at(firstBody); // if SPACE or TAB char: chop this char, too. but only the 1st!
nv.value = std::string( line.substr( firstBody + ( (firstBodyChar==' '|| firstBodyChar=='\t') ? 1 : 0) ));
}
}
}
}
if(!nv.name.empty())
{
headers.push_back(nv);
}
LOG << "Parsing result: " << headers.size() << " parsed header lines (from " << headersize << " original lines):\n";
for(const auto& h : headers)
{
LOG << "" << h.name << "” : “" << h.value << "\n";
}
mh = MimeHeaders{headers};
// strip the header lines:
body.insert(body.end(), lines.begin() + headersize, lines.end() );
}
std::ostream& operator<<(std::ostream& o, const Message& m)
{
o << "Message: " << m.headers.size() << " header lines:\n";
for(const auto& h : m.headers)
{
o << "\t" << h << "\n";
}
o << "followed by " << m.body.size() << " body lines.\n";
return o;
}
} // end of namespace pEpMIME

+ 22
- 0
src/message.hh View File

@ -0,0 +1,22 @@
#ifndef PEP_MIME_MESSAGE_HH
#define PEP_MIME_MESSAGE_HH
#include "pEpMIME_internal.hh"
#include "mime_headers.hh"
namespace pEpMIME
{
struct Message
{
explicit Message(const BodyLines& lines);
HeaderSection headers;
MimeHeaders mh;
BodyLines body;
};
std::ostream& operator<<(std::ostream&, const Message& m);
} // end of namespace pEpMIME
#endif // PEP_MIME_MESSAGE_HH

+ 1
- 0
src/mime_headers.cc View File

@ -139,6 +139,7 @@ void unwrap(std::vector<NameValue>& params)
std::string::const_iterator begin = p.name.cbegin();
if(qi::parse(begin, p.name.cend(), param_name, pn))
{
ascii_tolower(pn.name);
const std::string& value = pn.ext_value ? convert(charset, p.value ) : p.value;
switch(pn.count)
{


+ 5
- 1
src/mime_headers.hh View File

@ -1,3 +1,5 @@
// Defines data structures for Content-Type, Content-Disposition and Content-Transfer-Encoding headers
#ifndef PEP_MIME_MIME_HEADERS_HH
#define PEP_MIME_MIME_HEADERS_HH
@ -20,6 +22,8 @@ namespace pEpMIME
tolower();
if(type.empty()) { type = "text"; subtype="plain"; }
}
std::string mime_type() const { return type + "/" + subtype; }
};
struct ContentDisposition
@ -34,7 +38,7 @@ namespace pEpMIME
{
typedef char* (*Decoder)(const BodyLines&, size_t&);
MimeHeaders(const HeaderSection& headers);
explicit MimeHeaders(const HeaderSection& headers);
std::string transfer_encoding;
Decoder decoder;


+ 4
- 66
src/pEpMIME.cc View File

@ -1,5 +1,6 @@
#include "pEpMIME.hh"
#include "pEpMIME_internal.hh"
#include "message.hh"
#include "headerparser.hh"
#include "bodyparser.hh"
#include <initializer_list>
@ -12,23 +13,6 @@
namespace pEpMIME
{
// -1 = continuation line
// -2 = illegal line
// >0 = position of COLON, if starts with printable ASCII. :-)
int header_line_type(sv line)
{
if(line.empty())
return -2;
if(line[0] == ':')
return -2;
if(line[0] == ' ' || line[0] == '\t')
return -1;
if(line[0] < 33 || line[0] > 126)
return -2;
// okay, only printable ASCII characters without ':' are left.
return line.find(':');
}
namespace
{
@ -123,57 +107,11 @@ message* parse_message2(const char* begin, const char* const end)
}
LOG << "Parsing result: " << headersize << " raw header lines, " << (lines.size()-headersize) << " body lines:\n" << lines;
HeaderSection headers;
// collect and unfold the header lines
NameValue nv;
for(int i=0; i<headersize; ++i)
{
sv line = lines.at(i);
const int lineType = header_line_type(line);
LOG << "Line #" << i << ": \"" << line << "\". type=" << lineType << ". \n";
switch(lineType)
{
case -2: // line is bogus. But be robust: just skip that line and hope for the best...
//throw std::runtime_error("Cannot handle line \"" + std::string(line) + "\""); return nullptr; // TODO: C'mon, don't give up, yet! :-)
break;
case -1: nv.value += std::string(line);
break;
default:
{
if(!nv.name.empty())
{
headers.push_back(nv);
}
nv.name = std::string( line.substr(0, lineType) );
ascii_tolower(nv.name);
const int firstBody = lineType + 1; // 1st index _after_ the ':'
if(firstBody < line.size()) // there is something after the ':'
{
const char firstBodyChar = line.at(firstBody); // if SPACE or TAB char: chop this char, too. but only the 1st!
nv.value = std::string( line.substr( firstBody + ( (firstBodyChar==' '|| firstBodyChar=='\t') ? 1 : 0) ));
}
}
}
}
if(!nv.name.empty())
{
headers.push_back(nv);
}
Message m{lines};
LOG << "Parsing result: " << headers.size() << " parsed header lines (from " << headersize << " original lines):\n";
for(const auto& h : headers)
{
LOG << "" << h.name << "” : “" << h.value << "\n";
}
// strip the header lines:
lines.erase(lines.begin(), lines.begin() + headersize );
message* msg = new_message(PEP_dir_incoming);
parse_header(msg, headers);
parse_body(msg, headers, lines);
parse_header(msg, m.headers);
parse_body(msg, m.headers, m.body);
return msg;
}


Loading…
Cancel
Save