From 735c861de4d662f5bfe4fddff9fbfa8bc5a503c1 Mon Sep 17 00:00:00 2001
From: Guilhem Moulin <guilhem@fripost.org>
Date: Fri, 24 May 2019 23:52:37 +0200
Subject: libinterimap: astring is 1*ASTRING-CHAR / string.

Not 1*ATOM-CHAR / string.  Also accept LIST responses mailbox names
containing '%', '*', or ']'.

From RFC 3501:

        astring         = 1*ASTRING-CHAR / string
        ASTRING-CHAR    = ATOM-CHAR / resp-specials
        list            = "LIST" SP mailbox SP list-mailbox
        list-mailbox    = 1*list-char / string
        list-char       = ATOM-CHAR / list-wildcards / resp-specials
        list-wildcards  = "%" / "*"
        resp-specials   = "]"
---
 Changelog                 |  3 +++
 lib/Net/IMAP/InterIMAP.pm | 13 ++++++++++---
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/Changelog b/Changelog
index 9cce062..587dc9b 100644
--- a/Changelog
+++ b/Changelog
@@ -3,6 +3,9 @@ interimap (0.5) upstream;
  - libinterimap: bugfix: hierarchy delimiters in LIST responses were
    returned as an escaped quoted special, like "\\", not as a single
    character (backslash in this case).
+ - libinterimap: the parser choked on responses with non-quoted/literal
+   astring containing ']' characters.  And LIST responses with
+   non-quoted/literal list-mailbox names '%', '*' or ']' characters.
  - libinterimap: quote() the empty string as "" instead of a 0-length
    literal.  (This saves 3 bytes + one round-trip on servers not
    supporting non-synchronizing literals, and 4 bytes otherwise.)
diff --git a/lib/Net/IMAP/InterIMAP.pm b/lib/Net/IMAP/InterIMAP.pm
index 26c3712..86f08a9 100644
--- a/lib/Net/IMAP/InterIMAP.pm
+++ b/lib/Net/IMAP/InterIMAP.pm
@@ -40,9 +40,10 @@ BEGIN {
 }
 
 
-# Regexes for RFC 3501's 'ATOM-CHAR', 'ASTRING-CHAR' and 'TEXT-CHAR'.
+# Regexes for RFC 3501's 'ATOM-CHAR', 'ASTRING-CHAR', 'list-char' and 'TEXT-CHAR'.
 my $RE_ATOM_CHAR    = qr/[\x21\x23\x24\x26\x27\x2B-\x5B\x5E-\x7A\x7C-\x7E]/;
 my $RE_ASTRING_CHAR = qr/[\x21\x23\x24\x26\x27\x2B-\x5B\x5D-\x7A\x7C-\x7E]/;
+my $RE_LIST_CHAR    = qr/[\x21\x23-\x27\x2A\x2B-\x5B\x5D-\x7A\x7C-\x7E]/;
 my $RE_TEXT_CHAR    = qr/[\x01-\x09\x0B\x0C\x0E-\x7F]/;
 
 my $RE_SSL_PROTO = qr/(?:SSLv[23]|TLSv1|TLSv1\.[0-3])/;
@@ -2192,7 +2193,13 @@ sub _nstring($$) {
 # Parse and consume an RFC 3501 astring (1*ASTRING-CHAR / string).
 sub _astring($$) {
     my ($self, $stream) = @_;
-    return $$stream =~ s/\A($RE_ATOM_CHAR+)// ? $1 : $self->_string($stream);
+    return $$stream =~ s/\A$RE_ASTRING_CHAR+//p ? ${^MATCH} : $self->_string($stream);
+}
+
+# Parse and consume an RFC 3501 list-mailbox (1*list-char / string).
+sub _list_mailbox($$) {
+    my ($self, $stream) = @_;
+    return $$stream =~ s/\A$RE_LIST_CHAR+//p ? ${^MATCH} : $self->_string($stream);
 }
 
 # Parse and consume an RFC 3501 string (quoted / literal).
@@ -2364,7 +2371,7 @@ sub _resp($$;&$$) {
         elsif (s/\ALIST \((\\?$RE_ATOM_CHAR+(?: \\?$RE_ATOM_CHAR+)*)?\) ("(?:\\[\x22\x5C]|[\x01-\x09\x0B\x0C\x0E-\x21\x23-\x5B\x5D-\x7F])"|NIL) //) {
             my ($delim, $attrs) = ($2, $1);
             my @attrs = defined $attrs ? split(/ /, $attrs) : ();
-            my $mailbox = $self->_astring(\$_);
+            my $mailbox = $self->_list_mailbox(\$_);
             $self->panic($_) unless $_ eq '';
             $mailbox = 'INBOX' if uc $mailbox eq 'INBOX'; # INBOX is case-insensitive
             undef $delim if uc $delim eq 'NIL';
-- 
cgit v1.2.3