1 # Copyright (C) 2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
4 # Writes PublicInbox::Eml objects atomically to a mbox variant or Maildir
5 package PublicInbox::LeiToMail;
10 my %kw2char = ( # Maildir characters
18 flagged => [ 'X-Status' => 'F' ],
19 answered => [ 'X-Status' => 'A' ],
20 seen => [ 'Status' => 'R' ],
21 draft => [ 'X-Status' => 'T' ],
24 sub _mbox_hdr_buf ($$$) {
25 my ($eml, $type, $kw) = @_;
26 $eml->header_set($_) for (qw(Lines Bytes Content-Length));
27 my %hdr; # set Status, X-Status
29 if (my $ent = $kw2status{$k}) {
30 push @{$hdr{$ent->[0]}}, $ent->[1];
32 warn "TODO: keyword `$k' not supported for mbox\n";
35 while (my ($name, $chars) = each %hdr) {
36 $eml->header_set($name, join('', sort @$chars));
38 my $buf = delete $eml->{hdr};
40 # fixup old bug from import (pre-a0c07cba0e5d8b6a)
41 $$buf =~ s/\A[\r\n]*From [^\r\n]*\r?\n//s;
43 substr($$buf, 0, 0, # prepend From line
44 "From lei\@$type Thu Jan 1 00:00:00 1970$eml->{crlf}");
48 sub write_in_full_atomic ($$) {
50 defined(my $w = syswrite($fh, $$buf)) or die "write: $!";
51 $w == length($$buf) or die "short write: $w != ".length($$buf);
54 sub eml2mboxrd ($;$) {
56 my $buf = _mbox_hdr_buf($eml, 'mboxrd', $kw);
57 if (my $bdy = delete $eml->{bdy}) {
58 $$bdy =~ s/^(>*From )/>$1/gm;
59 $$buf .= $eml->{crlf};
60 substr($$bdy, 0, 0, $$buf); # prepend header
63 $$buf .= $eml->{crlf};
69 my $buf = _mbox_hdr_buf($eml, 'mboxo', $kw);
70 if (my $bdy = delete $eml->{bdy}) {
71 $$bdy =~ s/^From />From /gm;
72 $$buf .= $eml->{crlf};
73 substr($$bdy, 0, 0, $$buf); # prepend header
76 $$buf .= $eml->{crlf};
80 # mboxcl still escapes "From " lines
83 my $buf = _mbox_hdr_buf($eml, 'mboxcl', $kw);
84 my $crlf = $eml->{crlf};
85 if (my $bdy = delete $eml->{bdy}) {
86 $$bdy =~ s/^From />From /gm;
87 $$buf .= 'Content-Length: '.length($$bdy).$crlf.$crlf;
88 substr($$bdy, 0, 0, $$buf); # prepend header
95 # mboxcl2 has no "From " escaping
98 my $buf = _mbox_hdr_buf($eml, 'mboxcl2', $kw);
99 my $crlf = $eml->{crlf};
100 if (my $bdy = delete $eml->{bdy}) {
101 $$buf .= 'Content-Length: '.length($$bdy).$crlf.$crlf;
102 substr($$bdy, 0, 0, $$buf); # prepend header