Browse Source

merge MIME-11 into default branch.

MIME-14
Roker 2 years ago
parent
commit
5639f12b2b
11 changed files with 125 additions and 31 deletions
  1. +20
    -6
      src/bodygenerator.cc
  2. +3
    -1
      src/bodygenerator.hh
  3. +19
    -2
      src/bodyparser.cc
  4. +21
    -10
      src/header_generator.cc
  5. +2
    -1
      src/headerparser.cc
  6. +2
    -0
      src/headerparser.hh
  7. +7
    -5
      src/pEpMIME.cc
  8. +3
    -0
      src/pEpMIME_internal.cc
  9. +4
    -0
      src/pEpMIME_internal.hh
  10. +36
    -6
      src/unittest_mime.cc
  11. +8
    -0
      src/unittest_stringcase.cc

+ 20
- 6
src/bodygenerator.cc View File

@ -44,6 +44,18 @@ std::string create_delimiter(const std::vector<Attachment>& a)
}
std::string longmsg_mimetype(const message* msg)
{
const auto longmsg_format = stringpair_list_find(msg->opt_fields, Pseudo_Header_Format.data());
const auto longmsg_delsp = stringpair_list_find(msg->opt_fields, Pseudo_Header_Delsp.data());
return "text/plain"s
+ (longmsg_format ? "; format="s + longmsg_format->value->value : std::string())
+ (longmsg_delsp ? "; delsp="s + longmsg_delsp->value->value : std::string())
;
}
void generate_body(std::string& smsg, sv mime_type, sv body, bool transport_encode)
{
smsg += "Content-Type: "s + mime_type + "; charset=UTF-8;\r\n"s;
@ -60,7 +72,7 @@ void generate_body(std::string& smsg, sv mime_type, sv body, bool transport_enco
}
void generate_ma_body(std::string& smsg, sv plain, sv html, bool transport_encode)
void generate_ma_body(std::string& smsg, sv plain_mimetype, sv plain, sv html, bool transport_encode)
{
const bool encode_plain = transport_encode && need_transport_encoding(plain);
const bool encode_html = transport_encode && need_transport_encoding(html);
@ -79,7 +91,7 @@ void generate_ma_body(std::string& smsg, sv plain, sv html, bool transport_encod
"\r\n"; // end of header section
smsg += "--" + delimiter + "\r\n";
generate_body(smsg, "text/plain", plain, transport_encode);
generate_body(smsg, plain_mimetype, plain, transport_encode);
smsg += "--" + delimiter + "\r\n";
generate_body(smsg, "text/html", html, transport_encode);
smsg += "--" + delimiter + "--\r\n";
@ -110,9 +122,11 @@ void generate_mm_body(std::string& smsg, sv mime_type, sv body, const std::vecto
// see: https://dev.pep.foundation/libpEpMIME
void generate_complex_body(std::string& smsg, unsigned det, const message* msg, const std::vector<Attachment>& a, bool transport_encode)
{
const std::string longmsg_mimetype = pEpMIME::longmsg_mimetype(msg);
std::vector<Attachment> a2{a};
if(msg->longmsg)
a2.emplace_back(msg->longmsg, "text/plain");
a2.emplace_back(msg->longmsg, longmsg_mimetype);
if(msg->longmsg_formatted)
a2.emplace_back(msg->longmsg_formatted, "text/html");
@ -134,7 +148,7 @@ void generate_complex_body(std::string& smsg, unsigned det, const message* msg,
smsg += "Content-Type: multipart/alternative; boundary=\"" + delimiter_A + "\";\r\n"
"\r\n"
"--" + delimiter_A + "\r\n";
generate_body(smsg, "text/plain", msg->longmsg, transport_encode);
generate_body(smsg, longmsg_mimetype, msg->longmsg, transport_encode);
smsg += "--" + delimiter_A + "\r\n";
const std::string delimiter_R = delimiter + "R=";
smsg += "Content-Type: multipart/related; boundary=\"" + delimiter_R + "\";\r\n"
@ -155,7 +169,7 @@ void generate_complex_body(std::string& smsg, unsigned det, const message* msg,
smsg += "Content-Type: multipart/alternative; boundary=\"" + delimiter_A + "\";\r\n"
"\r\n"
"--" + delimiter_A + "\r\n";
generate_body(smsg, "text/plain", msg->longmsg, transport_encode);
generate_body(smsg, longmsg_mimetype, msg->longmsg, transport_encode);
smsg += "--" + delimiter_A + "\r\n";
generate_body(smsg, "text/html", msg->longmsg_formatted, transport_encode);
smsg += "--" + delimiter_A + "--\r\n"
@ -189,7 +203,7 @@ void generate_complex_body(std::string& smsg, unsigned det, const message* msg,
smsg += "Content-Type: multipart/alternative; boundary=\"" + delimiter_A + "\";\r\n"
"\r\n"
"--" + delimiter_A + "\r\n";
generate_body(smsg, "text/plain", msg->longmsg, transport_encode);
generate_body(smsg, longmsg_mimetype, msg->longmsg, transport_encode);
smsg += "--" + delimiter_A + "\r\n"
"Content-Type: multipart/related; boundary=\"" + delimiter_R + "\";\r\n"
"\r\n"


+ 3
- 1
src/bodygenerator.hh View File

@ -6,11 +6,13 @@
namespace pEpMIME
{
// is "text/plain", optionally annotated with format=... and delsp=...
std::string longmsg_mimetype(const message* msg);
void generate_body(std::string& smsg, sv mime_type, sv body, bool transport_encode);
// generate "multipart/alternative" body with "text/plain" and "text/html" parts
void generate_ma_body(std::string& smsg, sv plain, sv html, bool transport_encode);
void generate_ma_body(std::string& smsg, sv plain_mimetype, sv plain, sv html, bool transport_encode);
// generate "multipart/mixed" body
void generate_mm_body(std::string& smsg, sv mime_type, sv body, const std::vector<Attachment>& a, bool transport_encode);


+ 19
- 2
src/bodyparser.cc View File

@ -7,6 +7,7 @@
#include "message.hh"
#include "rules.hh"
#include "base64.hxx"
#include "headerparser.hh" // for add_opt_field()
#include "quoted_printable.hxx"
#include "string_case.hh"
#include "nfc.hh"
@ -128,6 +129,23 @@ struct has_mimetype
};
void set_longmsg(message* msg, const Message& m)
{
const sv txt_charset = header_value( m.mh.tparams, "charset" );
const sv format = header_value( m.mh.tparams, "format");
const sv delsp = header_value( m.mh.tparams, "delsp");
msg->longmsg = create_string(m.body, txt_charset, m.mh.decoder );
if(format.size())
{
add_opt_field(msg, Pseudo_Header_Format, format);
}
if(delsp.size())
{
add_opt_field(msg, Pseudo_Header_Delsp, delsp);
}
}
// handle multipart/alternative
void handle_multipart_alternative(message* msg, MultipartMessage& vm)
{
@ -136,8 +154,7 @@ void handle_multipart_alternative(message* msg, MultipartMessage& vm)
if(first_text != vm.end())
{
LOG << "ALT-TEXT: MH" << first_text->mh << "\n";
const sv txt_charset = header_value( first_text->mh.tparams, "charset" );
msg->longmsg = create_string(first_text->body, txt_charset, first_text->mh.decoder );
set_longmsg(msg, *first_text);
}
// only add to msg->longmsg_formatted if not already set


+ 21
- 10
src/header_generator.cc View File

@ -195,18 +195,29 @@ void generate_header(std::string& smsg, const message* msg, bool transport_encod
while(spl)
{
const std::string key_low = ascii_tolower(spl->value->key);
auto q = headersMap().find(key_low);
if(q == headersMap().end())
const char* const key = spl->value->key;
// header keys starting with ':' are pseudo headers for pEp-internal use only.
// Don't emit them in the MIME output
if(key[0]==':')
{
// unknown header: only encode if contained control characters or non-ASCII characters
LOG << "\t UNKNWON HDR: " << spl->value->key << " :: " << spl->value->value << " <<< \n";
smsg += E( *(br.vchar | br.ws), spl->value->key, spl->value->value, qp::Text);
smsg += CRLF;
}else{
LOG << "\t KNWON HDR: " << spl->value->key << " :: low_key: " << q->first << " name(): " << q->second->name() << " <<< \n";
q->second->output(smsg, msg, transport_encode);
goto skip_header;
}else
{
const std::string key_low = ascii_tolower(key);
auto q = headersMap().find(key_low);
if(q == headersMap().end())
{
// unknown header: only encode if contained control characters or non-ASCII characters
LOG << "\t UNKNWON HDR: " << spl->value->key << " :: " << spl->value->value << " <<< \n";
smsg += E( *(br.vchar | br.ws), spl->value->key, spl->value->value, qp::Text);
smsg += CRLF;
}else{
LOG << "\t KNWON HDR: " << spl->value->key << " :: low_key: " << q->first << " name(): " << q->second->name() << " <<< \n";
q->second->output(smsg, msg, transport_encode);
}
}
skip_header:
spl = spl->next;
}
}


+ 2
- 1
src/headerparser.cc View File

@ -45,7 +45,7 @@ std::string robust_to_utf8(sv s)
}
static void add_opt_field(message* msg, const sv& name, sv value)
void add_opt_field(message* msg, const sv& name, sv value)
{
const std::string nfc_name = robust_to_utf8(name); // TODO: use views/ranges to avoid copying
const std::string nfc_value = robust_to_utf8(value); // TODO: use views/ranges to avoid copying
@ -295,6 +295,7 @@ namespace {
GlobalCleanup gc;
}
// parses the header and fill the parts in msg
void parse_header(message* msg, const HeaderSection& header)
{


+ 2
- 0
src/headerparser.hh View File

@ -27,6 +27,8 @@ namespace pEpMIME
const HeadersMap& headersMap();
void add_opt_field(message* msg, const sv& name, sv value);
// parses the header and fill the parts in msg
void parse_header(message* msg, const HeaderSection& headers);


+ 7
- 5
src/pEpMIME.cc View File

@ -165,26 +165,28 @@ char* generate_message(const message* msg, bool omit_fields, bool transport_enco
{
generate_header(smsg, msg, transport_encode);
}
const std::string longmsg_mimetype = ::pEpMIME::longmsg_mimetype(msg);
switch(det)
{
case 0 : generate_body(smsg, "text/plain" , "", transport_encode); break; // empty text/plain body
case 1 : generate_body(smsg, "text/plain" , msg->longmsg, transport_encode); break;
case 1 : generate_body(smsg, longmsg_mimetype , msg->longmsg, transport_encode); break;
case 2 : generate_body(smsg, "text/html" , msg->longmsg_formatted, transport_encode); break;
case 3 : generate_ma_body(smsg, msg->longmsg, msg->longmsg_formatted, transport_encode); break;
case 3 : generate_ma_body(smsg, longmsg_mimetype, msg->longmsg, msg->longmsg_formatted, transport_encode); break;
case 4 : generate_mm_body(smsg, sv{}, sv{} , attachments, transport_encode); break;
case 5 : generate_mm_body(smsg, "text/plain", msg->longmsg , attachments, transport_encode); break;
case 5 : generate_mm_body(smsg, longmsg_mimetype, msg->longmsg , attachments, transport_encode); break;
case 6 : generate_complex_body(smsg, det, msg, attachments, transport_encode); break; // FIXME!
case 7 : generate_complex_body(smsg, det, msg, attachments, transport_encode); break; // FIXME!
case 8 : generate_mm_body(smsg, sv{}, sv{} , attachments, transport_encode); break;
case 9 : generate_mm_body(smsg, "text/plain", msg->longmsg , attachments, transport_encode); break;
case 9 : generate_mm_body(smsg, longmsg_mimetype, msg->longmsg , attachments, transport_encode); break;
case 10 : generate_mm_body(smsg, "text/html", msg->longmsg_formatted, attachments, transport_encode); break;
case 11 : generate_complex_body(smsg, det, msg, attachments, transport_encode); break; // FIXME!
case 12 : generate_mm_body(smsg, sv{}, sv{} , attachments, transport_encode); break;
case 13 : generate_mm_body(smsg, "text/plain", msg->longmsg , attachments, transport_encode); break;
case 13 : generate_mm_body(smsg, longmsg_mimetype, msg->longmsg , attachments, transport_encode); break;
case 14 : generate_complex_body(smsg, det, msg, attachments, transport_encode); break; // FIXME!
case 15 : generate_complex_body(smsg, det, msg, attachments, transport_encode); break; // FIXME!


+ 3
- 0
src/pEpMIME_internal.cc View File

@ -66,6 +66,9 @@ namespace pEpMIME
return ::operator<<(o,hs);
}
const sv Pseudo_Header_Format = ":pEp:MIME:longmsg:format";
const sv Pseudo_Header_Delsp = ":pEp:MIME:longmsg:delsp";
} // end of namespace pEpMIME
/*


+ 4
- 0
src/pEpMIME_internal.hh View File

@ -9,6 +9,7 @@
#include <string>
#include <vector>
#include <ostream>
#include <pEp/stringpair.h>
#if (__cplusplus >= 201606) // std::string_view is C++17.
# include <string_view>
@ -96,6 +97,9 @@ namespace pEpMIME
return (str!=nullptr) && (str[0] != '\0');
}
extern const sv Pseudo_Header_Format;
extern const sv Pseudo_Header_Delsp;
} // end of namespace pEpMIME
//template<class T>


+ 36
- 6
src/unittest_mime.cc View File

@ -2,6 +2,7 @@
// see LICENSE.txt
#include "pEpMIME.hh"
#include "pEpMIME_internal.hh" // for Pseudo_Header_Delsp
#include "headerparser.hh"
#include "mime_headers.hh"
#include "print_message.hh"
@ -127,6 +128,7 @@ namespace
const char* subject;
const char* text_plain;
const char* text_html;
const char* delsp;
unsigned nr_of_attachments;
};
@ -148,6 +150,7 @@ namespace
"Test “text/plain” Type: 0",
nullptr,
nullptr,
nullptr, // delsp
0
},
{
@ -160,6 +163,7 @@ namespace
"Test “text/plain” Type: 1",
"Übergrößenänderung.",
nullptr,
nullptr, // delsp
0
},
{
@ -172,6 +176,7 @@ namespace
"Test “text/html” Type: 2",
nullptr,
"<html><body>Übergrößenänderung.</body></html>",
nullptr, // delsp
0
},
{
@ -179,7 +184,7 @@ namespace
"Content-Type: multipart/alternative; boundary=\"=_X_=\"\r\n"
"\r\n"
"--=_X_=\r\n"
"Content-Type: text/plain; charset=\"ISO-8859-1\"\r\n"
"Content-Type: text/plain; delsp=\"Yes\"; charset=\"ISO-8859-1\"\r\n"
"Content-Transfer-Encoding: base64\r\n"
"\r\n"
"3GJlcmdy9t9lbuRuZGVydW5nLg==\r\n"
@ -195,6 +200,7 @@ namespace
"Test “multipart/alternative” Type: 3",
"Übergrößenänderung.",
"<html><body>Übergrößenänderung.</body></html>",
"Yes", // delsp
0
},
{
@ -203,7 +209,7 @@ namespace
"\r\n"
"--=_M_=\r\n"
"Content-Type: image/png;\r\n"
"Content-Disposition: inline\n\r"
"Content-Disposition: inline\r\n"
"Content-Transfer-Encoding: base64\r\n"
"Content-ID: <079addf6-4566-48e9-a88c-0bca23d527a8@pEp.example>\r\n"
"\r\n"
@ -215,6 +221,7 @@ namespace
"Test \"multipart/mixed\" Type: 4",
nullptr,
nullptr,
nullptr, // delsp
1
},
{
@ -223,7 +230,7 @@ namespace
"\r\n"
"--=_M_=\r\n"
"Content-Type: image/png;\r\n"
"Content-Disposition: inline\n\r"
"Content-Disposition: inline\r\n"
"Content-Transfer-Encoding: base64\r\n"
"Content-ID: <1a9d07d9-e799-418f-84c8-5f7c8ae1ba0a@pEp.example>\r\n"
"\r\n"
@ -239,6 +246,7 @@ namespace
"Test \"multipart/mixed\" Type: 5",
"Kürbis: 4€",
nullptr,
nullptr, // delsp
1
},
{
@ -247,7 +255,7 @@ namespace
"\r\n"
"--=_M_=\r\n"
"Content-Type: image/png;\r\n"
"Content-Disposition: inline\n\r"
"Content-Disposition: inline\r\n"
"Content-Transfer-Encoding: base64\r\n"
"Content-ID: <63fdb377-9bc1-4c0a-8252-fe4ed6f91ae9@pEp.example>\r\n"
"\r\n"
@ -263,6 +271,7 @@ namespace
"Test \"multipart/mixed\" Type: 6",
nullptr,
"<html><body>Kürbis: 4€</body></html>",
nullptr, // delsp
1
},
{
@ -271,7 +280,7 @@ namespace
"\r\n"
"--=_MM_=\r\n"
"Content-Type: image/png;\r\n"
"Content-Disposition: inline\n\r"
"Content-Disposition: inline\r\n"
"Content-Transfer-Encoding: base64\r\n"
"Content-ID: <e134a9aa-02ab-45ab-b1d0-0be23a9a0d44@pEp.example>\r\n"
"\r\n"
@ -291,6 +300,7 @@ namespace
"Test \"multipart/mixed\" Type: 7",
"Kürbis: 4€",
"<html><body>Kürbis: 4€</body></html>",
nullptr, // delsp
1 // HTML and PNG as attachment, because it is _not_ multipart/alternative!
},
};
@ -316,6 +326,16 @@ TEST_P( MimeTestP, MimeTypes )
EXPECT_STREQ(m->shortmsg, p.subject);
EXPECT_STREQ(m->longmsg, p.text_plain);
EXPECT_STREQ(m->longmsg_formatted, p.text_html);
const stringpair_list_t* const delsp = stringpair_list_find(m->opt_fields, pEpMIME::Pseudo_Header_Delsp.data());
if(p.delsp)
{
ASSERT_NE(delsp, nullptr);
EXPECT_STREQ(delsp->value->value, p.delsp);
}else{
EXPECT_EQ(delsp, nullptr);
}
EXPECT_EQ( bloblist_length(m->attachments), p.nr_of_attachments);
std::cerr << "Opt-Fields: " << pEpMIME::out(m->opt_fields) << "\n";
@ -326,7 +346,17 @@ TEST_P( MimeTestP, MimeTypes )
const pEpMIME::ContentType ct{ct_line};
EXPECT_EQ( ct.mime_type(), p.mime_type );
}
if(delsp)
{
char* c = pEpMIME::generate_message(m, false, true);
ASSERT_NE( c, nullptr );
std::cout << "§§§§§§ GENERATED DELSP MESSAGE: §§§§§§" << std::endl;
std::cout << c << std::endl;
std::cout << "§§§§§§§§§§§§§§§§§§" << std::endl;
free(c);
}
free_message(m);
}


+ 8
- 0
src/unittest_stringcase.cc View File

@ -4,6 +4,7 @@
#include <gtest/gtest.h>
#include "string_case.hh"
#include <set>
#include <vector>
#include <algorithm>
@ -77,3 +78,10 @@ TEST( StringCaseTest, Meh )
h=t.hash;
}
}
TEST( StringCaseTest, Collide )
{
std::set<TestEntry> s;
}

Loading…
Cancel
Save