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 B56751FBC4 for ; Wed, 10 Jun 2020 07:05:20 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 08/82] imap: implement STATUS command Date: Wed, 10 Jun 2020 07:04:05 +0000 Message-Id: <20200610070519.18252-9-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: I'm not sure if there's much use for this command, but it's part of RFC3501 and works read-only. --- lib/PublicInbox/IMAP.pm | 29 +++++++++++++++++++++++++++++ t/imapd.t | 5 +++++ 2 files changed, 34 insertions(+) diff --git a/lib/PublicInbox/IMAP.pm b/lib/PublicInbox/IMAP.pm index c8592dc0329..a2d59e5cccc 100644 --- a/lib/PublicInbox/IMAP.pm +++ b/lib/PublicInbox/IMAP.pm @@ -306,6 +306,35 @@ sub uid_fetch_m { # long_response 1; } +sub cmd_status ($$$;@) { + my ($self, $tag, $mailbox, @items) = @_; + my $ibx = $self->{imapd}->{groups}->{$mailbox} or + return "$tag NO Mailbox doesn't exist: $mailbox\r\n"; + return "$tag BAD no items\r\n" if !scalar(@items); + ($items[0] !~ s/\A\(//s || $items[-1] !~ s/\)\z//s) and + return "$tag BAD invalid args\r\n"; + + my $mm = $ibx->mm; + my ($max, @it); + for my $it (@items) { + $it = uc($it); + push @it, $it; + if ($it =~ /\A(?:MESSAGES|UNSEEN|RECENT)\z/) { + push(@it, ($max //= $mm->max // 0)); + } elsif ($it eq 'UIDNEXT') { + push(@it, ($max //= $mm->max // 0) + 1); + } elsif ($it eq 'UIDVALIDITY') { + push(@it, $mm->created_at // + return("$tag BAD UIDVALIDITY\r\n")); + } else { + return "$tag BAD invalid item\r\n"; + } + } + return "$tag BAD no items\r\n" if !@it; + "* STATUS $mailbox (".join(' ', @it).")\r\n" . + "$tag OK Status complete\r\n"; +} + sub cmd_uid_fetch ($$$;@) { my ($self, $tag, $range, @want) = @_; my $ibx = $self->{ibx} or return "$tag BAD No mailbox selected\r\n"; diff --git a/t/imapd.t b/t/imapd.t index b0caa8f1779..7512bb90050 100644 --- a/t/imapd.t +++ b/t/imapd.t @@ -81,6 +81,11 @@ 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'); +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 + \(MESSAGES\x20\d+\x20UIDNEXT\x20\d+\x20UIDVALIDITY\x20\d+\)\r\n/sx); +like($raw[1], qr/\A\S+ OK /, 'finished status response'); my $ret = $mic->search('all') or BAIL_OUT "SEARCH FAIL $@"; is_deeply($ret, [ 1 ], 'search all works');