]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/MailDiff.pm
ipc: remove {-reap_async} field
[public-inbox.git] / lib / PublicInbox / MailDiff.pm
1 # Copyright (C) all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3 package PublicInbox::MailDiff;
4 use v5.12;
5 use File::Temp 0.19 (); # 0.19 for ->newdir
6 use PublicInbox::ContentHash qw(content_digest);
7 use PublicInbox::ContentDigestDbg;
8 use Data::Dumper ();
9 use PublicInbox::MsgIter qw(msg_part_text);
10 use PublicInbox::ViewDiff qw(flush_diff);
11 use PublicInbox::GitAsyncCat;
12
13 sub write_part { # Eml->each_part callback
14         my ($ary, $self) = @_;
15         my ($part, $depth, $idx) = @$ary;
16         if ($idx ne '1' || $self->{-raw_hdr}) {
17                 open my $fh, '>', "$self->{curdir}/$idx.hdr" or die "open: $!";
18                 print $fh ${$part->{hdr}} or die "print $!";
19                 close $fh or die "close $!";
20         }
21         my $ct = $part->content_type || 'text/plain';
22         my ($s, $err) = msg_part_text($part, $ct);
23         my $sfx = defined($s) ? 'txt' : 'bin';
24         open my $fh, '>', "$self->{curdir}/$idx.$sfx" or die "open: $!";
25         print $fh ($s // $part->body) or die "print $!";
26         close $fh or die "close $!";
27 }
28
29 # public
30 sub dump_eml ($$$) {
31         my ($self, $dir, $eml) = @_;
32         local $self->{curdir} = $dir;
33         mkdir $dir or die "mkdir($dir): $!";
34         $eml->each_part(\&write_part, $self);
35
36         return if $self->{ctx}; # don't need content_digest noise in WWW UI
37
38         # XXX is this even useful?  perhaps hide it behind a CLI switch
39         open my $fh, '>', "$dir/content_digest" or die "open: $!";
40         my $dig = PublicInbox::ContentDigestDbg->new($fh);
41         local $Data::Dumper::Useqq = 1;
42         local $Data::Dumper::Terse = 1;
43         content_digest($eml, $dig);
44         print $fh "\n", $dig->hexdigest, "\n" or die "print $!";
45         close $fh or die "close: $!";
46 }
47
48 # public
49 sub prep_a ($$) {
50         my ($self, $eml) = @_;
51         $self->{tmp} = File::Temp->newdir('mail-diff-XXXX', TMPDIR => 1);
52         dump_eml($self, "$self->{tmp}/a", $eml);
53 }
54
55 sub next_smsg ($) {
56         my ($self) = @_;
57         my $ctx = $self->{ctx};
58         my $over = $ctx->{ibx}->over;
59         $self->{smsg} = $over ? $over->next_by_mid(@{$self->{next_arg}})
60                         : $ctx->gone('over');
61         if (!$self->{smsg}) {
62                 $ctx->write($ctx->_html_end);
63                 return $ctx->close;
64         }
65         my $async = $self->{ctx}->{env}->{'pi-httpd.async'};
66         $async->(undef, undef, $self) if $async # PublicInbox::HTTPD::Async->new
67 }
68
69 sub emit_msg_diff {
70         my ($bref, $self) = @_; # bref is `git diff' output
71         # will be escaped to `&#8226;' in HTML
72         $self->{ctx}->{ibx}->{obfuscate} and
73                 obfuscate_addrs($self->{ctx}->{ibx}, $$bref, "\x{2022}");
74         $$bref =~ s/\r+\n/\n/sg;
75         print { $self->{ctx}->{zfh} } '</pre><hr><pre>' if $self->{nr} > 1;
76         flush_diff($self->{ctx}, $bref);
77         next_smsg($self);
78 }
79
80 sub do_diff {
81         my ($self, $eml) = @_;
82         my $n = 'N'.(++$self->{nr});
83         my $dir = "$self->{tmp}/$n";
84         $self->dump_eml($dir, $eml);
85         my $cmd = [ qw(git diff --no-index --no-color -- a), $n ];
86         my $opt = { -C => "$self->{tmp}", quiet => 1 };
87         my $qsp = PublicInbox::Qspawn->new($cmd, undef, $opt);
88         $qsp->psgi_qx($self->{ctx}->{env}, undef, \&emit_msg_diff, $self);
89 }
90
91 sub diff_msg_i {
92         my ($self, $eml) = @_;
93         if ($eml) {
94                 if ($self->{tmp}) { # 2nd..last message
95                         do_diff($self, $eml);
96                 } else { # first message:
97                         prep_a($self, $eml);
98                         next_smsg($self);
99                 }
100         } else {
101                 warn "W: $self->{smsg}->{blob} missing\n";
102                 next_smsg($self);
103         }
104 }
105
106 sub diff_msg_i_async {
107         my ($bref, $oid, $type, $size, $self) = @_;
108         diff_msg_i($self, $bref ? PublicInbox::Eml->new($bref) : undef);
109 }
110
111 sub event_step {
112         my ($self) = @_;
113         eval {
114                 my $ctx = $self->{ctx};
115                 if ($ctx->{env}->{'pi-httpd.async'}) {
116                         ibx_async_cat($ctx->{ibx}, $self->{smsg}->{blob},
117                                         \&diff_msg_i_async, $self);
118                 } else {
119                         diff_msg_i($self, $ctx->{ibx}->smsg_eml($self->{smsg}));
120                 }
121         };
122         if ($@) {
123                 warn "E: $@";
124                 delete $self->{smsg};
125                 $self->{ctx}->close;
126         }
127 }
128
129 sub begin_mail_diff {
130         my ($self) = @_;
131         if (my $async = $self->{ctx}->{env}->{'pi-httpd.async'}) {
132                 $async->(undef, undef, $self); # PublicInbox::HTTPD::Async->new
133         } else {
134                 event_step($self) while $self->{smsg};
135         }
136 }
137
138 1;