Don't strip CRLF from iCal, vCard, and other formats that requires them.

* src/engine/rfc822/rfc822-part.vala (Part): Add a blacklist for text
  types that shouldn't have CRLF's stripped. Add unit tests.
This commit is contained in:
Michael James Gratton 2018-05-20 12:51:59 +10:00
parent 440a4d5932
commit 65b44c3b3a
2 changed files with 63 additions and 6 deletions

View file

@ -26,6 +26,27 @@ public class Geary.RFC822.Part : Object {
HTML;
}
// The set of text/* types that must have CRLF preserved, since it
// is part of their format. These really should be under
// application/*, but here we are.
private static Gee.Set<string> CR_PRESERVING_TEXT_TYPES =
new Gee.HashSet<string>();
static construct {
// VCard
CR_PRESERVING_TEXT_TYPES.add("vcard");
CR_PRESERVING_TEXT_TYPES.add("x-vcard");
CR_PRESERVING_TEXT_TYPES.add("directory");
// iCal
CR_PRESERVING_TEXT_TYPES.add("calendar");
// MS RTF
CR_PRESERVING_TEXT_TYPES.add("rtf");
}
/**
* The entity's Content-Type.
*
@ -157,9 +178,14 @@ public class Geary.RFC822.Part : Object {
bool flowed = content_type.params.has_value_ci("format", "flowed");
bool delsp = content_type.params.has_value_ci("DelSp", "yes");
// Unconditionally remove the CR's in any CRLF sequence, since
// they are effectively a wire encoding.
filter.add(new GMime.FilterCRLF(false, false));
// Remove the CR's in any CRLF sequence since they are
// effectively a wire encoding, unless the format requires
// them.
GMime.ContentEncoding encoding =
this.source_part.get_content_encoding();
if (!(content_type.media_subtype in CR_PRESERVING_TEXT_TYPES)) {
filter.add(new GMime.FilterCRLF(false, false));
}
if (flowed) {
filter.add(

View file

@ -7,17 +7,22 @@
class Geary.RFC822.PartTest : TestCase {
private const string BODY = "This is an attachment.\n";
private const string CR_BODY = "This is an attachment.\n";
private const string CRLF_BODY = "This is an attachment.\r\n";
private const string ICAL_BODY = "BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n";
public PartTest() {
base("Geary.RFC822.PartTest");
add_test("new_from_empty_mime_part", new_from_empty_mime_part);
add_test("new_from_complete_mime_part", new_from_complete_mime_part);
add_test("write_to_buffer_plain", write_to_buffer_plain);
add_test("write_to_buffer_plain_crlf", write_to_buffer_plain_crlf);
add_test("write_to_buffer_plain_ical", write_to_buffer_plain_ical);
}
public void new_from_empty_mime_part() throws Error {
GMime.Part part = new_part(null, BODY.data);
GMime.Part part = new_part(null, CR_BODY.data);
part.set_header("Content-Type", "");
Part test = new Part(part);
@ -33,7 +38,7 @@ class Geary.RFC822.PartTest : TestCase {
const string ID = "test-id";
const string DESC = "test description";
GMime.Part part = new_part(TYPE, BODY.data);
GMime.Part part = new_part(TYPE, CR_BODY.data);
part.set_content_id(ID);
part.set_content_description(DESC);
part.set_content_disposition(
@ -52,6 +57,32 @@ class Geary.RFC822.PartTest : TestCase {
);
}
public void write_to_buffer_plain() throws Error {
Part test = new Part(new_part("text/plain", CR_BODY.data));
Memory.Buffer buf = test.write_to_buffer();
assert_string(CR_BODY, buf.to_string());
}
public void write_to_buffer_plain_crlf() throws Error {
Part test = new Part(new_part("text/plain", CRLF_BODY.data));
Memory.Buffer buf = test.write_to_buffer();
// CRLF should be stripped
assert_string(CR_BODY, buf.to_string());
}
public void write_to_buffer_plain_ical() throws Error {
Part test = new Part(new_part("text/calendar", ICAL_BODY.data));
Memory.Buffer buf = test.write_to_buffer();
// CRLF should not be stripped
assert_string(ICAL_BODY, buf.to_string());
}
private GMime.Part new_part(string? mime_type,
uint8[] body,
GMime.ContentEncoding encoding = GMime.ContentEncoding.DEFAULT) {