diff --git a/src/client/accounts/accounts-manager.vala b/src/client/accounts/accounts-manager.vala index f6d5749f..7b7c101c 100644 --- a/src/client/accounts/accounts-manager.vala +++ b/src/client/accounts/accounts-manager.vala @@ -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 + ); } } diff --git a/src/client/composer/composer-email-entry.vala b/src/client/composer/composer-email-entry.vala index b5e87314..cd4202b0 100644 --- a/src/client/composer/composer-email-entry.vala +++ b/src/client/composer/composer-email-entry.vala @@ -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; + } } } diff --git a/src/engine/imap-db/imap-db-message-row.vala b/src/engine/imap-db/imap-db-message-row.vala index 36a4699b..4744a957 100644 --- a/src/engine/imap-db/imap-db-message-row.vala +++ b/src/engine/imap-db/imap-db-message-row.vala @@ -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) + ); } + } - diff --git a/src/engine/imap/message/imap-message-data.vala b/src/engine/imap/message/imap-message-data.vala index e4d423ff..07c10f3c 100644 --- a/src/engine/imap/message/imap-message-data.vala +++ b/src/engine/imap/message/imap-message-data.vala @@ -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); } } - diff --git a/src/engine/rfc822/rfc822-mailbox-address.vala b/src/engine/rfc822/rfc822-mailbox-address.vala index 6d994136..5f5ad87e 100644 --- a/src/engine/rfc822/rfc822-mailbox-address.vala +++ b/src/engine/rfc822/rfc822-mailbox-address.vala @@ -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) { diff --git a/src/engine/rfc822/rfc822-mailbox-addresses.vala b/src/engine/rfc822/rfc822-mailbox-addresses.vala index 4afd096d..41886acb 100644 --- a/src/engine/rfc822/rfc822-mailbox-addresses.vala +++ b/src/engine/rfc822/rfc822-mailbox-addresses.vala @@ -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++) { diff --git a/src/engine/rfc822/rfc822-message-data.vala b/src/engine/rfc822/rfc822-message-data.vala index d4a3aa0a..ba1a9708 100644 --- a/src/engine/rfc822/rfc822-message-data.vala +++ b/src/engine/rfc822/rfc822-message-data.vala @@ -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 { - 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; } diff --git a/src/engine/rfc822/rfc822-message.vala b/src/engine/rfc822/rfc822-message.vala index 4ef341c8..ff63f58e 100644 --- a/src/engine/rfc822/rfc822-message.vala +++ b/src/engine/rfc822/rfc822-message.vala @@ -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 get_sub_messages() { + public Gee.List get_sub_messages() + throws RFC822Error { Gee.List messages = new Gee.ArrayList(); find_sub_messages(messages, message.get_mime_part()); return messages; } - private void find_sub_messages(Gee.List messages, GMime.Object root) { + private void find_sub_messages(Gee.List 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"); } diff --git a/test/engine/rfc822-message-test.vala b/test/engine/rfc822-message-test.vala index cc3f0a40..979fcccb 100644 --- a/test/engine/rfc822-message-test.vala +++ b/test/engine/rfc822-message-test.vala @@ -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), diff --git a/test/integration/smtp/client-session.vala b/test/integration/smtp/client-session.vala index b6c0c801..33ee89d0 100644 --- a/test/integration/smtp/client-session.vala +++ b/test/integration/smtp/client-session.vala @@ -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)