]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/MdirReader.pm
lei: maildir: handle "forwarded" keyword as "P"
[public-inbox.git] / lib / PublicInbox / MdirReader.pm
1 # Copyright (C) 2020-2021 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3
4 # Maildirs for now, MH eventually
5 # ref: https://cr.yp.to/proto/maildir.html
6 #       https://wiki2.dovecot.org/MailboxFormat/Maildir
7 package PublicInbox::MdirReader;
8 use strict;
9 use v5.10.1;
10 use PublicInbox::InboxWritable qw(eml_from_path);
11
12 # returns Maildir flags from a basename ('' for no flags, undef for invalid)
13 sub maildir_basename_flags {
14         my (@f) = split(/:/, $_[0], -1);
15         return if (scalar(@f) > 2 || substr($f[0], 0, 1) eq '.');
16         $f[1] // return ''; # "new"
17         $f[1] =~ /\A2,([A-Za-z]*)\z/ ? $1 : undef; # "cur"
18 }
19
20 # same as above, but for full path name
21 sub maildir_path_flags {
22         my ($f) = @_;
23         my $i = rindex($f, '/');
24         $i >= 0 ? maildir_basename_flags(substr($f, $i + 1)) : undef;
25 }
26
27 sub maildir_each_file ($$;@) {
28         my ($dir, $cb, @arg) = @_;
29         $dir .= '/' unless substr($dir, -1) eq '/';
30         for my $d (qw(new/ cur/)) {
31                 my $pfx = $dir.$d;
32                 opendir my $dh, $pfx or next;
33                 while (defined(my $bn = readdir($dh))) {
34                         maildir_basename_flags($bn) // next;
35                         $cb->($pfx.$bn, @arg);
36                 }
37         }
38 }
39
40 my %c2kw = ('D' => 'draft', F => 'flagged', P => 'forwarded',
41         R => 'answered', S => 'seen');
42
43 sub maildir_each_eml ($$;@) {
44         my ($dir, $cb, @arg) = @_;
45         $dir .= '/' unless substr($dir, -1) eq '/';
46         my $pfx = $dir . 'new/';
47         if (opendir(my $dh, $pfx)) {
48                 while (defined(my $bn = readdir($dh))) {
49                         next if substr($bn, 0, 1) eq '.';
50                         my @f = split(/:/, $bn, -1);
51                         next if scalar(@f) != 1;
52                         my $f = $pfx.$bn;
53                         my $eml = eml_from_path($f) or next;
54                         $cb->($f, [], $eml, @arg);
55                 }
56         }
57         $pfx = $dir . 'cur/';
58         opendir my $dh, $pfx or return;
59         while (defined(my $bn = readdir($dh))) {
60                 my $fl = maildir_basename_flags($bn) // next;
61                 next if index($fl, 'T') >= 0;
62                 my $f = $pfx.$bn;
63                 my $eml = eml_from_path($f) or next;
64                 my @kw = sort(map { $c2kw{$_} // () } split(//, $fl));
65                 $cb->($f, \@kw, $eml, @arg);
66         }
67 }
68
69 1;