$capa .= ' COMPRESS=DEFLATE';
} else {
if (!($self->{sock} // $self)->can('accept_SSL') &&
- $self->{imapd}->{accept_tls}) {
+ $self->{imapd}->{ssl_ctx_opt}) {
$capa .= ' STARTTLS';
}
$capa .= ' AUTH=ANONYMOUS';
sub auth_challenge_ok ($) {
my ($self) = @_;
my $tag = delete($self->{-login_tag}) or return;
+ $self->{anon} = 1;
login_success($self, $tag);
}
"$idle_tag OK Idle done\r\n";
}
-sub ensure_slices_exist ($$$) {
- my ($imapd, $ibx, $max) = @_;
- defined(my $mb_top = $ibx->{newsgroup}) or return;
+sub ensure_slices_exist ($$) {
+ my ($imapd, $ibx) = @_;
+ my $mb_top = $ibx->{newsgroup} // return;
my $mailboxes = $imapd->{mailboxes};
- my @created;
- for (my $i = int($max/UID_SLICE); $i >= 0; --$i) {
+ my $list = $imapd->{mailboxlist}; # may be undef, just autoviv + noop
+ for (my $i = int($ibx->art_max/UID_SLICE); $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;
+ push @$list, qq[* LIST (\\HasNoChildren) "." $sub_mailbox\r\n]
}
- return unless @created;
- my $l = $imapd->{mailboxlist} or return;
- push @$l, map { qq[* LIST (\\HasNoChildren) "." $_\r\n] } @created;
}
sub inbox_lookup ($$;$) {
my $uid_end = $uid_base + UID_SLICE;
$exists = $over->imap_exists($uid_base, $uid_end);
}
- ensure_slices_exist($self->{imapd}, $ibx, $over->max);
+ delete $ibx->{-art_max};
+ ensure_slices_exist($self->{imapd}, $ibx);
} else {
if ($examine) {
$self->{uid_base} = $uid_base;
}
# if "INBOX.foo.bar" is selected and "INBOX.foo.bar.0",
# check for new UID ranges (e.g. "INBOX.foo.bar.1")
- if (my $z = $self->{imapd}->{mailboxes}->{"$mailbox.0"}) {
- ensure_slices_exist($self->{imapd}, $z,
- $z->over(1)->max);
+ if (my $ibx = $self->{imapd}->{mailboxes}->{"$mailbox.0"}) {
+ delete $ibx->{-art_max};
+ ensure_slices_exist($self->{imapd}, $ibx);
}
}
($ibx, $exists, $uidmax + 1, $uid_base);
if (!defined($v)) {
'NIL';
} elsif ($v =~ /[{"\r\n%*\\\[]/) { # literal string
+ utf8::encode($v);
'{' . length($v) . "}\r\n" . $v;
} else { # quoted string
+ utf8::encode($v);
qq{"$v"}
}
}
join('', @hold);
}
-sub requeue_once ($) {
- my ($self) = @_;
- # COMPRESS users all share the same DEFLATE context.
- # Flush it here to ensure clients don't see
- # each other's data
- $self->zflush;
-
- # no recursion, schedule another call ASAP,
- # but only after all pending writes are done.
- # autovivify wbuf:
- my $new_size = push(@{$self->{wbuf}}, \&long_step);
-
- # wbuf may be populated by $cb, no need to rearm if so:
- $self->requeue if $new_size == 1;
-}
-
sub fetch_run_ops {
my ($self, $smsg, $bref, $ops, $partial) = @_;
my $uid = $smsg->{num};
$self->msg_more(")\r\n");
}
+sub requeue { # overrides PublicInbox::DS::requeue
+ my ($self) = @_;
+ if ($self->{anon}) { # AUTH=ANONYMOUS gets high priority
+ $self->SUPER::requeue;
+ } else { # low priority
+ push(@{$self->{imapd}->{-authed_q}}, $self) == 1 and
+ PublicInbox::DS::requeue($self->{imapd});
+ }
+}
+
sub fetch_blob_cb { # called by git->cat_async via ibx_async_cat
my ($bref, $oid, $type, $size, $fetch_arg) = @_;
my ($self, undef, $msgs, $range_info, $ops, $partial) = @$fetch_arg;
# it's possible to have TOCTOU if an admin runs
# public-inbox-(edit|purge), just move onto the next message
warn "E: $smsg->{blob} missing in $ibx->{inboxdir}\n";
- return requeue_once($self);
+ return $self->requeue_once;
} else {
$smsg->{blob} eq $oid or die "BUG: $smsg->{blob} != $oid";
}
my $pre;
- if (!$self->{wbuf} && (my $nxt = $msgs->[0])) {
- $pre = ibx_async_prefetch($ibx, $nxt->{blob},
+ ($self->{anon} && !$self->{wbuf} && $msgs->[0]) and
+ $pre = ibx_async_prefetch($ibx, $msgs->[0]->{blob},
\&fetch_blob_cb, $fetch_arg);
- }
fetch_run_ops($self, $smsg, $bref, $ops, $partial);
- $pre ? $self->zflush : requeue_once($self);
+ $pre ? $self->dflush : $self->requeue_once;
}
sub emit_rfc822 {
# stabilize partial order for consistency and ease-of-debugging:
if (scalar keys %partial) {
$need |= NEED_BLOB;
- $r[2] = [ map { [ $_, @{$partial{$_}} ] } sort keys %partial ];
+ @{$r[2]} = map { [ $_, @{$partial{$_}} ] } sort keys %partial;
}
push @op, $OP_EML_NEW if ($need & (EML_HDR|EML_BDY));
# r[1] = [ $key1, $cb1, $key2, $cb2, ... ]
use sort 'stable'; # makes output more consistent
- $r[1] = [ map { ($_->[2], $_->[1]) } sort { $a->[0] <=> $b->[0] } @op ];
+ @{$r[1]} = map { ($_->[2], $_->[1]) } sort { $a->[0] <=> $b->[0] } @op;
@r;
}
my $range_info = range_step($self, \$range_csv);
return "$tag $range_info\r\n" if !ref($range_info);
uo2m_hibernate($self) if $cb == \&fetch_blob; # slow, save RAM
- long_response($self, $cb, $tag, [], $range_info, $ops, $partial);
+ $self->long_response($cb, $tag, [], $range_info, $ops, $partial);
}
sub cmd_fetch ($$$$;@) {
my $range_info = range_step($self, \$range_csv);
return "$tag $range_info\r\n" if !ref($range_info);
uo2m_hibernate($self) if $cb == \&fetch_blob; # slow, save RAM
- long_response($self, $cb, $tag, [], $range_info, $ops, $partial);
+ $self->long_response($cb, $tag, [], $range_info, $ops, $partial);
}
sub msn_convert ($$) {
my ($sql, $range_info) = delete @$q{qw(sql range_info)};
if (!scalar(keys %$q)) { # overview.sqlite3
$self->msg_more('* SEARCH');
- long_response($self, \&search_uid_range,
+ $self->long_response(\&search_uid_range,
$tag, $sql, $range_info, $want_msn);
} elsif ($q = $q->{xap}) {
my $srch = $self->{ibx}->isrch or
my $err = $@;
if ($err && $self->{sock}) {
$l =~ s/\r?\n//s;
- err($self, 'error from: %s (%s)', $l, $err);
+ warn("error from: $l ($err)\n");
$tag //= '*';
- $res = "$tag BAD program fault - command not performed\r\n";
- }
- return 0 unless defined $res;
- $self->write($res);
-}
-
-sub long_step {
- my ($self) = @_;
- # wbuf is unset or empty, here; {long} may add to it
- my ($fd, $cb, $t0, @args) = @{$self->{long_cb}};
- my $more = eval { $cb->($self, @args) };
- if ($@ || !$self->{sock}) { # something bad happened...
- delete $self->{long_cb};
- my $elapsed = now() - $t0;
- if ($@) {
- err($self,
- "%s during long response[$fd] - %0.6f",
- $@, $elapsed);
- }
- out($self, " deferred[$fd] aborted - %0.6f", $elapsed);
- $self->close;
- } elsif ($more) { # $self->{wbuf}:
- # control passed to ibx_async_cat if $more == \undef
- requeue_once($self) if !ref($more);
- } else { # all done!
- delete $self->{long_cb};
- my $elapsed = now() - $t0;
- my $fd = fileno($self->{sock});
- out($self, " deferred[$fd] done - %0.6f", $elapsed);
- my $wbuf = $self->{wbuf}; # do NOT autovivify
-
- $self->requeue unless $wbuf && @$wbuf;
+ $res = \"$tag BAD program fault - command not performed\r\n";
}
-}
-
-sub err ($$;@) {
- my ($self, $fmt, @args) = @_;
- printf { $self->{imapd}->{err} } $fmt."\n", @args;
+ defined($res) ? $self->write($res) : 0;
}
sub out ($$;@) {
printf { $self->{imapd}->{out} } $fmt."\n", @args;
}
-sub long_response ($$;@) {
- my ($self, $cb, @args) = @_; # cb returns true if more, false if done
-
- my $sock = $self->{sock} or return;
- # make sure we disable reading during a long response,
- # clients should not be sending us stuff and making us do more
- # work while we are stream a response to them
- $self->{long_cb} = [ fileno($sock), $cb, now(), @args ];
- long_step($self); # kick off!
- undef;
-}
-
# callback used by PublicInbox::DS for any (e)poll (in/out/hup/err)
sub event_step {
my ($self) = @_;
-
+ local $SIG{__WARN__} = $self->{imapd}->{warn_cb};
return unless $self->flush_write && $self->{sock} && !$self->{long_cb};
# only read more requests if we've drained the write buffer,
$self->requeue unless $pending;
}
-sub compressed { undef }
-
-sub zflush {} # overridden by IMAPdeflate
-
# RFC 4978
sub cmd_compress ($$$) {
my ($self, $tag, $alg) = @_;
# CRIME made TLS compression obsolete
# return "$tag NO [COMPRESSIONACTIVE]\r\n" if $self->tls_compressed;
- PublicInbox::IMAPdeflate->enable($self, $tag);
+ PublicInbox::IMAPdeflate->enable($self) or return
+ \"$tag BAD failed to activate compression\r\n";
+ PublicInbox::DS::write($self, \"$tag OK DEFLATE active\r\n");
$self->requeue;
undef
}
sub cmd_starttls ($$) {
my ($self, $tag) = @_;
- my $sock = $self->{sock} or return;
- if ($sock->can('stop_SSL') || $self->compressed) {
+ (($self->{sock} // return)->can('stop_SSL') || $self->compressed) and
return "$tag BAD TLS or compression already enabled\r\n";
- }
- my $opt = $self->{imapd}->{accept_tls} or
+ $self->{imapd}->{ssl_ctx_opt} or
return "$tag BAD can not initiate TLS negotiation\r\n";
$self->write(\"$tag OK begin TLS negotiation now\r\n");
- $self->{sock} = IO::Socket::SSL->start_SSL($sock, %$opt);
+ PublicInbox::TLS::start($self->{sock}, $self->{imapd});
$self->requeue if PublicInbox::DS::accept_tls_step($self);
undef;
}
sub logged_in { 0 }
+package PublicInbox::IMAPdeflate;
+use PublicInbox::DSdeflate;
+our @ISA = qw(PublicInbox::DSdeflate PublicInbox::IMAP);
+
1;