From 8af34015e9aa94e5ae4ae9e9fd2c4d155453ac94 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 10 Jun 2020 07:05:05 +0000 Subject: [PATCH] imap: LIST shows "INBOX" in all caps While selecting a mailbox is done case-insensitively, "INBOX" is special for the LIST command, according to RFC 3501 6.3.8: > The special name INBOX is included in the output from LIST, if > INBOX is supported by this server for this user and if the > uppercase string "INBOX" matches the interpreted reference and > mailbox name arguments with wildcards as described above. The > criteria for omitting INBOX is whether SELECT INBOX will > return failure; it is not relevant whether the user's real > INBOX resides on this or some other server. Thus, the existing news.public-inbox.org convention of naming newsgroups starting with "inbox." needs to be special-cased to not confuse clients. While we're at it, do not create ".0" for dummy newsgroups if they're selected, either. --- lib/PublicInbox/IMAP.pm | 8 ++++---- lib/PublicInbox/IMAPD.pm | 4 +++- t/imapd.t | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/PublicInbox/IMAP.pm b/lib/PublicInbox/IMAP.pm index d3f4874e..b3c449b0 100644 --- a/lib/PublicInbox/IMAP.pm +++ b/lib/PublicInbox/IMAP.pm @@ -254,13 +254,14 @@ sub cmd_done ($$) { sub ensure_ranges_exist ($$$) { my ($imapd, $ibx, $max) = @_; + defined(my $mb_top = $ibx->{newsgroup}) or return; my $mailboxes = $imapd->{mailboxes}; - my $mb_top = $ibx->{newsgroup}; my @created; for (my $i = int($max/UID_BLOCK); $i >= 0; --$i) { my $sub_mailbox = "$mb_top.$i"; last if exists $mailboxes->{$sub_mailbox}; $mailboxes->{$sub_mailbox} = $ibx; + $sub_mailbox =~ s/\Ainbox\./INBOX./i; # more familiar to users push @created, $sub_mailbox; } return unless @created; @@ -693,9 +694,8 @@ sub cmd_list ($$$$) { # request for hierarchy delimiter $l = [ qq[* LIST (\\Noselect) "." ""\r\n] ]; } elsif ($refname ne '' || $wildcard ne '*') { - $wildcard = lc $wildcard; - $wildcard =~ s!([^a-z0-9_])!$patmap{$1} // "\Q$1"!eg; - $l = [ grep(/ \Q$refname\E$wildcard\r\n\z/s, @$l) ]; + $wildcard =~ s!([^a-z0-9_])!$patmap{$1} // "\Q$1"!egi; + $l = [ grep(/ \Q$refname\E$wildcard\r\n\z/is, @$l) ]; } \(join('', @$l, "$tag OK List done\r\n")); } diff --git a/lib/PublicInbox/IMAPD.pm b/lib/PublicInbox/IMAPD.pm index 261d7560..186ec7b0 100644 --- a/lib/PublicInbox/IMAPD.pm +++ b/lib/PublicInbox/IMAPD.pm @@ -71,7 +71,9 @@ sub imapd_refresh_finalize { $imapd->{inboxlist} = [ map { my $no = $mailboxes->{$_} == $dummy ? '' : 'No'; - qq[* LIST (\\Has${no}Children) "." $_\r\n] + my $u = $_; # capitalize "INBOX" for user-familiarity + $u =~ s/\Ainbox(\.|\z)/INBOX$1/i; + qq[* LIST (\\Has${no}Children) "." $u\r\n] } sort { # shortest names first, alphabetically if lengths match length($a) == length($b) ? diff --git a/t/imapd.t b/t/imapd.t index 233be9f2..c691e1a9 100644 --- a/t/imapd.t +++ b/t/imapd.t @@ -101,13 +101,13 @@ like($raw[0], qr/\A\*\x20STATUS\x20inbox\.i1\.$first_range\x20 like($raw[1], qr/\A\S+ OK /, 'finished status response'); my @orig_list = @raw = $mic->list; -like($raw[0], qr/^\* LIST \(.*?\) "\." inbox/, +like($raw[0], qr/^\* LIST \(.*?\) "\." INBOX/, 'got an inbox'); like($raw[-1], qr/^\S+ OK /, 'response ended with OK'); is(scalar(@raw), scalar(@V) + 4, 'default LIST response'); @raw = $mic->list('', 'inbox.i1'); is(scalar(@raw), 2, 'limited LIST response'); -like($raw[0], qr/^\* LIST \(.*?\) "\." inbox/, +like($raw[0], qr/^\* LIST \(.*?\) "\." INBOX/, 'got an inbox.i1'); like($raw[-1], qr/^\S+ OK /, 'response ended with OK'); -- 2.44.0