p≡p MIME library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
3.3 KiB

  1. #include "bodyparser.hh"
  2. #include "pEpMIME_internal.hh"
  3. #include "rules.hh"
  4. #include "base64.hxx"
  5. #include "quoted_printable.hxx"
  6. #include <pEp/pEp_string.h>
  7. #include <boost/spirit/include/qi.hpp>
  8. #include <boost/spirit/include/phoenix.hpp>
  9. #include <boost/fusion/include/adapt_struct.hpp>
  10. namespace qi = boost::spirit::qi;
  11. struct ContentType
  12. {
  13. std::string type;
  14. std::string subtype;
  15. std::vector<pEpMIME::NameValue> params;
  16. void tolower(); // only for ASCII chars, but that's sufficient here.
  17. };
  18. BOOST_FUSION_ADAPT_STRUCT(
  19. ContentType,
  20. (std::string, type)
  21. (std::string, subtype)
  22. (std::vector<pEpMIME::NameValue>, params)
  23. )
  24. // that boost::fusion magic seems to work only in the actual TU
  25. // so it has to be defined here, instead of at the end of pEpMIME_internal.cc *sigh*
  26. BOOST_FUSION_ADAPT_STRUCT(
  27. pEpMIME::NameValue,
  28. (std::string, name)
  29. (std::string, value)
  30. )
  31. void ascii_tolower(std::string& s)
  32. {
  33. for(char& c : s)
  34. {
  35. if(c>='A' && c<='Z')
  36. {
  37. c += 32;
  38. }
  39. }
  40. }
  41. void ContentType::tolower()
  42. {
  43. ascii_tolower(type);
  44. ascii_tolower(subtype);
  45. }
  46. std::ostream& operator<<(std::ostream& o, const ContentType& ct)
  47. {
  48. return o << "CT:{" << ct.type << "/" << ct.subtype << ". params=" << ct.params << " } ";
  49. }
  50. namespace pEpMIME
  51. {
  52. char* base64_decode(const BodyLines& bl, size_t& output_size)
  53. {
  54. size_t out_size = 0;
  55. for(const auto& line : bl)
  56. {
  57. out_size += (line.size()+3)/4 * 3;
  58. }
  59. char* out_string = new_string(nullptr, out_size);
  60. char* out_begin = out_string;
  61. char* out_end = out_string + out_size;
  62. base64::decode_iter( BodyIterator{bl}, BodyIterator{}, out_begin, out_end);
  63. output_size = out_begin - out_string;
  64. return out_string;
  65. }
  66. char* qp_decode(const BodyLines& bl, size_t& output_size)
  67. {
  68. size_t out_size = 0;
  69. for(const auto& line : bl)
  70. {
  71. out_size += line.size();
  72. }
  73. char* out_string = new_string(nullptr, out_size);
  74. char* out_begin = out_string;
  75. char* out_end = out_string + out_size;
  76. qp::decode_iter( BodyIterator{bl}, BodyIterator{}, out_begin, out_end);
  77. output_size = out_begin - out_string;
  78. return out_string;
  79. }
  80. // Tokens from RFC 2045
  81. Rule token = +( vchar - qi::char_("]()<>@,;:\\\"/?=["));
  82. TRule<NameValue> parameter = token >> '=' >> (token | quoted_string.alias());
  83. TRule<ContentType> content_type = token >> '/' >> token >> *( qi::omit[*cfws] >> ';' >> qi::omit[*cfws] >> parameter);
  84. // parses the header and fill the parts in msg
  85. void parse_body(message* msg, const HeaderSection& headers, const std::deque<sv>& body)
  86. {
  87. if( header_value(headers, "MIME-Version") == "1.0" ) // TODO: According to RFC 2048 there can be comments in the header field value. -.-
  88. {
  89. // TODO: for whatever reason "string_view cts" does not work with qi::parse(). WTF!
  90. const std::string cts = header_value(headers, "Content-Type").to_string();
  91. ContentType ct;
  92. auto begin = cts.cbegin();
  93. const bool okay = qi::parse(begin, cts.cend(), content_type, ct);
  94. if(!okay)
  95. {
  96. throw std::runtime_error( "Cannot parse \"" + std::string{cts} + "\" as ContentType");
  97. }
  98. ct.tolower();
  99. std::cerr << ct << std::endl;
  100. }else{ // Non-MIME mail
  101. std::cerr << "<<< NO_MIME_MAIL >>>\n";
  102. }
  103. }
  104. } // end of namespace pEpMIME