diff --git a/cassandane/data/mime/utf8-domain.bin b/cassandane/data/mime/utf8-domain.bin new file mode 100644 index 0000000000..e85afc1052 --- /dev/null +++ b/cassandane/data/mime/utf8-domain.bin @@ -0,0 +1,9 @@ +From: J. Besteiro +To: to@local +Subject: test +Date: Mon, 13 Apr 2020 15:34:03 +0200 +MIME-Version: 1.0 +Content-Type: text/plain;charset=us-ascii +Content-Transfer-Encoding: 7bit + +hello diff --git a/cassandane/tiny-tests/JMAPEmail/email_get_utf8_domain b/cassandane/tiny-tests/JMAPEmail/email_get_utf8_domain new file mode 100644 index 0000000000..9fc7915f40 --- /dev/null +++ b/cassandane/tiny-tests/JMAPEmail/email_get_utf8_domain @@ -0,0 +1,38 @@ +#!perl +use Cassandane::Tiny; + +sub test_email_get_utf8_domain + :min_version_3_9 :needs_component_jmap :NoMunge8Bit :RFC2047_UTF8 +{ + my ($self) = @_; + my $jmap = $self->{jmap}; + my $imap = $self->{store}->get_client(); + + open(my $F, "data/mime/utf8-domain.bin") || die $!; + $imap->append('INBOX', $F) || die $@; + close($F); + + my $res = $jmap->CallMethods([ + ['Email/query', { }, 'R1'], + ['Email/get', { + '#ids' => { + resultOf => 'R1', + name => 'Email/query', + path => '/ids' + }, + properties => ['from', 'subject'], + }, 'R2'], + ]); + +use utf8; + $self->assert_deep_equals([{ + name => 'J. Besteiro', + email => 'jb@julián.example.com', + }], $res->[1][1]{list}[0]{from}); +no utf8; + + $imap->select('INBOX'); + $res = $imap->fetch('1:*', 'ENVELOPE'); + $self->assert_str_equals('"J. Besteiro" ', + $res->{1}{envelope}{From}); +} diff --git a/changes/next/parseaddr_utf8_domain b/changes/next/parseaddr_utf8_domain new file mode 100644 index 0000000000..2738e7b0ff --- /dev/null +++ b/changes/next/parseaddr_utf8_domain @@ -0,0 +1,11 @@ +Description: +Updates the email address parser to preserve non-ASCII characters +in the domain part. + + +Config changes: +None. + + +Upgrade instructions: +Reconstruct mailboxes with the -G option to force reparsing email headers. diff --git a/cunit/parseaddr.testc b/cunit/parseaddr.testc index 8ef0b3d5c3..8321ab3b39 100644 --- a/cunit/parseaddr.testc +++ b/cunit/parseaddr.testc @@ -367,6 +367,20 @@ static void test_rfc2047_text(void) parseaddr_free(a); } +static void test_utf8_domain(void) +{ + struct address *a; + + a = NULL; + parseaddr_list("J. Besteiro ", &a); + CU_ASSERT_PTR_NOT_NULL_FATAL(a); + CU_ASSERT_STRING_EQUAL(a->name, "J. Besteiro"); + CU_ASSERT_STRING_EQUAL(a->mailbox, "jb"); + CU_ASSERT_STRING_EQUAL(a->domain, "julián.example.com"); + CU_ASSERT_PTR_NULL(a->next); + + parseaddr_free(a); +} static void test_group(void) { diff --git a/imap/mailbox.h b/imap/mailbox.h index 471987efd6..2e9ea05cce 100644 --- a/imap/mailbox.h +++ b/imap/mailbox.h @@ -79,7 +79,7 @@ * changes to backend_version() in backend.c. */ #define MAILBOX_MINOR_VERSION 19 -#define MAILBOX_CACHE_MINOR_VERSION 11 +#define MAILBOX_CACHE_MINOR_VERSION 12 #define FNAME_HEADER "/cyrus.header" #define FNAME_INDEX "/cyrus.index" diff --git a/lib/parseaddr.c b/lib/parseaddr.c index 4483c1de2f..d20ebcb6bc 100644 --- a/lib/parseaddr.c +++ b/lib/parseaddr.c @@ -301,7 +301,7 @@ static int parseaddr_phrase(char **inp, char **phrasep, const char *specials) */ static int parseaddr_domain(char **inp, char **domainp, char **commentp, int *invalid) { - int c; + u_char c; char *src = *inp; char *dst; char *cdst; @@ -314,7 +314,7 @@ static int parseaddr_domain(char **inp, char **domainp, char **commentp, int *in for (;;) { c = *src++; - if (Uisalnum(c) || c == '-' || c == '[' || c == ']' || c == ':') { + if (Uisalnum(c) || c == '-' || c == '[' || c == ']' || c == ':' || c > 127) { *dst++ = c; if (commentp) *commentp = 0; }