Merge branch 'mainline' into remove-old-msgs-beyond-storage-pref

This commit is contained in:
Chris Heywood 2020-01-08 14:43:52 +01:00
commit 5d53bbec26
43 changed files with 5170 additions and 5814 deletions

View file

@ -180,7 +180,7 @@ private class Geary.ImapDB.Attachment : Geary.Attachment {
target_stream
);
stream = new GMime.StreamBuffer(
stream, GMime.StreamBufferMode.BLOCK_WRITE
stream, GMime.StreamBufferMode.WRITE
);
part.write_to_stream(stream, RFC822.Part.EncodingConversion.NONE);

View file

@ -325,7 +325,6 @@ geary_engine_dependencies = [
gmime,
libmath,
libxml,
libytnef,
posix,
sqlite
]

View file

@ -102,7 +102,7 @@ public class Geary.Mime.ContentDisposition : Geary.BaseObject {
out is_unknown);
is_unknown_disposition_type = is_unknown;
original_disposition_type_string = content_disposition.get_disposition();
params = new ContentParameters.from_gmime(content_disposition.get_params());
params = new ContentParameters.from_gmime(content_disposition.get_parameters());
}
}

View file

@ -51,11 +51,13 @@ public class Geary.Mime.ContentParameters : BaseObject {
}
}
internal ContentParameters.from_gmime(GMime.Param? gmime_param) {
internal ContentParameters.from_gmime(GMime.ParamList? gmime_params) {
Gee.Map<string,string> params = new Gee.HashMap<string,string>();
while (gmime_param != null) {
params.set(gmime_param.get_name(), gmime_param.get_value());
gmime_param = gmime_param.get_next();
if (gmime_params != null) {
for (int i = 0; i < gmime_params.length(); i++) {
GMime.Param gmime_param = gmime_params.get_parameter_at(i);
params.set(gmime_param.get_name(), gmime_param.get_value());
}
}
this(params);
}

View file

@ -74,7 +74,10 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
if (!str.contains("/"))
throw new MimeError.PARSE("Invalid MIME Content-Type: %s", str);
return new ContentType.from_gmime(new GMime.ContentType.from_string(str));
return new ContentType.from_gmime(GMime.ContentType.parse(
Geary.RFC822.get_parser_options(),
str
));
}
/**
@ -158,7 +161,7 @@ public class Geary.Mime.ContentType : Geary.BaseObject {
internal ContentType.from_gmime(GMime.ContentType content_type) {
media_type = content_type.get_media_type().strip();
media_subtype = content_type.get_media_subtype().strip();
params = new ContentParameters.from_gmime(content_type.get_params());
params = new ContentParameters.from_gmime(content_type.get_parameters());
}
/**

View file

@ -49,7 +49,7 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
return new_filter;
}
private void do_filter(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
private void do_filter(uint8[] inbuf, size_t prespace, out unowned uint8[] processed_buffer,
out size_t outprespace, bool flush) {
// This may not be strictly necessary.
@ -64,7 +64,7 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
}
for (uint i = 0; i < inbuf.length; i++) {
char c = inbuf[i];
uint8 c = inbuf[i];
if (in_prefix && !in_tag) {
if (c == Geary.RFC822.Utils.QUOTE_MARKER) {
@ -122,12 +122,12 @@ private class Geary.RFC822.FilterBlockquotes : GMime.Filter {
outprespace = this.outpre;
}
public override void filter(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
public override void filter(uint8[] inbuf, size_t prespace, out unowned uint8[] processed_buffer,
out size_t outprespace) {
do_filter(inbuf, prespace, out processed_buffer, out outprespace, false);
}
public override void complete(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
public override void complete(uint8[] inbuf, size_t prespace, out unowned uint8[] processed_buffer,
out size_t outprespace) {
do_filter(inbuf, prespace, out processed_buffer, out outprespace, true);
}

View file

@ -57,7 +57,7 @@ private class Geary.RFC822.FilterFlowed : GMime.Filter {
return new_filter;
}
public override void filter(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
public override void filter(uint8[] inbuf, size_t prespace, out unowned uint8[] processed_buffer,
out size_t outprespace) {
// Worst-case scenario: We are about to leave the prefix,
@ -67,7 +67,7 @@ private class Geary.RFC822.FilterFlowed : GMime.Filter {
uint out_index = 0;
for (uint i = 0; i < inbuf.length; i++) {
char c = inbuf[i];
uint8 c = inbuf[i];
if (this.in_prefix) {
if (c == '>') {
@ -147,7 +147,7 @@ private class Geary.RFC822.FilterFlowed : GMime.Filter {
outprespace = this.outpre;
}
public override void complete(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
public override void complete(uint8[] inbuf, size_t prespace, out unowned uint8[] processed_buffer,
out size_t outprespace) {
filter(inbuf, prespace, out processed_buffer, out outprespace);
}

View file

@ -26,7 +26,7 @@ private class Geary.RFC822.FilterPlain : GMime.Filter {
return new_filter;
}
public override void filter(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
public override void filter(uint8[] inbuf, size_t prespace, out unowned uint8[] processed_buffer,
out size_t outprespace) {
// This may not be strictly necessary.
@ -34,7 +34,7 @@ private class Geary.RFC822.FilterPlain : GMime.Filter {
uint out_index = 0;
for (uint i = 0; i < inbuf.length; i++) {
char c = inbuf[i];
uint8 c = inbuf[i];
if (in_prefix) {
if (c == '>') {
@ -56,7 +56,7 @@ private class Geary.RFC822.FilterPlain : GMime.Filter {
outprespace = this.outpre;
}
public override void complete(char[] inbuf, size_t prespace, out unowned char[] processed_buffer,
public override void complete(uint8[] inbuf, size_t prespace, out unowned uint8[] processed_buffer,
out size_t outprespace) {
filter(inbuf, prespace, out processed_buffer, out outprespace);
}

View file

@ -42,11 +42,17 @@ public class Geary.RFC822.MailboxAddress :
}
private static string decode_name(string name) {
return GMime.utils_header_decode_phrase(prepare_header_text_part(name));
return GMime.utils_header_decode_phrase(
Geary.RFC822.get_parser_options(),
prepare_header_text_part(name)
);
}
private static string decode_address_part(string mailbox) {
return GMime.utils_header_decode_text(prepare_header_text_part(mailbox));
return GMime.utils_header_decode_text(
Geary.RFC822.get_parser_options(),
prepare_header_text_part(mailbox)
);
}
private static bool display_name_needs_quoting(string name) {
@ -118,8 +124,9 @@ public class Geary.RFC822.MailboxAddress :
// _internet_address_decode_name() function.
// see if a broken mailer has sent raw 8-bit information
string text = GMime.utils_text_is_8bit(part, part.length)
? part : GMime.utils_decode_8bit(part, part.length);
string text = GMime.utils_text_is_8bit(part.data)
? part : GMime.utils_decode_8bit(Geary.RFC822.get_parser_options(),
part.data);
// unquote the string then decode the text
GMime.utils_unquote_string(text);
@ -222,16 +229,19 @@ public class Geary.RFC822.MailboxAddress :
}
public MailboxAddress.from_rfc822_string(string rfc822) throws RFC822Error {
InternetAddressList addrlist = InternetAddressList.parse_string(rfc822);
GMime.InternetAddressList addrlist = GMime.InternetAddressList.parse(
Geary.RFC822.get_parser_options(),
rfc822
);
if (addrlist == null)
return;
int length = addrlist.length();
for (int ctr = 0; ctr < length; ctr++) {
InternetAddress? addr = addrlist.get_address(ctr);
GMime.InternetAddress? addr = addrlist.get_address(ctr);
// TODO: Handle group lists
InternetAddressMailbox? mbox_addr = addr as InternetAddressMailbox;
GMime.InternetAddressMailbox? mbox_addr = addr as GMime.InternetAddressMailbox;
if (mbox_addr != null) {
this.gmime(mbox_addr);
return;
@ -240,11 +250,11 @@ public class Geary.RFC822.MailboxAddress :
throw new RFC822Error.INVALID("Could not parse RFC822 address: %s", rfc822);
}
public MailboxAddress.gmime(InternetAddressMailbox mailbox) {
public MailboxAddress.gmime(GMime.InternetAddressMailbox mailbox) {
// GMime strips source route for us, so the address part
// should only ever contain a single '@'
string? name = mailbox.get_name();
if (name != null) {
if (name != "") {
this.name = decode_name(name);
}
@ -456,7 +466,11 @@ public class Geary.RFC822.MailboxAddress :
public string to_rfc822_string() {
return has_distinct_name()
? "%s <%s>".printf(
GMime.utils_header_encode_phrase(this.name),
GMime.utils_header_encode_phrase(
Geary.RFC822.get_format_options(),
this.name,
"iso-8859-1"
),
to_rfc822_address()
)
: to_rfc822_address();

View file

@ -74,27 +74,30 @@ public class Geary.RFC822.MailboxAddresses :
}
public MailboxAddresses.from_rfc822_string(string rfc822) {
InternetAddressList addrlist = InternetAddressList.parse_string(rfc822);
GMime.InternetAddressList addrlist = GMime.InternetAddressList.parse(
Geary.RFC822.get_parser_options(),
rfc822
);
if (addrlist == null)
return;
int length = addrlist.length();
for (int ctr = 0; ctr < length; ctr++) {
InternetAddress? addr = addrlist.get_address(ctr);
GMime.InternetAddress? addr = addrlist.get_address(ctr);
InternetAddressMailbox? mbox_addr = addr as InternetAddressMailbox;
GMime.InternetAddressMailbox? mbox_addr = addr as GMime.InternetAddressMailbox;
if (mbox_addr != null) {
this.addrs.add(new MailboxAddress.gmime(mbox_addr));
} else {
// XXX this is pretty bad - we just flatten the
// group's addresses into this list, merging lists and
// losing the group names.
InternetAddressGroup? mbox_group = addr as InternetAddressGroup;
GMime.InternetAddressGroup? mbox_group = addr as GMime.InternetAddressGroup;
if (mbox_group != null) {
InternetAddressList group_list = mbox_group.get_members();
GMime.InternetAddressList group_list = mbox_group.get_members();
for (int i = 0; i < group_list.length(); i++) {
InternetAddressMailbox? group_addr =
addrlist.get_address(i) as InternetAddressMailbox;
GMime.InternetAddressMailbox? group_addr =
addrlist.get_address(i) as GMime.InternetAddressMailbox;
if (group_addr != null) {
this.addrs.add(new MailboxAddress.gmime(group_addr));
}

View file

@ -173,27 +173,13 @@ public class Geary.RFC822.Date : Geary.RFC822.MessageData, Geary.MessageData.Abs
public DateTime value { get; private set; }
public Date(string rfc822) throws ImapError {
int offset = 0;
int64 time_t_utc = GMime.utils_header_decode_date(rfc822, out offset);
if (time_t_utc == 0)
throw new ImapError.PARSE_ERROR(
"Unable to parse \"%s\": Not ISO-8601 date", rfc822
);
DateTime? value = new DateTime.from_unix_utc(time_t_utc);
DateTime? value = GMime.utils_header_decode_date(rfc822);
if (value == null) {
throw new ImapError.PARSE_ERROR(
"Unable to parse \"%s\": Outside supported range", rfc822
);
}
this.value = value;
if (offset != 0) {
this.value = value.to_timezone(
new GLib.TimeZone("%+05d".printf(offset))
);
}
this.original = rfc822;
}
@ -206,18 +192,7 @@ public class Geary.RFC822.Date : Geary.RFC822.MessageData, Geary.MessageData.Abs
* Returns the {@link Date} in RFC 822 format.
*/
public string to_rfc822_string() {
// Although GMime documents its conversion methods as
// requiring the tz offset in hours, it appears the number is
// handed directly to the string (i.e. an offset of -7:30 becomes
// "-0007", whereas we want "-0730").
int hours = (int) GLib.Math.floor(value.get_utc_offset() / TimeSpan.HOUR);
int minutes = (int) (
(value.get_utc_offset() % TimeSpan.HOUR) / (double) TimeSpan.HOUR * 60
);
return GMime.utils_header_format_date(
(time_t) this.value.to_utc().to_unix(),
(hours * 100) + minutes
);
return GMime.utils_header_format_date(this.value);
}
/**
@ -261,7 +236,7 @@ public class Geary.RFC822.Subject : Geary.MessageData.StringMessageData,
}
public Subject.decode(string value) {
base (GMime.utils_header_decode_text(value));
base (GMime.utils_header_decode_text(Geary.RFC822.get_parser_options(), value));
original = value;
}
@ -337,9 +312,8 @@ public class Geary.RFC822.Header : Geary.MessageData.BlockMessageData, Geary.RFC
GMime.Parser parser = new GMime.Parser.with_stream(Utils.create_stream_mem(buffer));
parser.set_respect_content_length(false);
parser.set_scan_from(false);
message = parser.construct_message();
message = parser.construct_message(Geary.RFC822.get_parser_options());
if (message == null)
throw new RFC822Error.INVALID("Unable to parse RFC 822 headers");
@ -347,17 +321,22 @@ public class Geary.RFC822.Header : Geary.MessageData.BlockMessageData, Geary.RFC
}
public string? get_header(string name) throws RFC822Error {
return get_headers().get(name);
GMime.Header header = get_headers().get_header(name);
if (header != null)
// We should not parse the raw-value here, but use GMime's parsing
// functionality instead.
// See: https://gitlab.gnome.org/GNOME/geary/merge_requests/382#note_669699
return GMime.utils_header_unfold(header.get_raw_value());
else
return null;
}
public string[] get_header_names() throws RFC822Error {
if (this.names == null) {
this.names = new string[0];
GMime.HeaderIter iter = new GMime.HeaderIter();
if (get_headers().get_iter(iter) && iter.first()) {
do {
names += iter.get_name();
} while (iter.next());
GMime.HeaderList headers = get_headers();
for (int i = 0; i < headers.get_count(); i++) {
names += headers.get_header_at(i).get_name();
}
}
return this.names;
@ -388,7 +367,7 @@ public class Geary.RFC822.PreviewText : Geary.RFC822.Text {
// Parse the header.
GMime.Stream header_stream = Utils.create_stream_mem(preview_header);
GMime.Parser parser = new GMime.Parser.with_stream(header_stream);
GMime.Part? gpart = parser.construct_part() as GMime.Part;
GMime.Part? gpart = parser.construct_part(Geary.RFC822.get_parser_options()) as GMime.Part;
if (gpart != null) {
Part part = new Part(gpart);
@ -402,7 +381,7 @@ public class Geary.RFC822.PreviewText : Geary.RFC822.Text {
new GMime.StreamMem.with_buffer(preview.get_uint8_array()),
gpart.get_content_encoding()
);
gpart.set_content_object(body);
gpart.set_content(body);
try {
Memory.Buffer preview_buffer = part.write_to_buffer(

View file

@ -29,7 +29,6 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
*/
public delegate string? InlinePartReplacer(Part part);
private const string HEADER_SENDER = "Sender";
private const string HEADER_IN_REPLY_TO = "In-Reply-To";
private const string HEADER_REFERENCES = "References";
private const string HEADER_MAILER = "X-Mailer";
@ -89,7 +88,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
public Message(Full full) throws RFC822Error {
GMime.Parser parser = new GMime.Parser.with_stream(Utils.create_stream_mem(full.buffer));
message = parser.construct_message();
message = parser.construct_message(Geary.RFC822.get_parser_options());
if (message == null)
throw new RFC822Error.INVALID("Unable to parse RFC 822 message");
@ -115,7 +114,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
stream_cat.add_source(new GMime.StreamMem.with_buffer(body.buffer.get_bytes().get_data()));
GMime.Parser parser = new GMime.Parser.with_stream(stream_cat);
message = parser.construct_message();
message = parser.construct_message(Geary.RFC822.get_parser_options());
if (message == null)
throw new RFC822Error.INVALID("Unable to parse RFC 822 message");
@ -136,67 +135,71 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
this.from = email.from;
this.date = email.date;
// GMimeMessage.set_sender actually sets the From header - and
// although the API docs make it sound otherwise, it also
// supports a list of addresses
message.set_sender(this.from.to_rfc822_string());
message.set_date_as_string(this.date.serialize());
if (message_id != null) {
this.message_id = new MessageID(message_id);
message.set_message_id(message_id);
this.message.set_date(this.date.value);
if (email.from != null) {
foreach (RFC822.MailboxAddress mailbox in email.from)
this.message.add_mailbox(FROM, mailbox.name, mailbox.address);
}
// Optional headers
if (email.to != null) {
this.to = email.to;
foreach (RFC822.MailboxAddress mailbox in email.to)
this.message.add_recipient(GMime.RecipientType.TO, mailbox.name, mailbox.address);
this.message.add_mailbox(TO, mailbox.name, mailbox.address);
}
if (email.cc != null) {
this.cc = email.cc;
foreach (RFC822.MailboxAddress mailbox in email.cc)
this.message.add_recipient(GMime.RecipientType.CC, mailbox.name, mailbox.address);
this.message.add_mailbox(CC, mailbox.name, mailbox.address);
}
if (email.bcc != null) {
this.bcc = email.bcc;
foreach (RFC822.MailboxAddress mailbox in email.bcc)
this.message.add_recipient(GMime.RecipientType.BCC, mailbox.name, mailbox.address);
this.message.add_mailbox(BCC, mailbox.name, mailbox.address);
}
if (email.sender != null) {
this.sender = email.sender;
this.message.set_header(HEADER_SENDER,
email.sender.to_rfc822_string());
this.message.add_mailbox(SENDER, this.sender.name, this.sender.address);
}
if (email.reply_to != null) {
this.reply_to = email.reply_to;
this.message.set_reply_to(email.reply_to.to_rfc822_string());
foreach (RFC822.MailboxAddress mailbox in email.reply_to)
this.message.add_mailbox(REPLY_TO, mailbox.name, mailbox.address);
}
if (email.in_reply_to != null) {
this.in_reply_to = email.in_reply_to;
// We could use `this.message.add_mailbox()` in a similar way like
// we did for the other headers, but this would require to change
// the type of `email.in_reply_to` and `this.in_reply_to` from
// `RFC822.MessageIDList` to `RFC822.MailboxAddresses`.
this.message.set_header(HEADER_IN_REPLY_TO,
email.in_reply_to.to_rfc822_string());
email.in_reply_to.to_rfc822_string(),
Geary.RFC822.get_charset());
}
if (email.references != null) {
this.references = email.references;
this.message.set_header(HEADER_REFERENCES,
email.references.to_rfc822_string());
email.references.to_rfc822_string(),
Geary.RFC822.get_charset());
}
if (email.subject != null) {
this.subject = email.subject;
this.message.set_subject(email.subject.value);
this.message.set_subject(email.subject.value,
Geary.RFC822.get_charset());
}
// User-Agent
if (!Geary.String.is_empty(email.mailer)) {
this.mailer = email.mailer;
this.message.set_header(HEADER_MAILER, email.mailer);
this.message.set_header(HEADER_MAILER, email.mailer,
Geary.RFC822.get_charset());
}
// Build the message's body mime parts
@ -385,28 +388,15 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
// create the new object. Kinda sucks, but our hands are tied.
this.from_buffer(email.message_to_memory_buffer(false, false));
// GMime also drops the ball for the *new* message. When it comes out of the GMime
// Parser, its "mime part" somehow isn't realizing it has a Content-Type header
// already, so whenever you manipulate the headers, it adds a duplicate one. This
// odd looking hack ensures that any header manipulation is done while the "mime
// part" is an empty object, and when we re-set the "mime part", there's only the
// one Content-Type header. In other words, this hack prevents the duplicate
// header, somehow.
GMime.Object original_mime_part = message.get_mime_part();
GMime.Message empty = new GMime.Message(true);
message.set_mime_part(empty.get_mime_part());
message.remove_header(HEADER_BCC);
bcc = null;
message.set_mime_part(original_mime_part);
this.message.remove_header(HEADER_BCC);
this.bcc = null;
}
private GMime.Object? coalesce_related(Gee.List<GMime.Object> parts,
string type) {
GMime.Object? part = coalesce_parts(parts, "related");
if (parts.size > 1) {
part.set_header("Type", type);
part.set_header("Type", type, Geary.RFC822.get_charset());
}
return part;
}
@ -433,11 +423,12 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
FileQueryInfoFlags.NONE
);
GMime.Part part = new GMime.Part();
GMime.Part part = new GMime.Part.with_type("text", "plain");
part.set_disposition(disposition.serialize());
part.set_filename(file.get_basename());
GMime.ContentType content_type = new GMime.ContentType.from_string(
GMime.ContentType content_type = GMime.ContentType.parse(
Geary.RFC822.get_parser_options(),
file_info.get_content_type()
);
part.set_content_type(content_type);
@ -468,7 +459,10 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
);
}
GMime.ContentType? content_type = new GMime.ContentType.from_string(mime_type.get_mime_type());
GMime.ContentType? content_type = GMime.ContentType.parse(
Geary.RFC822.get_parser_options(),
mime_type.get_mime_type()
);
if (content_type == null) {
throw new RFC822Error.INVALID(
@ -476,7 +470,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
);
}
GMime.Part part = new GMime.Part();
GMime.Part part = new GMime.Part.with_type("text", "plain");
part.set_disposition(disposition.serialize());
part.set_filename(basename);
part.set_content_type(content_type);
@ -515,7 +509,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
}
part.set_content_encoding(encoding);
part.set_content_object(
part.set_content(
new GMime.DataWrapper.with_stream(
stream, GMime.ContentEncoding.BINARY
)
@ -536,7 +530,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
Geary.Email email = new Geary.Email(id);
email.set_message_header(new Geary.RFC822.Header(new Geary.Memory.StringBuffer(
message.get_headers())));
message.get_headers(Geary.RFC822.get_format_options()))));
email.set_send_date(date);
email.set_originators(from, sender, reply_to);
email.set_receivers(to, cc, bcc);
@ -884,68 +878,76 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
}
private void stock_from_gmime() {
this.message.get_header_list().foreach((name, value) => {
switch (name.down()) {
case "from":
this.from = append_address(this.from, value);
break;
GMime.HeaderList headers = this.message.get_header_list();
for (int i = 0; i < headers.get_count(); i++) {
GMime.Header header = headers.get_header_at(i);
string name = header.get_name();
// We should not parse the raw-value here, but use GMime's parsing
// functionality instead.
// See: https://gitlab.gnome.org/GNOME/geary/merge_requests/382#note_669699
string value = GMime.utils_header_unfold(header.get_raw_value());
switch (name.down()) {
case "from":
this.from = append_address(this.from, value);
break;
case "sender":
try {
this.sender = new RFC822.MailboxAddress.from_rfc822_string(value);
} catch (Error err) {
debug("Could parse subject: %s", err.message);
}
break;
case "sender":
try {
this.sender = new RFC822.MailboxAddress.from_rfc822_string(value);
} catch (Error err) {
debug("Could parse subject: %s", err.message);
}
break;
case "reply-to":
this.reply_to = append_address(this.reply_to, value);
break;
case "reply-to":
this.reply_to = append_address(this.reply_to, value);
break;
case "to":
this.to = append_address(this.to, value);
break;
case "to":
this.to = append_address(this.to, value);
break;
case "cc":
this.cc = append_address(this.cc, value);
break;
case "cc":
this.cc = append_address(this.cc, value);
break;
case "bcc":
this.bcc = append_address(this.bcc, value);
break;
case "bcc":
this.bcc = append_address(this.bcc, value);
break;
case "subject":
this.subject = new RFC822.Subject.decode(value);
break;
case "subject":
this.subject = new RFC822.Subject.decode(value);
break;
case "date":
try {
this.date = new Geary.RFC822.Date(value);
} catch (Error err) {
debug("Could not parse date: %s", err.message);
}
break;
case "date":
try {
this.date = new Geary.RFC822.Date(value);
} catch (Error err) {
debug("Could not parse date: %s", err.message);
}
break;
case "message-id":
this.message_id = new MessageID(value);
break;
case "message-id":
this.message_id = new MessageID(value);
break;
case "in-reply-to":
this.in_reply_to = append_message_id(this.in_reply_to, value);
break;
case "in-reply-to":
this.in_reply_to = append_message_id(this.in_reply_to, value);
break;
case "references":
this.references = append_message_id(this.references, value);
break;
case "references":
this.references = append_message_id(this.references, value);
break;
case "x-mailer":
this.mailer = GMime.utils_header_decode_text(value);
break;
case "x-mailer":
this.mailer = GMime.utils_header_decode_text(Geary.RFC822.get_parser_options(), value);
break;
default:
break;
}
});
default:
// do nothing
break;
}
};
}
private MailboxAddresses append_address(MailboxAddresses? existing,
@ -990,11 +992,11 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
if (requested_disposition == Mime.DispositionType.UNSPECIFIED || disposition == requested_disposition) {
GMime.Stream stream = new GMime.StreamMem();
message.write_to_stream(stream);
message.write_to_stream(Geary.RFC822.get_format_options(), stream);
GMime.DataWrapper data = new GMime.DataWrapper.with_stream(stream,
GMime.ContentEncoding.BINARY); // Equivalent to no encoding
GMime.Part part = new GMime.Part.with_type("message", "rfc822");
part.set_content_object(data);
part.set_content(data);
part.set_filename((message.get_subject() ?? _("(no subject)")) + ".eml");
attachments.add(new Part(part));
}
@ -1017,7 +1019,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
#if WITH_TNEF_SUPPORT
if (content_type.is_type("application", "vnd.ms-tnef")) {
GMime.StreamMem stream = new GMime.StreamMem();
((GMime.Part) root).get_content_object().write_to_stream(stream);
((GMime.Part) root).get_content().write_to_stream(stream);
ByteArray tnef_data = stream.get_byte_array();
Ytnef.TNEFStruct tn;
if (Ytnef.ParseMemory(tnef_data.data, out tn) == 0) {
@ -1050,10 +1052,10 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
string filename = (string) filenameProp.data;
uint8[] data = Bytes.unref_to_data(new Bytes(a.FileData.data));
GMime.Part part = new GMime.Part();
GMime.Part part = new GMime.Part.with_type("text", "plain");
part.set_filename(filename);
part.set_content_type(new GMime.ContentType.from_string(GLib.ContentType.guess(filename, data, null)));
part.set_content_object(new GMime.DataWrapper.with_stream(new GMime.StreamMem.with_buffer(data), GMime.ContentEncoding.BINARY));
part.set_content_type(GMime.ContentType.parse(Geary.RFC822.get_parser_options(), GLib.ContentType.guess(filename, data, null)));
part.set_content(new GMime.DataWrapper.with_stream(new GMime.StreamMem.with_buffer(data), GMime.ContentEncoding.BINARY));
return part;
}
#endif
@ -1092,19 +1094,29 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
stream.set_owner(false);
GMime.StreamFilter stream_filter = new GMime.StreamFilter(stream);
stream_filter.add(new GMime.FilterCRLF(encoded, dotstuffed));
if (encoded) {
stream_filter.add(new GMime.FilterUnix2Dos(false));
} else {
stream_filter.add(new GMime.FilterDos2Unix(false));
}
if (dotstuffed) {
stream_filter.add(new GMime.FilterSmtpData());
}
if (message.write_to_stream(stream_filter) < 0)
throw new RFC822Error.FAILED("Unable to write RFC822 message to memory buffer");
if (message.write_to_stream(Geary.RFC822.get_format_options(), stream_filter) < 0)
throw new RFC822Error.FAILED("Unable to write RFC822 message to filter stream");
if (stream_filter.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush RFC822 message to memory stream");
if (stream.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush RFC822 message to memory buffer");
return new Memory.ByteBuffer.from_byte_array(byte_array);
}
public string to_string() {
return message.to_string();
return message.to_string(Geary.RFC822.get_format_options());
}
/**
@ -1152,11 +1164,13 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
// Base64-encoded text needs to have CR's added after LF's
// before encoding, otherwise it breaks format=flowed. See
// Bug 753528.
filter_stream.add(new GMime.FilterCRLF(true, false));
filter_stream.add(new GMime.FilterUnix2Dos(false));
}
GMime.ContentType complete_type =
new GMime.ContentType.from_string(content_type);
GMime.ContentType complete_type = GMime.ContentType.parse(
Geary.RFC822.get_parser_options(),
content_type
);
complete_type.set_parameter("charset", charset);
if (is_flowed) {
complete_type.set_parameter("format", "flowed");
@ -1166,9 +1180,9 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
filter_stream, GMime.ContentEncoding.DEFAULT
);
GMime.Part body_part = new GMime.Part();
GMime.Part body_part = new GMime.Part.with_type("text", "plain");
body_part.set_content_type(complete_type);
body_part.set_content_object(body);
body_part.set_content(body);
body_part.set_content_encoding(encoding);
return body_part;
}

View file

@ -160,7 +160,7 @@ public class Geary.RFC822.Part : Object {
BodyFormatting format = BodyFormatting.NONE)
throws RFC822Error {
GMime.DataWrapper? wrapper = (this.source_part != null)
? this.source_part.get_content_object() : null;
? this.source_part.get_content() : null;
if (wrapper == null) {
throw new RFC822Error.INVALID(
"Could not get the content wrapper for content-type %s",
@ -201,7 +201,7 @@ public class Geary.RFC822.Part : Object {
if ((this.source_part == null ||
this.source_part.encoding != BASE64) &&
!(content_type.media_subtype in CR_PRESERVING_TEXT_TYPES)) {
filter.add(new GMime.FilterCRLF(false, false));
filter.add(new GMime.FilterDos2Unix(false));
}
if (flowed) {
@ -226,12 +226,18 @@ public class Geary.RFC822.Part : Object {
filter.add(new Geary.RFC822.FilterBlockquotes());
}
wrapper.write_to_stream(filter);
filter.flush();
if (wrapper.write_to_stream(filter) < 0)
throw new RFC822Error.FAILED("Unable to write textual RFC822 part to filter stream");
if (filter.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush textual RFC822 part to destination stream");
if (destination.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush textual RFC822 part to destination");
} else {
// Keep as binary
wrapper.write_to_stream(destination);
destination.flush();
if (wrapper.write_to_stream(destination) < 0)
throw new RFC822Error.FAILED("Unable to write binary RFC822 part to destination stream");
if (destination.flush() != 0)
throw new RFC822Error.FAILED("Unable to flush binary RFC822 part to destination");
}
}

View file

@ -186,7 +186,7 @@ public string email_addresses_for_reply(Geary.RFC822.MailboxAddresses? addresses
}
public bool comp_char_arr_slice(char[] array, uint start, string comp) {
public bool comp_char_arr_slice(uint8[] array, uint start, string comp) {
for (int i = 0; i < comp.length; i++) {
if (array[start + i] != comp[i])
return false;
@ -277,7 +277,7 @@ public async string get_best_charset(GMime.Stream in_stream,
},
cancellable
);
return filter.charset();
return filter.get_charset();
}
/**

View file

@ -19,11 +19,6 @@ public enum TextFormat {
*/
public const string UTF8_CHARSET = "UTF-8";
/**
* Official IANA charset encoding name for the ASCII character set.
*/
public const string ASCII_CHARSET = "US-ASCII";
private int init_count = 0;
internal Regex? invalid_filename_character_re = null;
@ -32,18 +27,8 @@ public void init() {
if (init_count++ != 0)
return;
GMime.init(GMime.ENABLE_RFC2047_WORKAROUNDS);
// This has the effect of ensuring all non US-ASCII and non-ISO-8859-1
// headers are always encoded as UTF-8. This should be fine because
// message bodies are also always sent as UTF-8.
const string?[] USER_CHARSETS = {
UTF8_CHARSET,
// GMime.set_user_charsets calls g_strdupv under the hood, so
// the array needs to be null-terminated
null
};
GMime.set_user_charsets(USER_CHARSETS);
GMime.init();
GMime.ParserOptions.get_default().set_allow_addresses_without_domain(true);
try {
invalid_filename_character_re = new Regex("[/\\0]");
@ -52,6 +37,17 @@ public void init() {
}
}
public GMime.FormatOptions get_format_options() {
return GMime.FormatOptions.get_default().clone();
}
public GMime.ParserOptions get_parser_options() {
return GMime.ParserOptions.get_default().clone();
}
public string? get_charset() {
return UTF8_CHARSET;
}
internal bool is_utf_8(string charset) {
string up = charset.up();