-# Copyright (C) 2021 all contributors <meta@public-inbox.org>
+# Copyright (C) all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
# common reader code for IMAP and NNTP (and maybe JMAP)
my $sec = uri_section($uri);
# knobs directly for Mail::IMAPClient->new
- for my $k (qw(Starttls Debug Compress Ignoresizeerrors)) {
+ for my $k (qw(Starttls Debug Compress)) {
my $bool = cfg_bool($cfg, "imap.$k", $$uri) // next;
$mic_common->{$sec}->{$k} = $bool;
}
mic_for($self, $uri, $mic_common, $lei) //
die "Unable to continue\n";
next unless $self->isa('PublicInbox::NetWriter');
+ next if $self->{-skip_creat};
my $dst = $orig_uri->mailbox // next;
next if $mic->exists($dst); # already exists
$mic->create($dst) or die "CREATE $dst failed <$orig_uri>: $@";
my ($self, $uri, $r_uidval, $mic) = @_;
return (undef, undef, $r_uidval) unless $self->{incremental};
my ($itrk, $l_uid, $l_uidval);
- if (defined(my $lms = $self->{-lms_ro})) { # LeiMailSync or 0
+ if (defined(my $lms = $self->{-lms_rw})) { # LeiMailSync or 0
$uri->uidvalidity($r_uidval) if defined $r_uidval;
if ($mic) {
my $auth = $mic->Authmechanism // '';
# may be overridden in NetWriter or Watch
sub folder_select { $_[0]->{each_old} ? 'select' : 'examine' }
+sub _imap_fetch_bodies ($$$$) {
+ my ($self, $mic, $uri, $uids) = @_;
+ my $req = $mic->imap4rev1 ? 'BODY.PEEK[]' : 'RFC822.PEEK';
+ my $key = $req;
+ $key =~ s/\.PEEK//;
+ my $sec = uri_section($uri);
+ my $mbx = $uri->mailbox;
+ my $bs = $self->{cfg_opt}->{$sec}->{batch_size} // 1;
+ my ($last_uid, $err);
+ my $use_fl = $self->{-use_fl};
+
+ while (scalar @$uids) {
+ my @batch = splice(@$uids, 0, $bs);
+ my $batch = join(',', @batch);
+ local $0 = "UID:$batch $mbx $sec";
+ my $r = $mic->fetch_hash($batch, $req, 'FLAGS');
+ unless ($r) { # network error?
+ last if $!{EINTR} && $self->{quit};
+ $err = "E: $uri UID FETCH $batch error: $!";
+ last;
+ }
+ for my $uid (@batch) {
+ # messages get deleted, so holes appear
+ my $per_uid = delete $r->{$uid} // next;
+ my $raw = delete($per_uid->{$key}) // next;
+ my $fl = $use_fl ? $per_uid->{FLAGS} : undef;
+ _imap_do_msg($self, $uri, $uid, \$raw, $fl);
+ $last_uid = $uid;
+ last if $self->{quit};
+ }
+ last if $self->{quit};
+ }
+ ($last_uid, $err);
+}
+
sub _imap_fetch_all ($$$) {
my ($self, $mic, $orig_uri) = @_;
my $sec = uri_section($orig_uri);
$mic->Uid(1); # the default, we hope
my $err;
my $use_fl = perm_fl_ok($perm_fl);
+ local $self->{-use_fl} = $use_fl;
if (!defined($single_uid) && $self->{each_old} && $use_fl) {
$err = each_old_flags($self, $mic, $uri, $l_uid);
return $err if $err;
my $m = $mod ? " [(UID % $mod) == $shard]" : '';
warn "# $uri fetching UID $l_uid:$r_uid$m\n";
}
- my $bs = $self->{cfg_opt}->{$sec}->{batch_size} // 1;
- my $req = $mic->imap4rev1 ? 'BODY.PEEK[]' : 'RFC822.PEEK';
- my $key = $req;
- $key =~ s/\.PEEK//;
- my ($uids, $batch);
+ my $fetch_cb = \&_imap_fetch_bodies;
do {
# I wish "UID FETCH $START:*" could work, but:
# 1) servers do not need to return results in any order
# 2) Mail::IMAPClient doesn't offer a streaming API
+ my $uids;
if (defined $single_uid) {
$uids = [ $single_uid ];
} elsif (!($uids = $mic->search("UID $l_uid:*"))) {
return if $uids->[0] < $l_uid;
$l_uid = $uids->[-1] + 1; # for next search
- my $last_uid;
- my $n = $self->{max_batch};
-
@$uids = grep { ($_ % $mod) == $shard } @$uids if $mod;
- while (scalar @$uids) {
- my @batch = splice(@$uids, 0, $bs);
- $batch = join(',', @batch);
- local $0 = "UID:$batch $mbx $sec";
- my $r = $mic->fetch_hash($batch, $req, 'FLAGS');
- unless ($r) { # network error?
- last if $!{EINTR} && $self->{quit};
- $err = "E: $uri UID FETCH $batch error: $!";
- last;
- }
- for my $uid (@batch) {
- # messages get deleted, so holes appear
- my $per_uid = delete $r->{$uid} // next;
- my $raw = delete($per_uid->{$key}) // next;
- my $fl = $use_fl ? $per_uid->{FLAGS} : undef;
- _imap_do_msg($self, $uri, $uid, \$raw, $fl);
- $last_uid = $uid;
- last if $self->{quit};
- }
- last if $self->{quit};
- }
+ (my $last_uid, $err) = $fetch_cb->($self, $mic, $uri, $uids);
run_commit_cb($self);
$itrk->update_last($r_uidval, $last_uid) if $itrk;
} until ($err || $self->{quit} || defined($single_uid));
}
my $mic = mic_new($self, $mic_arg, $sec, $uri);
$cached //= {}; # invalid placeholder if no cache enabled
- $mic && $mic->IsConnected ? ($cached->{$sec} = $mic) : undef;
+ if ($mic && $mic->IsConnected) {
+ $cached->{$sec} = $mic;
+ } else {
+ warn 'IMAP LastError: ',$mic->LastError, "\n" if $mic;
+ warn "IMAP errno: $!\n" if $!;
+ undef;
+ }
}
sub imap_each {
}
(defined($num_a) && defined($num_b) && $num_a > $num_b) and
return "E: $uri: backwards range: $num_a > $num_b";
-
+ if (defined($num_a)) { # no article numbers in mail_sync.sqlite3
+ $uri = $uri->clone;
+ $uri->group($group);
+ }
# IMAPTracker is also used for tracking NNTP, UID == article number
# LIST.ACTIVE can get the equivalent of UIDVALIDITY, but that's
# expensive. So we assume newsgroups don't change: