]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/IMAP.pm
imap: LIST shows "INBOX" in all caps
[public-inbox.git] / lib / PublicInbox / IMAP.pm
index 13f415cf8d64766a30075befebb36f5e5a7f5f64..b3c449b0712778b571ab2cbbb0d5e24b72e53c65 100644 (file)
@@ -16,7 +16,7 @@
 package PublicInbox::IMAP;
 use strict;
 use base qw(PublicInbox::DS);
-use fields qw(imapd logged_in ibx long_cb -login_tag
+use fields qw(imapd ibx long_cb -login_tag
        uid_min -idle_tag -idle_max);
 use PublicInbox::Eml;
 use PublicInbox::EmlContentFoo qw(parse_content_disposition);
@@ -27,6 +27,7 @@ use Text::ParseWords qw(parse_line);
 use Errno qw(EAGAIN);
 use Time::Local qw(timegm);
 use POSIX qw(strftime);
+use Hash::Util qw(unlock_hash); # dependency of fields for perl 5.10+, anyways
 
 my $Address;
 for my $mod (qw(Email::Address::XS Mail::Address)) {
@@ -52,7 +53,7 @@ my %FETCH_NEED = (
        'BODY[]' => [ NEED_BLOB, \&emit_rfc822 ],
        'RFC822.HEADER' => [ NEED_EML, \&emit_rfc822_header ],
        'RFC822.TEXT' => [ NEED_EML, \&emit_rfc822_text ],
-       'RFC822.SIZE' => [ NEED_BLOB, \&emit_rfc822_size ],
+       'RFC822.SIZE' => [ NEED_SMSG, \&emit_rfc822_size ],
        RFC822 => [ NEED_BLOB, \&emit_rfc822 ],
        BODY => [ NEED_EML, \&emit_body ],
        BODYSTRUCTURE => [ NEED_EML, \&emit_bodystructure ],
@@ -91,7 +92,8 @@ sub greet ($) {
 
 sub new ($$$) {
        my ($class, $sock, $imapd) = @_;
-       my $self = fields::new($class);
+       my $self = fields::new('PublicInbox::IMAP_preauth');
+       unlock_hash(%$self);
        my $ev = EPOLLIN;
        my $wbuf;
        if ($sock->can('accept_SSL') && !$sock->accept_SSL) {
@@ -110,13 +112,15 @@ sub new ($$$) {
        $self;
 }
 
+sub logged_in { 1 }
+
 sub capa ($) {
        my ($self) = @_;
 
        # dovecot advertises IDLE pre-login; perhaps because some clients
        # depend on it, so we'll do the same
        my $capa = 'CAPABILITY IMAP4rev1 IDLE';
-       if ($self->{logged_in}) {
+       if ($self->logged_in) {
                $capa .= ' COMPRESS=DEFLATE';
        } else {
                if (!($self->{sock} // $self)->can('accept_SSL') &&
@@ -129,7 +133,7 @@ sub capa ($) {
 
 sub login_success ($$) {
        my ($self, $tag) = @_;
-       $self->{logged_in} = 1;
+       bless $self, 'PublicInbox::IMAP';
        my $capa = capa($self);
        "$tag OK [$capa] Logged in\r\n";
 }
@@ -154,7 +158,7 @@ sub cmd_close ($$) {
 
 sub cmd_logout ($$) {
        my ($self, $tag) = @_;
-       delete @$self{qw(logged_in -idle_tag)};
+       delete $self->{-idle_tag};
        $self->write(\"* BYE logging out\r\n$tag OK Logout done\r\n");
        $self->shutdn; # PublicInbox::DS::shutdn
        undef;
@@ -250,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;
@@ -496,11 +501,12 @@ sub emit_rfc822 {
        $self->msg_more($$bref);
 }
 
-# Mail::IMAPClient::message_string cares about this by default
-# (->Ignoresizeerrors attribute)
+# Mail::IMAPClient::message_string cares about this by default,
+# (->Ignoresizeerrors attribute).  Admins are encouraged to
+# --reindex for IMAP support, anyways.
 sub emit_rfc822_size {
-       my ($self, $k, undef, $bref) = @_;
-       $self->msg_more(' RFC822.SIZE ' . length($$bref));
+       my ($self, $k, $smsg) = @_;
+       $self->msg_more(' RFC822.SIZE ' . $smsg->{bytes});
 }
 
 sub emit_internaldate {
@@ -688,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"));
 }
@@ -879,7 +884,7 @@ sub fetch_compile ($) {
        @r;
 }
 
-sub cmd_uid_fetch ($$$;@) {
+sub cmd_uid_fetch ($$$$;@) {
        my ($self, $tag, $range_csv, @want) = @_;
        my $ibx = $self->{ibx} or return "$tag BAD No mailbox selected\r\n";
        my ($cb, $ops, $partial) = fetch_compile(\@want);
@@ -1238,4 +1243,9 @@ no warnings 'once';
 *cmd_select = \&cmd_examine;
 *cmd_fetch = \&cmd_uid_fetch;
 
+package PublicInbox::IMAP_preauth;
+our @ISA = qw(PublicInbox::IMAP);
+
+sub logged_in { 0 }
+
 1;