From 1c22eb4ebdb0b7e86a32b32b15800c08f589da2a Mon Sep 17 00:00:00 2001 From: Michael Gratton Date: Mon, 17 Aug 2020 13:57:15 +1000 Subject: [PATCH] Geary.RFC822.MailboxAddress: Handle empty mailbox and domain parts better If an address does not have an `@`, or if the IMAP constructor is called with empty mailbox or domain parts, then these properties will be empty. This is not uncommon, especially on UNIX hosts where system accounts send email. Ensure this is handled correctly in the constructors and are round-tripped correctly by `to_rfc822_address` and hence `to_rfc822_string`. --- src/engine/rfc822/rfc822-mailbox-address.vala | 57 ++++-- .../rfc822/rfc822-mailbox-address-test.vala | 177 +++++++++++------- 2 files changed, 155 insertions(+), 79 deletions(-) diff --git a/src/engine/rfc822/rfc822-mailbox-address.vala b/src/engine/rfc822/rfc822-mailbox-address.vala index 80cb0112..1e23fca4 100644 --- a/src/engine/rfc822/rfc822-mailbox-address.vala +++ b/src/engine/rfc822/rfc822-mailbox-address.vala @@ -233,7 +233,18 @@ public class Geary.RFC822.MailboxAddress : this.source_route = source_route; this.mailbox = decode_address_part(mailbox); this.domain = domain; - this.address = "%s@%s".printf(mailbox, domain); + + bool empty_mailbox = String.is_empty_or_whitespace(mailbox); + bool empty_domain = String.is_empty_or_whitespace(domain); + if (!empty_mailbox && !empty_domain) { + this.address = "%s@%s".printf(mailbox, domain); + } else if (empty_mailbox) { + this.address = domain; + } else if (empty_domain) { + this.address = mailbox; + } else { + this.address = ""; + } } public MailboxAddress.from_rfc822_string(string rfc822) throws Error { @@ -266,9 +277,11 @@ public class Geary.RFC822.MailboxAddress : // GMime strips source route for us, so the address part // should only ever contain a single '@' string? name = mailbox.get_name(); - if (name != "") { - this.name = decode_name(name); - } + this.name = ( + !String.is_empty_or_whitespace(name) + ? decode_name(name) + : null + ); string address = mailbox.get_addr(); int atsign = Ascii.last_index_of(address, '@'); @@ -286,7 +299,7 @@ public class Geary.RFC822.MailboxAddress : } else { this.mailbox = ""; this.domain = ""; - this.address = address; + this.address = decode_address_part(address); } } @@ -502,15 +515,33 @@ public class Geary.RFC822.MailboxAddress : // GMime.utils_header_encode_text will use MIME encoding, // which is disallowed in mailboxes by RFC 2074 §5. So quote // manually. - string local_part = this.mailbox; - if (local_part_needs_quoting(local_part)) { - local_part = quote_string(local_part); + var address = ""; + if (this.mailbox != "") { + address = this.mailbox; + if (local_part_needs_quoting(address)) { + address = quote_string(address); + } } - return "%s@%s".printf( - local_part, - // XXX Need to punycode international domains. - this.domain - ); + if (this.domain != "") { + address = "%s@%s".printf( + address, + // XXX Need to punycode international domains. + this.domain + ); + } + if (address == "") { + // Both mailbox and domain are empty, i.e. there was no + // '@' symbol in the address, so just assume the address + // is a mailbox since this is not uncommon practice on + // UNIX systems where mail is sent from a local account, + // and it supports a greater range of characters than the + // domain component + address = this.address; + if (local_part_needs_quoting(address)) { + address = quote_string(address); + } + } + return address; } /** diff --git a/test/engine/rfc822/rfc822-mailbox-address-test.vala b/test/engine/rfc822/rfc822-mailbox-address-test.vala index 8718636d..fdef5b0b 100644 --- a/test/engine/rfc822/rfc822-mailbox-address-test.vala +++ b/test/engine/rfc822/rfc822-mailbox-address-test.vala @@ -9,6 +9,7 @@ class Geary.RFC822.MailboxAddressTest : TestCase { public MailboxAddressTest() { base("Geary.RFC822.MailboxAddressTest"); + add_test("imap_address", imap_address); add_test("is_valid_address", is_valid_address); add_test("unescaped_constructor", unescaped_constructor); add_test("from_rfc822_string_encoded", from_rfc822_string_encoded); @@ -24,6 +25,25 @@ class Geary.RFC822.MailboxAddressTest : TestCase { add_test("equal_to", equal_to); } + public void imap_address() throws GLib.Error { + assert_equal( + new MailboxAddress.imap(null, null, "test", "example.com").address, + "test@example.com" + ); + assert_equal( + new MailboxAddress.imap(null, null, "test", "").address, + "test" + ); + assert_equal( + new MailboxAddress.imap(null, null, "", "example.com").address, + "example.com" + ); + assert_equal( + new MailboxAddress.imap(null, null, "", "").address, + "" + ); + } + public void is_valid_address() throws GLib.Error { assert(Geary.RFC822.MailboxAddress.is_valid_address("john@dep.aol.museum") == true); assert(Geary.RFC822.MailboxAddress.is_valid_address("test@example.com") == true); @@ -73,84 +93,93 @@ class Geary.RFC822.MailboxAddressTest : TestCase { } public void from_rfc822_string_encoded() throws GLib.Error { - try { - MailboxAddress addr = new MailboxAddress.from_rfc822_string("test@example.com"); - assert(addr.name == null); - assert(addr.mailbox == "test"); - assert(addr.domain == "example.com"); + var encoded = "test@example.com"; + var addr = new MailboxAddress.from_rfc822_string(encoded); + assert_null(addr.name, encoded); + assert_equal(addr.mailbox, "test", encoded); + assert_equal(addr.domain, "example.com", encoded); - addr = new MailboxAddress.from_rfc822_string("\"test\"@example.com"); - assert(addr.name == null); - assert(addr.address == "test@example.com"); - assert(addr.mailbox == "test"); - assert(addr.domain == "example.com"); + encoded = "\"test\"@example.com"; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_null(addr.name, encoded); + assert_equal(addr.mailbox, "test", encoded); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test@example.com", encoded); - addr = new MailboxAddress.from_rfc822_string("=?UTF-8?b?dGVzdA==?=@example.com"); - assert(addr.name == null); - assert(addr.address == "test@example.com"); - assert(addr.mailbox == "test"); - assert(addr.domain == "example.com"); + encoded = "=?UTF-8?b?dGVzdA==?=@example.com"; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_null(addr.name, encoded); + assert_equal(addr.mailbox, "test", encoded); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test@example.com", encoded); - addr = new MailboxAddress.from_rfc822_string("\"=?UTF-8?b?dGVzdA==?=\"@example.com"); - assert(addr.name == null); - assert(addr.address == "test@example.com"); - assert(addr.mailbox == "test"); - assert(addr.domain == "example.com"); + encoded = "\"=?UTF-8?b?dGVzdA==?=\"@example.com"; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_null(addr.name, encoded); + assert_equal(addr.mailbox, "test", encoded); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test@example.com", encoded); - addr = new MailboxAddress.from_rfc822_string(""); - assert(addr.name == null); - assert(addr.address == "test@example.com"); - assert(addr.mailbox == "test"); - assert(addr.domain == "example.com"); + encoded = ""; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_null(addr.name, encoded); + assert_equal(addr.mailbox, "test"); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test@example.com", encoded); - addr = new MailboxAddress.from_rfc822_string("<\"test\"@example.com>"); - assert(addr.name == null); - assert(addr.address == "test@example.com"); - assert(addr.mailbox == "test"); - assert(addr.domain == "example.com"); + encoded = "<\"test\"@example.com>"; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_null(addr.name, encoded); + assert_equal(addr.mailbox, "test", encoded); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test@example.com", encoded); - addr = new MailboxAddress.from_rfc822_string("Test 1 "); - assert(addr.name == "Test 1"); - assert(addr.address == "test2@example.com"); - assert(addr.mailbox == "test2"); - assert(addr.domain == "example.com"); + encoded = "Test 1 "; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_equal(addr.name, "Test 1", encoded); + assert_equal(addr.mailbox, "test2", encoded); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test2@example.com", encoded); - addr = new MailboxAddress.from_rfc822_string("\"Test 1\" "); - assert(addr.name == "Test 1"); - assert(addr.address == "test2@example.com"); - assert(addr.mailbox == "test2"); - assert(addr.domain == "example.com"); + encoded = "\"Test 1\" "; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_equal(addr.name, "Test 1", encoded); + assert_equal(addr.mailbox, "test2", encoded); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test2@example.com", encoded); - addr = new MailboxAddress.from_rfc822_string("Test 1 <\"test2\"@example.com>"); - assert(addr.name == "Test 1"); - assert(addr.address == "test2@example.com"); - assert(addr.mailbox == "test2"); - assert(addr.domain == "example.com"); + encoded = "Test 1 <\"test2\"@example.com>"; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_equal(addr.name, "Test 1", encoded); + assert_equal(addr.mailbox, "test2", encoded); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test2@example.com", encoded); - addr = new MailboxAddress.from_rfc822_string("=?UTF-8?b?VGVzdCAx?= "); - assert(addr.name == "Test 1"); - assert(addr.address == "test2@example.com"); - assert(addr.mailbox == "test2"); - assert(addr.domain == "example.com"); + encoded = "=?UTF-8?b?VGVzdCAx?= "; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_equal(addr.name, "Test 1", encoded); + assert_equal(addr.mailbox, "test2", encoded); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test2@example.com", encoded); - addr = new MailboxAddress.from_rfc822_string("\"=?UTF-8?b?VGVzdCAx?=\" "); - assert(addr.name == "Test 1"); - assert(addr.address == "test2@example.com"); - assert(addr.mailbox == "test2"); - assert(addr.domain == "example.com"); + encoded = "\"=?UTF-8?b?VGVzdCAx?=\" "; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_equal(addr.name, "Test 1", encoded); + assert_equal(addr.mailbox, "test2", encoded); + assert_equal(addr.domain, "example.com", encoded); + assert_equal(addr.address, "test2@example.com", encoded); - // Courtesy Mailsploit https://www.mailsploit.com - addr = new MailboxAddress.from_rfc822_string("\"=?utf-8?b?dGVzdCIgPHBvdHVzQHdoaXRlaG91c2UuZ292Pg==?==?utf-8?Q?=00=0A?=\" "); - assert(addr.name == "test ?"); - assert(addr.address == "demo@mailsploit.com"); + // Courtesy Mailsploit https://www.mailsploit.com + encoded = "\"=?utf-8?b?dGVzdCIgPHBvdHVzQHdoaXRlaG91c2UuZ292Pg==?==?utf-8?Q?=00=0A?=\" "; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_equal(addr.name, "test ?", encoded); + assert_equal(addr.address, "demo@mailsploit.com", encoded); - // Courtesy Mailsploit https://www.mailsploit.com - addr = new MailboxAddress.from_rfc822_string("\"=?utf-8?Q?=42=45=47=49=4E=20=2F=20=28=7C=29=7C=3C=7C=3E=7C=40=7C=2C=7C=3B=7C=3A=7C=5C=7C=22=7C=2F=7C=5B=7C=5D=7C=3F=7C=2E=7C=3D=20=2F=20=00=20=50=41=53=53=45=44=20=4E=55=4C=4C=20=42=59=54=45=20=2F=20=0D=0A=20=50=41=53=53=45=44=20=43=52=4C=46=20=2F=20?==?utf-8?b?RU5E=?=\""); - assert(addr.name == null); - assert(addr.address == "BEGIN / (|)|<|>|@|,|;|:|\\|\"|/|[|]|?|.|= / ? PASSED NULL BYTE / \r\n PASSED CRLF / END"); - } catch (Error err) { - assert_not_reached(); - } + // Courtesy Mailsploit https://www.mailsploit.com + encoded = "\"=?utf-8?Q?=42=45=47=49=4E=20=2F=20=28=7C=29=7C=3C=7C=3E=7C=40=7C=2C=7C=3B=7C=3A=7C=5C=7C=22=7C=2F=7C=5B=7C=5D=7C=3F=7C=2E=7C=3D=20=2F=20=00=20=50=41=53=53=45=44=20=4E=55=4C=4C=20=42=59=54=45=20=2F=20=0D=0A=20=50=41=53=53=45=44=20=43=52=4C=46=20=2F=20?==?utf-8?b?RU5E=?=\""; + addr = new MailboxAddress.from_rfc822_string(encoded); + assert_equal(addr.name, null, encoded); + assert_equal(addr.address, "BEGIN / (|)|<|>|@|,|;|:|\\|\"|/|[|]|?|.|= / ? PASSED NULL BYTE / \r\n PASSED CRLF / END", encoded); } public void prepare_header_text_part() throws GLib.Error { @@ -286,6 +315,22 @@ class Geary.RFC822.MailboxAddressTest : TestCase { "😸@example.com" ); + assert_equal( + new MailboxAddress(null, "example1").to_rfc822_address(), + "example1" + ); + assert_equal( + new MailboxAddress.imap(null, null, "example2", "").to_rfc822_address(), + "example2" + ); + assert_equal( + new MailboxAddress.imap(null, null, "", "example3").to_rfc822_address(), + "@example3" + ); + assert_equal( + new MailboxAddress.imap(null, null, "", "").to_rfc822_address(), + "" + ); } public void to_rfc822_string() throws GLib.Error {