]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/LeiToMail.pm
lei <q|up>: distinguish between mset and l2m counts
[public-inbox.git] / lib / PublicInbox / LeiToMail.pm
index 46a82a4bd586d295caa3b021e5fd0be7ddb163cf..a546ab421eeb7c72282f1e72de3a811fbad1f2ed 100644 (file)
@@ -38,7 +38,7 @@ sub _mbox_hdr_buf ($$$) {
                if (my $ent = $kw2status{$k}) {
                        push @{$hdr{$ent->[0]}}, $ent->[1];
                } else { # X-Label?
-                       warn "TODO: keyword `$k' not supported for mbox\n";
+                       warn "# keyword `$k' not supported for mbox\n";
                }
        }
        # Messages are always 'O' (non-\Recent in IMAP), it saves
@@ -196,9 +196,13 @@ sub _mbox_write_cb ($$) {
                return if $dedupe->is_dup($eml, $smsg);
                $lse->xsmsg_vmd($smsg) if $lse;
                $buf = $eml2mbox->($eml, $smsg);
-               return atomic_append($lei, $buf) if $atomic_append;
-               my $lk = $ovv->lock_for_scope;
-               $lei->out($$buf);
+               if ($atomic_append) {
+                       atomic_append($lei, $buf);
+               } else {
+                       my $lk = $ovv->lock_for_scope;
+                       $lei->out($$buf);
+               }
+               ++$lei->{-nr_write};
        }
 }
 
@@ -273,15 +277,13 @@ sub _maildir_write_cb ($$) {
        my $dst = $lei->{ovv}->{dst};
        my $lse = $lei->{lse}; # may be undef
        sub { # for git_to_mail
-               my ($buf, $smsg, $eml) = @_;
+               my ($bref, $smsg, $eml) = @_;
                $dst // return $lei->fail; # dst may be undef-ed in last run
-               $buf //= \($eml->as_string);
+               return if $dedupe && $dedupe->is_dup($eml //
+                                               PublicInbox::Eml->new($$bref));
                $lse->xsmsg_vmd($smsg) if $lse;
-               return _buf2maildir($dst, $buf, $smsg) if !$dedupe;
-               $eml //= PublicInbox::Eml->new($$buf); # copy buf
-               return if $dedupe->is_dup($eml, $smsg);
-               undef $eml;
-               _buf2maildir($dst, $buf, $smsg);
+               _buf2maildir($dst, $bref // \($eml->as_string), $smsg);
+               ++$lei->{-nr_write};
        }
 }
 
@@ -296,16 +298,35 @@ sub _imap_write_cb ($$) {
        sub { # for git_to_mail
                my ($bref, $smsg, $eml) = @_;
                $mic // return $lei->fail; # mic may be undef-ed in last run
-               if ($dedupe) {
-                       $eml //= PublicInbox::Eml->new($$bref); # copy bref
-                       return if $dedupe->is_dup($eml, $smsg);
-               }
+               return if $dedupe && $dedupe->is_dup($eml //
+                                               PublicInbox::Eml->new($$bref));
                $lse->xsmsg_vmd($smsg) if $lse;
                eval { $imap_append->($mic, $folder, $bref, $smsg, $eml) };
                if (my $err = $@) {
                        undef $mic;
                        die $err;
                }
+               ++$lei->{-nr_write};
+       }
+}
+
+sub _text_write_cb ($$) {
+       my ($self, $lei) = @_;
+       my $dedupe = $lei->{dedupe};
+       $dedupe->prepare_dedupe if $dedupe;
+       my $lvt = $lei->{lvt};
+       my $ovv = $lei->{ovv};
+       $lei->{1} // die "no stdout ($ovv->{dst})"; # redirected earlier
+       $lei->{1}->autoflush(1);
+       binmode $lei->{1}, ':utf8';
+       my $lse = $lei->{lse}; # may be undef
+       sub { # for git_to_mail
+               my ($bref, $smsg, $eml) = @_;
+               $lse->xsmsg_vmd($smsg) if $lse;
+               $eml //= PublicInbox::Eml->new($bref); # copy bref
+               return if $dedupe && $dedupe->is_dup($eml, $smsg);
+               my $lk = $ovv->lock_for_scope;
+               $lei->out(${$lvt->eml_to_text($smsg, $eml)}, "\n");
        }
 }
 
@@ -329,27 +350,39 @@ sub new {
                $lei->{ovv}->{dst} = $dst .= '/' if substr($dst, -1) ne '/';
        } elsif (substr($fmt, 0, 4) eq 'mbox') {
                require PublicInbox::MboxReader;
-               (-d $dst || (-e _ && !-w _)) and die
-                       "$dst exists and is not a writable file\n";
                $self->can("eml2$fmt") or die "bad mbox format: $fmt\n";
                $self->{base_type} = 'mbox';
-       } elsif ($fmt =~ /\Aimaps?\z/) { # TODO .onion support
+       } elsif ($fmt =~ /\Aimaps?\z/) {
                require PublicInbox::NetWriter;
                require PublicInbox::URIimap;
                my $net = PublicInbox::NetWriter->new;
                $net->{quiet} = $lei->{opt}->{quiet};
                my $uri = PublicInbox::URIimap->new($dst)->canonical;
                $net->add_url($uri);
-               my $err = $net->errors;
+               my $err = $net->errors($lei);
                return $lei->fail($err) if $err;
                $uri->mailbox or return $lei->fail("No mailbox: $dst");
                $self->{uri} = $uri;
                $dst = $lei->{ovv}->{dst} = $$uri; # canonicalized
                $lei->{net} = $net;
                $self->{base_type} = 'imap';
+       } elsif ($fmt eq 'text') {
+               require PublicInbox::LeiViewText;
+               $lei->{lvt} = PublicInbox::LeiViewText->new($lei);
+               $self->{base_type} = 'text';
        } else {
                die "bad mail --format=$fmt\n";
        }
+       if ($self->{base_type} =~ /\A(?:text|mbox)\z/) {
+               (-d $dst || (-e _ && !-w _)) and die
+                       "$dst exists and is not a writable file\n";
+       }
+       if ($self->{base_type} eq 'text') {
+               my @err = map {
+                       defined($lei->{opt}->{$_}) ? "--$_" : ();
+               } (qw(mua save));
+               die "@err incompatible with $fmt\n" if @err;
+       }
        $self->{dst} = $dst;
        $lei->{dedupe} = $lei->{lss} // do {
                my $dd_cls = 'PublicInbox::'.
@@ -371,6 +404,8 @@ sub _pre_augment_maildir {
                File::Path::mkpath($d);
                -d $d or die "$d is not a directory";
        }
+       # for utime, so no opendir
+       open $self->{poke_dh}, '<', "${dst}cur" or die "open ${dst}cur: $!";
 }
 
 sub _do_augment_maildir {
@@ -427,6 +462,29 @@ sub _do_augment_imap {
        }
 }
 
+sub _pre_augment_text {
+       my ($self, $lei) = @_;
+       my $dst = $lei->{ovv}->{dst};
+       my $out;
+       my $devfd = $lei->path_to_fd($dst) // die "bad $dst";
+       if ($devfd >= 0) {
+               $out = $lei->{$devfd};
+       } else { # normal-looking path
+               if (-p $dst) {
+                       open $out, '>', $dst or die "open($dst): $!";
+               } elsif (-f _ || !-e _) {
+                       # text allows augment, HTML/Atom won't
+                       my $mode = $lei->{opt}->{augment} ? '>>' : '>';
+                       open $out, $mode, $dst or die "open($mode, $dst): $!";
+               } else {
+                       die "$dst is not a file or FIFO\n";
+               }
+       }
+       $lei->{ovv}->ovv_out_lk_init if !$lei->{ovv}->{lock_path};
+       $lei->{1} = $out;
+       undef;
+}
+
 sub _pre_augment_mbox {
        my ($self, $lei) = @_;
        my $dst = $lei->{ovv}->{dst};
@@ -521,8 +579,8 @@ sub pre_augment { # fast (1 disk seek), runs in same process as post_augment
 sub do_augment { # slow, runs in wq worker
        my ($self, $lei) = @_;
        # _do_augment_maildir, _do_augment_mbox, or _do_augment_imap
-       my $m = "_do_augment_$self->{base_type}";
-       $self->$m($lei);
+       my $m = $self->can("_do_augment_$self->{base_type}") or return;
+       $m->($self, $lei);
 }
 
 # fast (spawn compressor or mkdir), runs in same process as pre_augment
@@ -588,7 +646,7 @@ sub poke_dst {
        my ($self) = @_;
        if ($self->{base_type} eq 'maildir') {
                my $t = time + 1;
-               utime($t, $t, $self->{dst} . 'cur');
+               utime($t, $t, $self->{poke_dh}) or warn "futimes: $!";
        }
 }
 
@@ -602,7 +660,12 @@ sub write_mail { # via ->wq_io_do
 sub wq_atexit_child {
        my ($self) = @_;
        delete $self->{wcb};
-       $self->{lei}->{ale}->git->async_wait_all;
+       my $lei = $self->{lei};
+       $lei->{ale}->git->async_wait_all;
+       my $nr = delete($lei->{-nr_write}) or return;
+       return if $lei->{early_mua} || !$lei->{-progress} || !$lei->{pkt_op_p};
+       require PublicInbox::PktOp;
+       PublicInbox::PktOp::pkt_do($lei->{pkt_op_p}, 'l2m_progress', $nr);
 }
 
 # called in top-level lei-daemon when LeiAuth is done