public-inbox.git  about / heads / tags
an "archives first" approach to mailing lists
blob 7511144c6419e235e7b2e63f5a81f64fa3fed6a0 3831 bytes (raw)
$ git show fuse3:lib/PublicInbox/MailDiff.pm	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
 
# Copyright (C) all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
package PublicInbox::MailDiff;
use v5.12;
use File::Temp 0.19 (); # 0.19 for ->newdir
use PublicInbox::ContentHash qw(content_digest);
use PublicInbox::MsgIter qw(msg_part_text);
use PublicInbox::ViewDiff qw(flush_diff);
use PublicInbox::GitAsyncCat;

sub write_part { # Eml->each_part callback
	my ($ary, $self) = @_;
	my ($part, $depth, $idx) = @$ary;
	if ($idx ne '1' || $self->{-raw_hdr}) {
		open my $fh, '>', "$self->{curdir}/$idx.hdr" or die "open: $!";
		print $fh ${$part->{hdr}} or die "print $!";
		close $fh or die "close $!";
	}
	my $ct = $part->content_type || 'text/plain';
	my ($s, $err) = msg_part_text($part, $ct);
	my $sfx = defined($s) ? 'txt' : 'bin';
	$s //= $part->body;
	$s =~ s/\r+\n/\n/sg;
	open my $fh, '>:utf8', "$self->{curdir}/$idx.$sfx" or die "open: $!";
	print $fh $s or die "print $!";
	close $fh or die "close $!";
}

# public
sub dump_eml ($$$) {
	my ($self, $dir, $eml) = @_;
	local $self->{curdir} = $dir;
	mkdir $dir or die "mkdir($dir): $!";
	$eml->each_part(\&write_part, $self);

	return if $self->{ctx}; # don't need content_digest noise in WWW UI
	require PublicInbox::ContentDigestDbg;

	# XXX is this even useful?  perhaps hide it behind a CLI switch
	open my $fh, '>', "$dir/content_digest" or die "open: $!";
	my $dig = PublicInbox::ContentDigestDbg->new($fh);
	content_digest($eml, $dig);
	print $fh "\n", $dig->hexdigest, "\n" or die "print $!";
	close $fh or die "close: $!";
}

# public
sub prep_a ($$) {
	my ($self, $eml) = @_;
	$self->{tmp} = File::Temp->newdir('mail-diff-XXXX', TMPDIR => 1);
	dump_eml($self, "$self->{tmp}/a", $eml);
}

sub next_smsg ($) {
	my ($self) = @_;
	my $ctx = $self->{ctx};
	my $over = $ctx->{ibx}->over;
	$self->{smsg} = $over ? $over->next_by_mid(@{$self->{next_arg}})
			: $ctx->gone('over');
	if (!$self->{smsg}) {
		$ctx->write($ctx->_html_end);
		return $ctx->close;
	}
	my $async = $self->{ctx}->{env}->{'pi-httpd.async'};
	$async->(undef, undef, $self) if $async # PublicInbox::HTTPD::Async->new
}

sub emit_msg_diff {
	my ($bref, $self) = @_; # bref is `git diff' output
	# will be escaped to `&#8226;' in HTML
	utf8::decode($$bref);
	$self->{ctx}->{ibx}->{obfuscate} and
		obfuscate_addrs($self->{ctx}->{ibx}, $$bref, "\x{2022}");
	print { $self->{ctx}->{zfh} } '</pre><hr><pre>' if $self->{nr} > 1;
	flush_diff($self->{ctx}, $bref);
	next_smsg($self);
}

sub do_diff {
	my ($self, $eml) = @_;
	my $n = 'N'.(++$self->{nr});
	my $dir = "$self->{tmp}/$n";
	$self->dump_eml($dir, $eml);
	my $cmd = [ qw(git diff --no-index --no-color -- a), $n ];
	my $opt = { -C => "$self->{tmp}", quiet => 1 };
	my $qsp = PublicInbox::Qspawn->new($cmd, undef, $opt);
	$qsp->psgi_qx($self->{ctx}->{env}, undef, \&emit_msg_diff, $self);
}

sub diff_msg_i {
	my ($self, $eml) = @_;
	if ($eml) {
		if ($self->{tmp}) { # 2nd..last message
			do_diff($self, $eml);
		} else { # first message:
			prep_a($self, $eml);
			next_smsg($self);
		}
	} else {
		warn "W: $self->{smsg}->{blob} missing\n";
		next_smsg($self);
	}
}

sub diff_msg_i_async {
	my ($bref, $oid, $type, $size, $self) = @_;
	diff_msg_i($self, $bref ? PublicInbox::Eml->new($bref) : undef);
}

sub event_step {
	my ($self) = @_;
	eval {
		my $ctx = $self->{ctx};
		if ($ctx->{env}->{'pi-httpd.async'}) {
			ibx_async_cat($ctx->{ibx}, $self->{smsg}->{blob},
					\&diff_msg_i_async, $self);
		} else {
			diff_msg_i($self, $ctx->{ibx}->smsg_eml($self->{smsg}));
		}
	};
	if ($@) {
		warn "E: $@";
		delete $self->{smsg};
		$self->{ctx}->close;
	}
}

sub begin_mail_diff {
	my ($self) = @_;
	if (my $async = $self->{ctx}->{env}->{'pi-httpd.async'}) {
		$async->(undef, undef, $self); # PublicInbox::HTTPD::Async->new
	} else {
		event_step($self) while $self->{smsg};
	}
}

1;

git clone https://public-inbox.org/public-inbox.git
git clone http://7fh6tueqddpjyxjmgtdiueylzoqt6pt7hec3pukyptlmohoowvhde4yd.onion/public-inbox.git