Geary.RFC822: Ensure various data constructors throw errors

Rather than just silently ignoring error conditions, throw RFC822
errors instead.
This commit is contained in:
Michael Gratton 2020-05-05 21:48:11 +10:00 committed by Michael James Gratton
parent 97e0093e29
commit e0c1fb8a80
10 changed files with 160 additions and 144 deletions

View file

@ -1274,10 +1274,15 @@ public class Accounts.AccountConfigLegacy : AccountConfig, GLib.Object {
ALTERNATE_EMAILS_KEY
);
foreach (string alt_email in alt_email_list) {
Geary.RFC822.MailboxAddresses mailboxes =
new Geary.RFC822.MailboxAddresses.from_rfc822_string(alt_email);
foreach (Geary.RFC822.MailboxAddress mailbox in mailboxes.get_all()) {
info.append_sender(mailbox);
try {
var mailboxes = new Geary.RFC822.MailboxAddresses.from_rfc822_string(alt_email);
foreach (Geary.RFC822.MailboxAddress mailbox in mailboxes.get_all()) {
info.append_sender(mailbox);
}
} catch (Geary.RFC822Error error) {
throw new ConfigError.SYNTAX(
"Invalid alternate email: %s", error.message
);
}
}

View file

@ -81,9 +81,14 @@ public class Composer.EmailEntry : Gtk.Entry {
this._addresses = new Geary.RFC822.MailboxAddresses();
this.is_valid = false;
} else {
this._addresses =
new Geary.RFC822.MailboxAddresses.from_rfc822_string(text);
this.is_valid = true;
try {
this._addresses =
new Geary.RFC822.MailboxAddresses.from_rfc822_string(text);
this.is_valid = true;
} catch (Geary.RFC822Error err) {
this._addresses = new Geary.RFC822.MailboxAddresses();
this.is_valid = false;
}
}
}

View file

@ -272,20 +272,22 @@ private class Geary.ImapDB.MessageRow {
return (addrs == null || addrs.size == 0) ? null : addrs.to_rfc822_string();
}
private RFC822.MailboxAddress? unflatten_address(string? str) {
RFC822.MailboxAddress? addr = null;
if (str != null) {
try {
addr = new RFC822.MailboxAddress.from_rfc822_string(str);
} catch (RFC822Error e) {
// oh well
}
}
return addr;
private RFC822.MailboxAddress? unflatten_address(string? str)
throws RFC822Error {
return (
String.is_empty_or_whitespace(str)
? null
: new RFC822.MailboxAddress.from_rfc822_string(str)
);
}
private RFC822.MailboxAddresses? unflatten_addresses(string? str) {
return String.is_empty(str) ? null : new RFC822.MailboxAddresses.from_rfc822_string(str);
private RFC822.MailboxAddresses? unflatten_addresses(string? str)
throws RFC822Error {
return (
String.is_empty_or_whitespace(str)
? null
: new RFC822.MailboxAddresses.from_rfc822_string(str)
);
}
}

View file

@ -26,8 +26,13 @@ public class Geary.Imap.RFC822Size : Geary.RFC822.Size, Geary.Imap.MessageData {
}
public class Geary.Imap.RFC822Header : Geary.RFC822.Header, Geary.Imap.MessageData {
public RFC822Header(Memory.Buffer buffer) {
base (buffer);
public RFC822Header(Memory.Buffer buffer)
throws ImapError {
try {
base(buffer);
} catch (RFC822Error error) {
throw new ImapError.INVALID(error.message);
}
}
}
@ -42,4 +47,3 @@ public class Geary.Imap.RFC822Full : Geary.RFC822.Full, Geary.Imap.MessageData {
base (buffer);
}
}

View file

@ -233,21 +233,27 @@ public class Geary.RFC822.MailboxAddress :
Geary.RFC822.get_parser_options(),
rfc822
);
if (addrlist == null)
return;
int length = addrlist.length();
for (int ctr = 0; ctr < length; ctr++) {
GMime.InternetAddress? addr = addrlist.get_address(ctr);
// TODO: Handle group lists
GMime.InternetAddressMailbox? mbox_addr = addr as GMime.InternetAddressMailbox;
if (mbox_addr != null) {
this.gmime(mbox_addr);
return;
}
if (addrlist == null) {
throw new RFC822Error.INVALID(
"Not a RFC822 mailbox address: %s", rfc822
);
}
throw new RFC822Error.INVALID("Could not parse RFC822 address: %s", rfc822);
if (addrlist.length() != 1) {
throw new RFC822Error.INVALID(
"Not a single RFC822 mailbox address: %s", rfc822
);
}
GMime.InternetAddress? addr = addrlist.get_address(0);
// TODO: Handle group lists
var mbox_addr = addr as GMime.InternetAddressMailbox;
if (mbox_addr == null) {
throw new RFC822Error.INVALID(
"Group lists not currently supported: %s", rfc822
);
}
this.gmime(mbox_addr);
}
public MailboxAddress.gmime(GMime.InternetAddressMailbox mailbox) {

View file

@ -78,13 +78,12 @@ public class Geary.RFC822.MailboxAddresses :
this.addrs.add(addr);
}
public MailboxAddresses.from_rfc822_string(string rfc822) {
GMime.InternetAddressList addrlist = GMime.InternetAddressList.parse(
Geary.RFC822.get_parser_options(),
rfc822
);
if (addrlist == null)
return;
public MailboxAddresses.from_rfc822_string(string rfc822)
throws RFC822Error {
var addrlist = GMime.InternetAddressList.parse(null, rfc822);
if (addrlist == null) {
throw new RFC822Error.INVALID("Not a RFC822 mailbox address list");
}
int length = addrlist.length();
for (int ctr = 0; ctr < length; ctr++) {

View file

@ -169,17 +169,15 @@ public class Geary.RFC822.MessageIDList : Geary.MessageData.AbstractMessageData,
public class Geary.RFC822.Date : Geary.RFC822.MessageData, Geary.MessageData.AbstractMessageData,
Gee.Hashable<Geary.RFC822.Date> {
public string? original { get; private set; }
public string original { get; private set; }
public DateTime value { get; private set; }
public Date(string rfc822) throws ImapError {
DateTime? value = GMime.utils_header_decode_date(rfc822);
if (value == null) {
throw new ImapError.PARSE_ERROR(
"Unable to parse \"%s\": Outside supported range", rfc822
);
public Date(string rfc822) throws RFC822Error {
var date = GMime.utils_header_decode_date(rfc822);
if (date == null) {
throw new RFC822Error.INVALID("Not ISO-8601 date: %s", rfc822);
}
this.value = value;
this.value = date;
this.original = rfc822;
}
@ -302,42 +300,38 @@ public class Geary.RFC822.Header : Geary.MessageData.BlockMessageData, Geary.RFC
private GMime.Message? message = null;
private string[]? names = null;
public Header(Memory.Buffer buffer) {
public Header(Memory.Buffer buffer) throws RFC822Error {
base("RFC822.Header", buffer);
}
private unowned GMime.HeaderList get_headers() throws RFC822Error {
if (message != null)
return message.get_header_list();
GMime.Parser parser = new GMime.Parser.with_stream(Utils.create_stream_mem(buffer));
var parser = new GMime.Parser.with_stream(
Utils.create_stream_mem(buffer)
);
parser.set_respect_content_length(false);
parser.set_format(MESSAGE);
message = parser.construct_message(Geary.RFC822.get_parser_options());
if (message == null)
this.message = parser.construct_message(null);
if (this.message == null) {
throw new RFC822Error.INVALID("Unable to parse RFC 822 headers");
return message.get_header_list();
}
}
public string? get_header(string name) throws RFC822Error {
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(string name) {
string? value = null;
var header = this.message.get_header_list().get_header(name);
if (header != null) {
value = header.get_value();
}
return value;
}
public string[] get_header_names() throws RFC822Error {
public string[] get_header_names() {
if (this.names == null) {
this.names = new string[0];
GMime.HeaderList headers = get_headers();
for (int i = 0; i < headers.get_count(); i++) {
names += headers.get_header_at(i).get_name();
GMime.HeaderList headers = this.message.get_header_list();
var names = new string[headers.get_count()];
for (int i = 0; i < names.length; i++) {
names[i] = headers.get_header_at(i).get_name();
}
this.names = names;
}
return this.names;
}

View file

@ -6,6 +6,7 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
/**
* An RFC-822 style email message.
*
@ -99,16 +100,19 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
stock_from_gmime();
}
public Message.from_gmime_message(GMime.Message message) {
public Message.from_gmime_message(GMime.Message message)
throws RFC822Error {
this.message = message;
stock_from_gmime();
}
public Message.from_buffer(Memory.Buffer full_email) throws RFC822Error {
public Message.from_buffer(Memory.Buffer full_email)
throws RFC822Error {
this(new Geary.RFC822.Full(full_email));
}
public Message.from_parts(Header header, Text body) throws RFC822Error {
public Message.from_parts(Header header, Text body)
throws RFC822Error {
GMime.StreamCat stream_cat = new GMime.StreamCat();
stream_cat.add_source(new GMime.StreamMem.with_buffer(header.buffer.get_bytes().get_data()));
stream_cat.add_source(new GMime.StreamMem.with_buffer(body.buffer.get_bytes().get_data()));
@ -126,9 +130,11 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
public async Message.from_composed_email(Geary.ComposedEmail email,
string? message_id,
GLib.Cancellable? cancellable) {
GLib.Cancellable? cancellable)
throws RFC822Error {
this.message = new GMime.Message(true);
//
// Required headers
this.from = email.from;
@ -802,7 +808,8 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
* disabled by passing false in include_sub_messages). Note that values
* that come out of this function are persisted.
*/
public string? get_searchable_body(bool include_sub_messages = true) {
public string? get_searchable_body(bool include_sub_messages = true)
throws RFC822Error {
string? body = null;
bool html = false;
try {
@ -880,81 +887,70 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
return attachments;
}
private void stock_from_gmime() {
GMime.HeaderList headers = this.message.get_header_list();
private void stock_from_gmime() throws RFC822Error {
GMime.HeaderList headers = this.message.headers;
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;
string value = header.get_value();
switch (header.get_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":
this.sender = new MailboxAddress.from_rfc822_string(value);
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 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":
this.date = new Date(value);
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(Geary.RFC822.get_parser_options(), value);
break;
case "x-mailer":
this.mailer = GMime.utils_header_decode_text(null, value);
break;
default:
// do nothing
break;
default:
// Ignore anything else not
break;
}
};
}
}
private MailboxAddresses append_address(MailboxAddresses? existing,
string header_value) {
string header_value)
throws RFC822Error {
MailboxAddresses addresses = new MailboxAddresses.from_rfc822_string(header_value);
if (existing != null) {
addresses = existing.append(addresses);
@ -1063,13 +1059,16 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
}
#endif
public Gee.List<Geary.RFC822.Message> get_sub_messages() {
public Gee.List<Geary.RFC822.Message> get_sub_messages()
throws RFC822Error {
Gee.List<Geary.RFC822.Message> messages = new Gee.ArrayList<Geary.RFC822.Message>();
find_sub_messages(messages, message.get_mime_part());
return messages;
}
private void find_sub_messages(Gee.List<Geary.RFC822.Message> messages, GMime.Object root) {
private void find_sub_messages(Gee.List<Message> messages,
GMime.Object root)
throws RFC822Error {
// If this is a multipart container, check each of its children.
GMime.Multipart? multipart = root as GMime.Multipart;
if (multipart != null) {
@ -1084,7 +1083,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
if (messagepart != null) {
GMime.Message sub_message = messagepart.get_message();
if (sub_message != null) {
messages.add(new Geary.RFC822.Message.from_gmime_message(sub_message));
messages.add(new Message.from_gmime_message(sub_message));
} else {
warning("Corrupt message, possibly bug 769697");
}

View file

@ -371,7 +371,8 @@ This is the second line.
assert_true(out_buffer.size > (buffer.size+buffer2.size), "Expected sizeable message");
}
private async Geary.RFC822.Message message_from_composed_email(Geary.ComposedEmail composed) {
private async Message message_from_composed_email(ComposedEmail composed)
throws RFC822Error {
return yield new Geary.RFC822.Message.from_composed_email(
composed,
GMime.utils_generate_message_id(composed.from.get(0).domain),

View file

@ -118,7 +118,8 @@ class Integration.Smtp.ClientSession : TestCase {
}
private async Geary.RFC822.Message new_message(Geary.RFC822.MailboxAddress from,
Geary.RFC822.MailboxAddress to) {
Geary.RFC822.MailboxAddress to)
throws Geary.RFC822Error {
Geary.ComposedEmail composed = new Geary.ComposedEmail(
new GLib.DateTime.now_local(),
new Geary.RFC822.MailboxAddresses.single(from)