Ensure mailbox addresses escaped correctly when formatted as RFC 822.
* src/engine/rfc822/rfc822-mailbox-address.vala (MailboxAddress): Escape and encode name and mailbox (local-part) when serialising as a RFC 822 string. Provide a means to get a RFC 822 version of the address only for SMTP. Add unit tests. * src/engine/smtp/smtp-request.vala (MailRequest): Use proper RFC 822 formatted version of mailbox addresses.
This commit is contained in:
parent
71e0e6835e
commit
8810e95753
3 changed files with 111 additions and 20 deletions
|
|
@ -9,6 +9,11 @@
|
|||
/**
|
||||
* An immutable representation of an RFC 822 mailbox address.
|
||||
*
|
||||
* The properties of this class such as {@link name} and {@link
|
||||
* address} are stores decoded UTF-8, thus they must be re-encoded
|
||||
* using methods such as {@link to_rfc822_string} before being re-used
|
||||
* in a message envelope.
|
||||
*
|
||||
* See [[https://tools.ietf.org/html/rfc5322#section-3.4]]
|
||||
*/
|
||||
public class Geary.RFC822.MailboxAddress : Geary.MessageData.SearchableMessageData,
|
||||
|
|
@ -31,40 +36,52 @@ public class Geary.RFC822.MailboxAddress : Geary.MessageData.SearchableMessageDa
|
|||
|
||||
|
||||
internal delegate string ListToStringDelegate(MailboxAddress address);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The optional user-friendly name associated with the {@link MailboxAddress}.
|
||||
* The optional human-readable part of the mailbox address.
|
||||
*
|
||||
* For "Dirk Gently <dirk@example.com>", this would be "Dirk Gently".
|
||||
*
|
||||
* The returned value has been decoded into UTF-8.
|
||||
*/
|
||||
public string? name { get; private set; }
|
||||
|
||||
|
||||
/**
|
||||
* The routing of the message (optional, obsolete).
|
||||
*
|
||||
* The returned value has been decoded into UTF-8.
|
||||
*/
|
||||
public string? source_route { get; private set; }
|
||||
|
||||
|
||||
/**
|
||||
* The mailbox (local-part) portion of the {@link MailboxAddress}.
|
||||
* The mailbox (local-part) portion of the mailbox's address.
|
||||
*
|
||||
* For "Dirk Gently <dirk@example.com>", this would be "dirk".
|
||||
*
|
||||
* The returned value has been decoded into UTF-8.
|
||||
*/
|
||||
public string mailbox { get; private set; }
|
||||
|
||||
|
||||
/**
|
||||
* The domain portion of the {@link MailboxAddress}.
|
||||
* The domain portion of the mailbox's address.
|
||||
*
|
||||
* For "Dirk Gently <dirk@example.com>", this would be "example.com".
|
||||
*
|
||||
* The returned value has been decoded into UTF-8.
|
||||
*/
|
||||
public string domain { get; private set; }
|
||||
|
||||
|
||||
/**
|
||||
* The address specification of the {@link MailboxAddress}.
|
||||
* The complete address part of the mailbox address.
|
||||
*
|
||||
* For "Dirk Gently <dirk@example.com>", this would be "dirk@example.com".
|
||||
*
|
||||
* The returned value has been decoded into UTF-8.
|
||||
*/
|
||||
public string address { get; private set; }
|
||||
|
||||
|
||||
public MailboxAddress(string? name, string address) {
|
||||
this.name = name;
|
||||
this.address = address;
|
||||
|
|
@ -293,14 +310,38 @@ public class Geary.RFC822.MailboxAddress : Geary.MessageData.SearchableMessageDa
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the address suitable for insertion into an RFC822 message.
|
||||
* Returns the complete mailbox address, armoured for RFC 822 use.
|
||||
*
|
||||
* @return the RFC822 quoted form of the full address.
|
||||
* This method is similar to {@link to_full_display}, but only
|
||||
* checks for a distinct address (per Postel's Law) and not for
|
||||
* any spoofing, and does not strip extra white space or
|
||||
* non-printing characters.
|
||||
*
|
||||
* @return the RFC822 encoded form of the full address.
|
||||
*/
|
||||
public string to_rfc822_string() {
|
||||
return has_distinct_name()
|
||||
? "%s <%s>".printf(GMime.utils_quote_string(this.name), this.address)
|
||||
: this.address;
|
||||
? "%s <%s>".printf(
|
||||
GMime.utils_header_encode_phrase(this.name),
|
||||
to_rfc822_address()
|
||||
)
|
||||
: to_rfc822_address();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address part only, armoured for RFC 822 use.
|
||||
*
|
||||
* @return the RFC822 encoded form of the address, without angle
|
||||
* brackets.
|
||||
*/
|
||||
public string to_rfc822_address() {
|
||||
return "%s@%s".printf(
|
||||
// XXX utils_quote_string won't quote if spaces or quotes
|
||||
// present, so need to do that manually
|
||||
GMime.utils_quote_string(GMime.utils_header_encode_text(this.mailbox)),
|
||||
// XXX Need to punycode international domains.
|
||||
this.domain
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ public class Geary.Smtp.EhloRequest : Geary.Smtp.Request {
|
|||
|
||||
public class Geary.Smtp.MailRequest : Geary.Smtp.Request {
|
||||
public MailRequest(Geary.RFC822.MailboxAddress from) {
|
||||
base (Command.MAIL, { "from:%s".printf(from.to_address_display("<", ">")) });
|
||||
base (Command.MAIL, { "from:<%s>".printf(from.to_rfc822_address()) });
|
||||
}
|
||||
|
||||
public MailRequest.plain(string addr) {
|
||||
|
|
|
|||
|
|
@ -10,10 +10,13 @@ class Geary.RFC822.MailboxAddressTest : Gee.TestCase {
|
|||
public MailboxAddressTest() {
|
||||
base("Geary.RFC822.MailboxAddressTest");
|
||||
add_test("is_valid_address", is_valid_address);
|
||||
add_test("unescaped_constructor", unescaped_constructor);
|
||||
add_test("is_spoofed", is_spoofed);
|
||||
add_test("has_distinct_name", has_distinct_name);
|
||||
add_test("to_full_display", to_full_display);
|
||||
add_test("to_short_display", to_short_display);
|
||||
// latter depends on the former, so test that first
|
||||
add_test("to_rfc822_address", to_rfc822_address);
|
||||
add_test("to_rfc822_string", to_rfc822_string);
|
||||
}
|
||||
|
||||
|
|
@ -35,6 +38,34 @@ class Geary.RFC822.MailboxAddressTest : Gee.TestCase {
|
|||
assert(Geary.RFC822.MailboxAddress.is_valid_address("") == false);
|
||||
}
|
||||
|
||||
public void unescaped_constructor() {
|
||||
MailboxAddress addr1 = new MailboxAddress("test1", "test2@example.com");
|
||||
assert(addr1.name == "test1");
|
||||
assert(addr1.address == "test2@example.com");
|
||||
assert(addr1.mailbox == "test2");
|
||||
assert(addr1.domain == "example.com");
|
||||
|
||||
MailboxAddress addr2 = new MailboxAddress(null, "test1@test2@example.com");
|
||||
assert(addr2.address == "test1@test2@example.com");
|
||||
assert(addr2.mailbox == "test1@test2");
|
||||
assert(addr2.domain == "example.com");
|
||||
|
||||
MailboxAddress addr3 = new MailboxAddress(null, "©@example.com");
|
||||
assert(addr3.address == "©@example.com");
|
||||
assert(addr3.mailbox == "©");
|
||||
assert(addr3.domain == "example.com");
|
||||
|
||||
MailboxAddress addr4 = new MailboxAddress(null, "😸@example.com");
|
||||
assert(addr4.address == "😸@example.com");
|
||||
assert(addr4.mailbox == "😸");
|
||||
assert(addr4.domain == "example.com");
|
||||
|
||||
MailboxAddress addr5 = new MailboxAddress(null, "example.com");
|
||||
assert(addr5.address == "example.com");
|
||||
assert(addr5.mailbox == "");
|
||||
assert(addr5.domain == "");
|
||||
}
|
||||
|
||||
public void is_spoofed() {
|
||||
assert(new MailboxAddress(null, "example@example.com").is_spoofed() == false);
|
||||
assert(new MailboxAddress("", "example@example.com").is_spoofed() == false);
|
||||
|
|
@ -91,6 +122,23 @@ class Geary.RFC822.MailboxAddressTest : Gee.TestCase {
|
|||
"example@example@example.com");
|
||||
}
|
||||
|
||||
public void to_rfc822_address() {
|
||||
assert(new MailboxAddress(null, "example@example.com").to_rfc822_address() ==
|
||||
"example@example.com");
|
||||
//assert(new MailboxAddress(null, "test test@example.com").to_rfc822_address() ==
|
||||
// "\"test test\"@example.com");
|
||||
//assert(new MailboxAddress(null, "test\" test@example.com").to_rfc822_address() ==
|
||||
// "\"test\" test\"@example.com");
|
||||
//assert(new MailboxAddress(null, "test\"test@example.com").to_rfc822_address() ==
|
||||
// "\"test\"test\"@example.com");
|
||||
assert(new MailboxAddress(null, "test@test@example.com").to_rfc822_address() ==
|
||||
"\"test@test\"@example.com");
|
||||
assert(new MailboxAddress(null, "©@example.com").to_rfc822_address() ==
|
||||
"\"=?iso-8859-1?b?qQ==?=\"@example.com");
|
||||
assert(new MailboxAddress(null, "😸@example.com").to_rfc822_address() ==
|
||||
"\"=?UTF-8?b?8J+YuA==?=\"@example.com");
|
||||
}
|
||||
|
||||
public void to_rfc822_string() {
|
||||
assert(new MailboxAddress("", "example@example.com").to_rfc822_string() ==
|
||||
"example@example.com");
|
||||
|
|
@ -102,14 +150,16 @@ class Geary.RFC822.MailboxAddressTest : Gee.TestCase {
|
|||
"test test <example@example.com>");
|
||||
assert(new MailboxAddress("example@example.com", "example@example.com").to_rfc822_string() ==
|
||||
"example@example.com");
|
||||
// Technically, per
|
||||
// https://tools.ietf.org/html/rfc5322#appendix-A.1.2 this
|
||||
// would be fine as just "test? <example@example.com>",
|
||||
// i.e. without the name being quoted, but I guess GMime is
|
||||
// just being conservative here?
|
||||
assert(new MailboxAddress("test?", "example@example.com").to_rfc822_string() ==
|
||||
"\"test?\" <example@example.com>");
|
||||
"test? <example@example.com>");
|
||||
assert(new MailboxAddress("test@test", "example@example.com").to_rfc822_string() ==
|
||||
"\"test@test\" <example@example.com>");
|
||||
assert(new MailboxAddress(";", "example@example.com").to_rfc822_string() ==
|
||||
"\";\" <example@example.com>");
|
||||
assert(new MailboxAddress("©", "example@example.com").to_rfc822_string() ==
|
||||
"=?iso-8859-1?b?qQ==?= <example@example.com>");
|
||||
assert(new MailboxAddress("😸", "example@example.com").to_rfc822_string() ==
|
||||
"=?UTF-8?b?8J+YuA==?= <example@example.com>");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue