]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/InboxWritable.pm
treewide: replace {-inbox} with {ibx} for consistency
[public-inbox.git] / lib / PublicInbox / InboxWritable.pm
index e684f5464167d2d25bf15b2dae0998d1d95fe2f7..4980904541af7f245c09787d6bea59f8082d64c6 100644 (file)
@@ -4,10 +4,12 @@
 # Extends read-only Inbox for writing
 package PublicInbox::InboxWritable;
 use strict;
-use warnings;
-use base qw(PublicInbox::Inbox);
+use v5.10.1;
+use parent qw(PublicInbox::Inbox Exporter);
 use PublicInbox::Import;
 use PublicInbox::Filter::Base qw(REJECT);
+use Errno qw(ENOENT);
+our @EXPORT_OK = qw(eml_from_path warn_ignore_cb);
 
 use constant {
        PERM_UMASK => 0,
@@ -19,6 +21,7 @@ use constant {
 
 sub new {
        my ($class, $ibx, $creat_opt) = @_;
+       return $ibx if ref($ibx) eq $class;
        my $self = bless $ibx, $class;
 
        # TODO: maybe stop supporting this
@@ -36,12 +39,33 @@ sub assert_usable_dir {
        die "no inboxdir defined for $self->{name}\n";
 }
 
+sub _init_v1 {
+       my ($self, $skip_artnum) = @_;
+       if (defined($self->{indexlevel}) || defined($skip_artnum)) {
+               require PublicInbox::SearchIdx;
+               require PublicInbox::Msgmap;
+               my $sidx = PublicInbox::SearchIdx->new($self, 1); # just create
+               $sidx->begin_txn_lazy;
+               if (defined $skip_artnum) {
+                       my $mm = PublicInbox::Msgmap->new($self->{inboxdir}, 1);
+                       $mm->{dbh}->begin_work;
+                       $mm->skip_artnum($skip_artnum);
+                       $mm->{dbh}->commit;
+               }
+               $sidx->commit_txn_lazy;
+       } else {
+               open my $fh, '>>', "$self->{inboxdir}/ssoma.lock" or
+                       die "$self->{inboxdir}/ssoma.lock: $!\n";
+       }
+}
+
 sub init_inbox {
        my ($self, $shards, $skip_epoch, $skip_artnum) = @_;
-       # TODO: honor skip_artnum
        if ($self->version == 1) {
                my $dir = assert_usable_dir($self);
                PublicInbox::Import::init_bare($dir);
+               $self->umask_prepare;
+               $self->with_umask(\&_init_v1, $self, $skip_artnum);
        } else {
                my $v2w = importer($self);
                $v2w->init_inbox($shards, $skip_epoch, $skip_artnum);
@@ -56,7 +80,7 @@ sub importer {
                die "v2 not supported: $@\n" if $@;
                my $opt = $self->{-creat_opt};
                my $v2w = PublicInbox::V2Writable->new($self, $opt);
-               $v2w->{parallel} = $parallel;
+               $v2w->{parallel} = $parallel if defined $parallel;
                $v2w;
        } elsif ($v == 1) {
                my @arg = (undef, undef, undef, $self);
@@ -78,7 +102,7 @@ sub filter {
                        $im->done;
                }
 
-               my @args = (-inbox => $self);
+               my @args = (ibx => $self);
                # basic line splitting, only
                # Perhaps we can have proper quote splitting one day...
                ($f, @args) = split(/\s+/, $f) if $f =~ /\s+/;
@@ -110,19 +134,14 @@ sub is_maildir_path ($) {
        (is_maildir_basename($p[-1]) && -f $path) ? 1 : 0;
 }
 
-sub maildir_path_load ($) {
+sub eml_from_path ($) {
        my ($path) = @_;
        if (open my $fh, '<', $path) {
-               local $/;
-               my $str = <$fh>;
-               $str or return;
-               return PublicInbox::MIME->new(\$str);
-       } elsif ($!{ENOENT}) {
-               # common with Maildir
-               return;
-       } else {
-               warn "failed to open $path: $!\n";
-               return;
+               my $str = do { local $/; <$fh> } or return;
+               PublicInbox::Eml->new(\$str);
+       } else { # ENOENT is common with Maildir
+               warn "failed to open $path: $!\n" if $! != ENOENT;
+               undef;
        }
 }
 
@@ -137,7 +156,7 @@ sub import_maildir {
                opendir my $dh, "$dir/$sub" or die "opendir $dir/$sub: $!\n";
                while (defined(my $fn = readdir($dh))) {
                        next unless is_maildir_basename($fn);
-                       my $mime = maildir_path_load("$dir/$fn") or next;
+                       my $mime = eml_from_path("$dir/$fn") or next;
 
                        if (my $filter = $self->filter($im)) {
                                my $ret = $filter->scrub($mime) or return;
@@ -156,12 +175,12 @@ my $from_strict = qr/^From \S+ +\S+ \S+ +\S+ [^:]+:[^:]+:[^:]+ [^:]+/;
 sub mb_add ($$$$) {
        my ($im, $variant, $filter, $msg) = @_;
        $$msg =~ s/(\r?\n)+\z/$1/s;
-       my $mime = PublicInbox::MIME->new($msg);
        if ($variant eq 'mboxrd') {
-               $$msg =~ s/^>(>*From )/$1/sm;
+               $$msg =~ s/^>(>*From )/$1/gms;
        } elsif ($variant eq 'mboxo') {
-               $$msg =~ s/^>From /From /sm;
+               $$msg =~ s/^>From /From /gms;
        }
+       my $mime = PublicInbox::Eml->new($msg);
        if ($filter) {
                my $ret = $filter->scrub($mime) or return;
                return if $ret == REJECT();
@@ -239,9 +258,9 @@ sub _umask_for {
 }
 
 sub with_umask {
-       my ($self, $cb) = @_;
+       my ($self, $cb, @arg) = @_;
        my $old = umask $self->{umask};
-       my $rv = eval { $cb->() };
+       my $rv = eval { $cb->(@arg) };
        my $err = $@;
        umask $old;
        die $err if $err;
@@ -259,4 +278,45 @@ sub cleanup ($) {
        delete @{$_[0]}{qw(over mm git search)};
 }
 
+# warnings to ignore when handling spam mailboxes and maybe other places
+sub warn_ignore {
+       my $s = "@_";
+       # Email::Address::XS warnings
+       $s =~ /^Argument contains empty address at /
+       || $s =~ /^Element at index [0-9]+ contains /
+       # PublicInbox::MsgTime
+       || $s =~ /^bogus TZ offset: .+?, ignoring and assuming \+0000/
+       || $s =~ /^bad Date: .+? in /
+}
+
+# this expects to be RHS in this assignment: "local $SIG{__WARN__} = ..."
+sub warn_ignore_cb {
+       my $cb = $SIG{__WARN__} // sub { print STDERR @_ };
+       sub {
+               return if warn_ignore(@_);
+               $cb->(@_);
+       }
+}
+
+# v2+ only
+sub git_dir_n { "$_[0]->{inboxdir}/git/$_[1].git" }
+
+# v2+ only
+sub git_dir_latest {
+       my ($self, $max) = @_;
+       $$max = -1;
+       my $pfx = "$self->{inboxdir}/git";
+       return unless -d $pfx;
+       my $latest;
+       opendir my $dh, $pfx or die "opendir $pfx: $!\n";
+       while (defined(my $git_dir = readdir($dh))) {
+               $git_dir =~ m!\A([0-9]+)\.git\z! or next;
+               if ($1 > $$max) {
+                       $$max = $1;
+                       $latest = "$pfx/$git_dir";
+               }
+       }
+       $latest;
+}
+
 1;