1 # Copyright (C) 2016 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3 package PublicInbox::WatchMaildir;
7 use Email::MIME::ContentType;
8 $Email::MIME::ContentType::STRICT_PARAMS = 0; # user input is imperfect
10 use PublicInbox::Import;
14 my ($class, $config) = @_;
16 foreach my $k (keys %$config) {
17 $k =~ /\Apublicinbox\.([^\.]+)\.watch\z/ or next;
19 my $watch = $config->{$k};
20 if ($watch =~ s/\Amaildir://) {
22 my $inbox = $config->lookup_name($name);
23 if (my $wm = $inbox->{watchheader}) {
24 my ($k, $v) = split(/:/, $wm, 2);
25 $inbox->{-watchheader} = [ $k, qr/\Q$v\E/ ];
27 my $new = "$watch/new";
28 my $cur = "$watch/cur";
29 push @mdir, $new, $cur;
30 $mdmap{$new} = $inbox;
31 $mdmap{$cur} = $inbox;
33 warn "watch unsupported: $k=$watch\n";
38 my $mdre = join('|', map { quotemeta($_) } @mdir);
39 $mdre = qr!\A($mdre)/!;
49 my ($self, $paths) = @_;
50 _try_path($self, $_->{path}) foreach @$paths;
51 $_->done foreach values %{$self->{importers}};
55 my ($self, $path) = @_;
56 if ($path !~ $self->{mdre}) {
57 warn "unrecognized path: $path\n";
60 my $inbox = $self->{mdmap}->{$1};
62 warn "unmappable dir: $1\n";
65 my $im = $inbox->{-import} ||= eval {
66 my $git = $inbox->git;
67 my $name = $inbox->{name};
68 my $addr = $inbox->{-primary_address};
69 PublicInbox::Import->new($git, $name, $addr);
71 $self->{importers}->{"$im"} = $im;
73 if (open my $fh, '<', $path) {
77 $mime = Email::MIME->new(\$str);
78 } elsif ($!{ENOENT}) {
81 warn "failed to open $path: $!\n";
85 $mime->header_set($_) foreach @PublicInbox::MDA::BAD_HEADERS;
86 my $wm = $inbox->{-watchheader};
88 my $v = $mime->header_obj->header_raw($wm->[0]);
89 return unless ($v && $v =~ $wm->[1]);
91 my $f = $inbox->{filter};
92 if ($f && $f =~ /::/) {
98 $mime = $f->scrub($mime);
102 my $mid = $mime->header_obj->header_raw('Message-Id');
108 my $cb = sub { _try_fsn_paths($self, \@_) };
109 my $mdir = $self->{mdir};
111 require Filesys::Notify::Simple;
112 my $watcher = Filesys::Notify::Simple->new($mdir);
113 $watcher->wait($cb) while (1);
118 my $mdir = $self->{mdir};
119 foreach my $dir (@$mdir) {
120 my $ok = opendir(my $dh, $dir);
122 warn "failed to open $dir: $!\n";
125 while (my $fn = readdir($dh)) {
126 next unless $fn =~ /\A[a-zA-Z0-9][\w:,=\.]+\z/;
129 _try_path($self, $fn);
131 warn "not a file: $fn\n";