From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id A7C851FBF7 for ; Wed, 10 Jun 2020 07:06:26 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 40/82] imap: case-insensitive mailbox name comparisons Date: Wed, 10 Jun 2020 07:04:37 +0000 Message-Id: <20200610070519.18252-41-e@yhbt.net> In-Reply-To: <20200610070519.18252-1-e@yhbt.net> References: <20200610070519.18252-1-e@yhbt.net> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: IMAP RFC 3501 stipulates case-insensitive comparisons, and so does RFC 977 (NNTP). However, INN (nnrpd) uses case-sensitive comparisons, so we've always used case-sensitive comparisons for NNTP to match nnrpd behavior. Unfortunately, some IMAP clients insist on sending "INBOX" with caps, which causes problems for us. Since NNTP group names are typically all lowercase anyways, just force all comparisons to lowercase for IMAP and warn admins if uppercase-containing newsgroups won't be accessible over IMAP. This ensures our existing -nntpd behavior remains unchanged while being compatible with the expectations of real-world IMAP clients. --- lib/PublicInbox/IMAPD.pm | 7 +++++++ t/imap.t | 7 ++++++- t/imapd.t | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/PublicInbox/IMAPD.pm b/lib/PublicInbox/IMAPD.pm index a3a2598661b..966879d8ea5 100644 --- a/lib/PublicInbox/IMAPD.pm +++ b/lib/PublicInbox/IMAPD.pm @@ -25,6 +25,13 @@ sub refresh_inboxlist ($) { my ($self) = @_; my @names = map { $_->{newsgroup} } @{delete $self->{grouplist}}; my %ns; # "\Noselect \HasChildren" + + if (my @uc = grep(/[A-Z]/, @names)) { + warn "Uppercase not allowed for IMAP newsgroup(s):\n", + map { "\t$_\n" } @uc; + my %uc = map { $_ => 1 } @uc; + @names = grep { !$uc{$_} } @names; + } for (@names) { my $up = $_; while ($up =~ s/\.[^\.]+\z//) { diff --git a/t/imap.t b/t/imap.t index 451b6596bf9..aa262a19632 100644 --- a/t/imap.t +++ b/t/imap.t @@ -8,9 +8,14 @@ use PublicInbox::IMAP; use PublicInbox::IMAPD; { # make sure we get '%' globbing right - my @n = map { { newsgroup => $_ } } (qw(x.y.z x.z.y)); + my @w; + local $SIG{__WARN__} = sub { push @w, @_ }; + my @n = map { { newsgroup => $_ } } (qw(x.y.z x.z.y IGNORE.THIS)); my $self = { imapd => { grouplist => \@n } }; PublicInbox::IMAPD::refresh_inboxlist($self->{imapd}); + is(scalar(@w), 1, 'got a warning for upper-case'); + like($w[0], qr/IGNORE\.THIS/, 'warned about upper-case'); + my $res = PublicInbox::IMAP::cmd_list($self, 'tag', 'x', '%'); is(scalar($$res =~ tr/\n/\n/), 2, 'only one result'); like($$res, qr/ x\r\ntag OK/, 'saw expected'); diff --git a/t/imapd.t b/t/imapd.t index 2c4315dec30..a5324f78da0 100644 --- a/t/imapd.t +++ b/t/imapd.t @@ -88,7 +88,7 @@ like($e, qr/\bNO\b/, 'got a NO on EXAMINE for non-existent'); ok(!$mic->select('foo') && ($e = $@), 'EXAMINE non-existent'); like($e, qr/\bNO\b/, 'got a NO on EXAMINE for non-existent'); ok($mic->select('inbox.i1'), 'SELECT succeeds'); -ok($mic->examine('inbox.i1'), 'EXAMINE succeeds'); +ok($mic->examine('INBOX.i1'), 'EXAMINE succeeds'); my @raw = $mic->status('inbox.i1', qw(Messages uidnext uidvalidity)); is(scalar(@raw), 2, 'got status response'); like($raw[0], qr/\A\*\x20STATUS\x20inbox\.i1\x20