From 2394cb0bdc671605729b5a4c578ef4cd3b9813fd Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 24 Oct 2019 00:12:36 +0000 Subject: [PATCH] view: display redundant headers in permalink Mail headers can contain multiple headers of any type, so ensure we don't hide any information we're getting in the per-message permalink views. This means it's possible to have multiple From, Date, To, Cc, Subject, and In-Reply-To headers displayed. The thread indices are a special case, I guess, since we run out of space on the line if the headers too long and tools like mutt only show the first one. --- lib/PublicInbox/Linkify.pm | 29 +++++++++++++++ lib/PublicInbox/View.pm | 75 ++++++++++++++++++++++---------------- 2 files changed, 73 insertions(+), 31 deletions(-) diff --git a/lib/PublicInbox/Linkify.pm b/lib/PublicInbox/Linkify.pm index 175f8d72..5b83742c 100644 --- a/lib/PublicInbox/Linkify.pm +++ b/lib/PublicInbox/Linkify.pm @@ -89,4 +89,33 @@ sub linkify_2 { $_[1]; } +# single pass linkification of within $str +# with $pfx being the URL prefix +sub linkify_mids { + my ($self, $pfx, $str) = @_; + $$str =~ s!<([^>]+)>! + my $msgid = PublicInbox::Hval->new_msgid($1); + my $html = $msgid->as_html; + my $href = $msgid->{href}; + $href = ascii_html($href); # for IDN + + # salt this, as this could be exploited to show + # links in the HTML which don't show up in the raw mail. + my $key = sha1_hex($html . $SALT); + $self->{$key} = [ $href, $html ]; + ''; + !ge; + $$str = ascii_html($$str); + $$str =~ s!\bPI-LINK-([a-f0-9]{40})\b! + my $key = $1; + my $repl = $_[0]->{$key}; + if (defined $repl) { + "[0]/\">$repl->[1]"; + } else { + # false positive or somebody tried to mess with us + $key; + } + !ge; +} + 1; diff --git a/lib/PublicInbox/View.pm b/lib/PublicInbox/View.pm index aeb32fc8..1aa014fd 100644 --- a/lib/PublicInbox/View.pm +++ b/lib/PublicInbox/View.pm @@ -190,8 +190,8 @@ sub fold_addresses ($) { sub _hdr_names_html ($$) { my ($hdr, $field) = @_; - my $val = $hdr->header($field) or return ''; - ascii_html(join(', ', PublicInbox::Address::names($val))); + my @vals = $hdr->header($field) or return ''; + ascii_html(join(', ', PublicInbox::Address::names(join(',', @vals)))); } sub nr_to_s ($$$) { @@ -643,12 +643,11 @@ sub _msg_html_prepare { if ($over) { $ctx->{-upfx} = '../'; } - my @title; - my $v; - if (defined($v = $hdr->header('From'))) { + my @title; # (Subject[0], From[0]) + for my $v ($hdr->header('From')) { $v = PublicInbox::Hval->new($v); my @n = PublicInbox::Address::names($v->raw); - $title[1] = ascii_html(join(', ', @n)); + $title[1] //= ascii_html(join(', ', @n)); $v = $v->as_html; if ($obfs_ibx) { obfuscate_addrs($obfs_ibx, $v); @@ -657,26 +656,31 @@ sub _msg_html_prepare { $rv .= "From: $v\n" if $v ne ''; } foreach my $h (qw(To Cc)) { - defined($v = $hdr->header($h)) or next; - fold_addresses($v); - $v = ascii_html($v); - obfuscate_addrs($obfs_ibx, $v) if $obfs_ibx; - $rv .= "$h: $v\n" if $v ne ''; + for my $v ($hdr->header($h)) { + fold_addresses($v); + $v = ascii_html($v); + obfuscate_addrs($obfs_ibx, $v) if $obfs_ibx; + $rv .= "$h: $v\n" if $v ne ''; + } } - if (defined($v = $hdr->header('Subject')) && ($v ne '')) { - $v = ascii_html($v); - obfuscate_addrs($obfs_ibx, $v) if $obfs_ibx; - if ($over) { - $rv .= qq(Subject: $v\n); - } else { - $rv .= "Subject: $v\n"; + my @subj = $hdr->header('Subject'); + if (@subj) { + for my $v (@subj) { + $v = ascii_html($v); + obfuscate_addrs($obfs_ibx, $v) if $obfs_ibx; + $rv .= 'Subject: '; + if ($over) { + $rv .= qq($v\n); + } else { + $rv .= "$v\n"; + } + $title[0] //= $v; } - $title[0] = $v; } else { # dummy anchor for thread skeleton at bottom of page $rv .= qq() if $over; $title[0] = '(no subject)'; } - if (defined($v = $hdr->header('Date'))) { + for my $v ($hdr->header('Date')) { $v = ascii_html($v); obfuscate_addrs($obfs_ibx, $v) if $obfs_ibx; # possible :P $rv .= "Date: $v\n"; @@ -727,8 +731,9 @@ sub thread_skel { $$dst .= "$nr+ messages / $expand"; $$dst .= qq! top\n!; - my $subj = $hdr->header('Subject'); - defined $subj or $subj = ''; + # nb: mutt only shows the first Subject in the index pane + # when multiple Subject: headers are present, so we follow suit: + my $subj = $hdr->header('Subject') // ''; $subj = '(no subject)' if $subj eq ''; $ctx->{prev_subj} = [ split(/ /, subject_normalized($subj)) ]; $ctx->{cur} = $mid; @@ -746,21 +751,29 @@ sub thread_skel { sub _parent_headers { my ($hdr, $over) = @_; my $rv = ''; - - my $refs = references($hdr); - my $irt = pop @$refs; - if (defined $irt) { - my $v = PublicInbox::Hval->new_msgid($irt); - my $html = $v->as_html; - my $href = $v->{href}; - $rv .= "In-Reply-To: <"; - $rv .= "$html>\n"; + my @irt = $hdr->header_raw('In-Reply-To'); + my $refs; + if (@irt) { + my $lnk = PublicInbox::Linkify->new; + $rv .= "In-Reply-To: $_\n" for @irt; + $lnk->linkify_mids('..', \$rv); + } else { + $refs = references($hdr); + my $irt = pop @$refs; + if (defined $irt) { + my $v = PublicInbox::Hval->new_msgid($irt); + my $html = $v->as_html; + my $href = $v->{href}; + $rv .= "In-Reply-To: <"; + $rv .= "$html>\n"; + } } # do not display References: if search is present, # we show the thread skeleton at the bottom, instead. return $rv if $over; + $refs //= references($hdr); if (@$refs) { @$refs = map { linkify_ref_no_over($_) } @$refs; $rv .= 'References: '. join("\n\t", @$refs) . "\n"; -- 2.44.0