]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/View.pm
view: do not redundantly obfuscate addresses
[public-inbox.git] / lib / PublicInbox / View.pm
index e586b068fce8fdd643ba5de61505fbee7582fc1b..33b323dce248670dc1da248b795419470b4c0019 100644 (file)
@@ -8,9 +8,10 @@ use strict;
 use warnings;
 use bytes (); # only for bytes::length
 use PublicInbox::MsgTime qw(msg_datestamp);
-use PublicInbox::Hval qw(ascii_html obfuscate_addrs prurl);
+use PublicInbox::Hval qw(ascii_html obfuscate_addrs prurl mid_href);
 use PublicInbox::Linkify;
-use PublicInbox::MID qw/id_compress mid_escape mids mids_for_index references/;
+use PublicInbox::MID qw(id_compress mids mids_for_index references
+                       $MID_EXTRACT);
 use PublicInbox::MsgIter;
 use PublicInbox::Address;
 use PublicInbox::WwwStream;
@@ -18,7 +19,7 @@ use PublicInbox::Reply;
 use PublicInbox::ViewDiff qw(flush_diff);
 use POSIX qw(strftime);
 use Time::Local qw(timegm);
-use PublicInbox::SearchMsg qw(subject_normalized);
+use PublicInbox::Smsg qw(subject_normalized);
 use constant COLS => 72;
 use constant INDENT => '  ';
 use constant TCHILD => '` ';
@@ -26,22 +27,14 @@ sub th_pfx ($) { $_[0] == 0 ? '' : TCHILD };
 
 sub msg_page_i {
        my ($nr, $ctx) = @_;
-       my $more = $ctx->{more};
-       if ($nr == 1) {
-               # $more cannot be true w/o $smsg being defined:
-               $ctx->{mhref} = $more ? '../'.mid_escape($ctx->{smsg}->mid).'/'
-                                     : '';
-               multipart_text_as_html(delete $ctx->{mime}, $ctx);
-               ${delete $ctx->{obuf}} .= '</pre><hr>';
-       } elsif ($more) {
-               ++$ctx->{end_nr};
-               # fake an EOF if {more} retrieval fails fails;
-               eval { msg_page_more($ctx, $nr) };
-       } elsif ($nr == $ctx->{end_nr}) {
+       if (my $more = delete $ctx->{more}) { # unlikely
+               # fake an EOF if $more retrieval fails;
+               eval { msg_page_more($ctx, $nr, @$more) };
+       } elsif (my $hdr = delete $ctx->{hdr}) {
                # fake an EOF if generating the footer fails;
                # we want to at least show the message if something
                # here crashes:
-               eval { html_footer($ctx) };
+               eval { html_footer($ctx, $hdr) };
        } else {
                undef
        }
@@ -53,40 +46,37 @@ sub msg_page {
        my ($ctx) = @_;
        my $mid = $ctx->{mid};
        my $ibx = $ctx->{-inbox};
-       my ($first);
-       my $smsg;
+       my ($smsg, $first, $next);
        if (my $over = $ibx->over) {
                my ($id, $prev);
-               $smsg = $over->next_by_mid($mid, \$id, \$prev);
-               $first = $ibx->msg_by_smsg($smsg) if $smsg;
-               if ($first) {
-                       my $next = $over->next_by_mid($mid, \$id, \$prev);
-                       $ctx->{more} = [ $id, $prev, $next ] if $next;
-               }
-               return unless $first;
+               $smsg = $over->next_by_mid($mid, \$id, \$prev) or return;
+               $first = $ibx->msg_by_smsg($smsg) or return;
+               $next = $over->next_by_mid($mid, \$id, \$prev);
+               $ctx->{more} = [ $id, $prev, $next ] if $next;
        } else {
                $first = $ibx->msg_by_mid($mid) or return;
        }
-       my $mime = $ctx->{mime} = PublicInbox::MIME->new($first);
+       my $mime = PublicInbox::MIME->new($first);
        $ctx->{-obfs_ibx} = $ibx->{obfuscate} ? $ibx : undef;
        my $hdr = $ctx->{hdr} = $mime->header_obj;
-       _msg_page_prepare_obuf($hdr, $ctx, 0);
-       $ctx->{end_nr} = 2;
+       $ctx->{obuf} = _msg_page_prepare_obuf($hdr, $ctx, 0);
        $ctx->{smsg} = $smsg;
+       # $next cannot be true w/o $smsg being defined:
+       $ctx->{mhref} = $next ? '../'.mid_href($smsg->{mid}).'/' : '';
+       multipart_text_as_html($mime, $ctx);
+       $ctx->{-html_tip} = (${delete $ctx->{obuf}} .= '</pre><hr>');
        PublicInbox::WwwStream->response($ctx, 200, \&msg_page_i);
 }
 
-sub msg_page_more {
-       my ($ctx, $nr) = @_;
-       my ($id, $prev, $smsg) = @{$ctx->{more}};
+sub msg_page_more { # cold
+       my ($ctx, $nr, $id, $prev, $smsg) = @_;
        my $ibx = $ctx->{-inbox};
-       $smsg = $ibx->smsg_mime($smsg);
        my $next = $ibx->over->next_by_mid($ctx->{mid}, \$id, \$prev);
-       $ctx->{more} = $next ? [ $id, $prev, $next ] : undef;
-       return '' unless $smsg;
-       $ctx->{mhref} = '../' . mid_escape($smsg->mid) . '/';
+       $ctx->{more} = [ $id, $prev, $next ] if $next;
+       $smsg = $ibx->smsg_mime($smsg) or return '';
+       $ctx->{mhref} = '../' . mid_href($smsg->{mid}) . '/';
        my $mime = delete $smsg->{mime};
-       _msg_page_prepare_obuf($mime->header_obj, $ctx, $nr);
+       $ctx->{obuf} = _msg_page_prepare_obuf($mime->header_obj, $ctx, $nr);
        multipart_text_as_html($mime, $ctx);
        ${delete $ctx->{obuf}} .= '</pre><hr>';
 }
@@ -220,7 +210,7 @@ sub index_entry {
        obfuscate_addrs($obfs_ibx, $from) if $obfs_ibx;
        $rv .= "From: $from @ ".fmt_ts($ds)." UTC";
        my $upfx = $ctx->{-upfx};
-       my $mhref = $upfx . mid_escape($mid_raw) . '/';
+       my $mhref = $upfx . mid_href($mid_raw) . '/';
        $rv .= qq{ (<a\nhref="$mhref">permalink</a> / };
        $rv .= qq{<a\nhref="${mhref}raw">raw</a>)\n};
        my $to = fold_addresses(_hdr_names_html($hdr, 'To'));
@@ -244,9 +234,8 @@ sub index_entry {
 
        my $mapping = $ctx->{mapping};
        if (!$mapping && (defined($irt) || defined($irt = in_reply_to($hdr)))) {
-               my $mirt = PublicInbox::Hval->new_msgid($irt);
-               my $href = $upfx . $mirt->{href}. '/';
-               my $html = $mirt->as_html;
+               my $href = $upfx . mid_href($irt) . '/';
+               my $html = ascii_html($irt);
                $rv .= qq(In-Reply-To: &lt;<a\nhref="$href">$html</a>&gt;\n)
        }
        $rv .= "\n";
@@ -311,7 +300,7 @@ sub _th_index_lite {
        if (my $smsg = $node->{smsg}) {
                # delete saves about 200KB on a 1K message thread
                if (my $refs = delete $smsg->{references}) {
-                       ($$irt) = ($refs =~ m/<([^>]+)>\z/);
+                       ($$irt) = ($refs =~ m/$MID_EXTRACT\z/o);
                }
        }
        my $irt_map = $mapping->{$$irt} if defined $$irt;
@@ -423,7 +412,7 @@ sub stream_thread ($$) {
        return missing_thread($ctx) unless $smsg;
 
        $ctx->{-obfs_ibx} = $ibx->{obfuscate} ? $ibx : undef;
-       $ctx->{-title_html} = ascii_html($smsg->subject);
+       $ctx->{-title_html} = ascii_html($smsg->{subject});
        $ctx->{-html_tip} = thread_index_entry($ctx, $level, $smsg);
        $ctx->{-queue} = \@q;
        PublicInbox::WwwStream->response($ctx, 200, \&stream_thread_i);
@@ -469,7 +458,7 @@ sub thread_html {
                $smsg = $ibx->smsg_mime($m) and last;
        }
        return missing_thread($ctx) unless $smsg;
-       $ctx->{-title_html} = ascii_html($smsg->subject);
+       $ctx->{-title_html} = ascii_html($smsg->{subject});
        $ctx->{-html_tip} = '<pre>'.index_entry($smsg, $ctx, scalar @$msgs);
        $ctx->{msgs} = $msgs;
        PublicInbox::WwwStream->response($ctx, 200, \&thread_html_i);
@@ -493,17 +482,6 @@ sub multipart_text_as_html {
        msg_iter($_[0], \&add_text_body, $_[1], 1);
 }
 
-sub flush_quote {
-       my ($s, $l, $quot) = @_;
-
-       my $rv = $l->to_html($$quot);
-
-       # we use a <span> here to allow users to specify their own
-       # color for quoted text
-       $$quot = undef;
-       $$s .= qq(<span\nclass="q">) . $rv . '</span>'
-}
-
 sub attach_link ($$$$;$) {
        my ($ctx, $ct, $p, $fn, $err) = @_;
        my ($part, $depth, @idx) = @$p;
@@ -515,9 +493,6 @@ sub attach_link ($$$$;$) {
        # spotting MUA problems:
        $ct =~ s/;.*// unless $err;
        $ct = ascii_html($ct);
-       my $desc = $part->header('Content-Description');
-       $desc = $fn unless defined $desc;
-       $desc = '' unless defined $desc;
        my $sfn;
        if (defined $fn && $fn =~ /\A$PublicInbox::Hval::FN\z/o) {
                $sfn = $fn;
@@ -529,10 +504,13 @@ sub attach_link ($$$$;$) {
        my $rv = $ctx->{obuf};
        $$rv .= qq($nl<a\nhref="$ctx->{mhref}$idx-$sfn">);
        if ($err) {
-               $$rv .= "[-- Warning: decoded text below may be mangled --]\n";
+               $$rv .= <<EOF;
+[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
+EOF
        }
        $$rv .= "[-- Attachment #$idx: ";
        my $ts = "Type: $ct, Size: $size bytes";
+       my $desc = $part->header('Content-Description') // $fn // '';
        $desc = ascii_html($desc);
        $$rv .= ($desc eq '') ? "$ts --]" : "$desc --]\n[-- $ts --]";
        $$rv .= "</a>\n";
@@ -554,6 +532,9 @@ sub add_text_body { # callback for msg_iter
        # link generation in diffs with the extra '%0D'
        $s =~ s/\r\n/\n/sg;
 
+       # will be escaped to `&#8226;' in HTML
+       obfuscate_addrs($ibx, $s, "\x{2022}") if $ibx->{obfuscate};
+
        # always support diff-highlighting, but we can't linkify hunk
        # headers for solver unless some coderepo are configured:
        my $diff;
@@ -587,8 +568,8 @@ sub add_text_body { # callback for msg_iter
        $s .= "\n" unless $s =~ /\n\z/s;
 
        # split off quoted and unquoted blocks:
-       my @sections = split(/((?:^>[^\n]*\n)+)/sm, $s);
-       $s = '';
+       my @sections = PublicInbox::MsgIter::split_quotes($s);
+       undef $s; # free memory
        my $rv = $ctx->{obuf};
        if (defined($fn) || $depth > 0 || $err) {
                # badly-encoded message with $err? tell the world about it!
@@ -598,17 +579,19 @@ sub add_text_body { # callback for msg_iter
        my $l = $ctx->{-linkify} //= PublicInbox::Linkify->new;
        foreach my $cur (@sections) {
                if ($cur =~ /\A>/) {
-                       flush_quote($rv, $l, \$cur);
+                       # we use a <span> here to allow users to specify
+                       # their own color for quoted text
+                       $$rv .= qq(<span\nclass="q">);
+                       $$rv .= $l->to_html($cur);
+                       $$rv .= '</span>';
                } elsif ($diff) {
-                       flush_diff($rv, $ctx, \$cur);
+                       flush_diff($ctx, \$cur);
                } else {
                        # regular lines, OK
                        $$rv .= $l->to_html($cur);
-                       $cur = undef;
                }
+               undef $cur; # free memory
        }
-
-       obfuscate_addrs($ibx, $$rv) if $ibx->{obfuscate};
 }
 
 sub _msg_page_prepare_obuf {
@@ -631,10 +614,9 @@ sub _msg_page_prepare_obuf {
        }
        my @title; # (Subject[0], From[0])
        for my $v ($hdr->header('From')) {
-               $v = PublicInbox::Hval->new($v);
-               my @n = PublicInbox::Address::names($v->raw);
+               my @n = PublicInbox::Address::names($v);
+               $v = ascii_html($v);
                $title[1] //= ascii_html(join(', ', @n));
-               $v = $v->as_html;
                if ($obfs_ibx) {
                        obfuscate_addrs($obfs_ibx, $v);
                        obfuscate_addrs($obfs_ibx, $title[1]);
@@ -672,8 +654,7 @@ sub _msg_page_prepare_obuf {
        }
        $ctx->{-title_html} = join(' - ', @title);
        if (scalar(@$mids) == 1) { # common case
-               my $mid = PublicInbox::Hval->new_msgid($mids->[0]);
-               my $mhtml = $mid->as_html;
+               my $mhtml = ascii_html($mids->[0]);
                $rv .= "Message-ID: &lt;$mhtml&gt; ";
                $rv .= "(<a\nhref=\"raw\">raw</a>)\n";
        } else {
@@ -689,7 +670,7 @@ sub _msg_page_prepare_obuf {
        }
        $rv .= _parent_headers($hdr, $over);
        $rv .= "\n";
-       $ctx->{obuf} = \$rv;
+       \$rv;
 }
 
 sub SKEL_EXPAND () {
@@ -751,9 +732,8 @@ sub _parent_headers {
                $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};
+                       my $html = ascii_html($irt);
+                       my $href = mid_href($irt);
                        $rv .= "In-Reply-To: &lt;";
                        $rv .= "<a\nhref=\"../$href/\">$html</a>&gt;\n";
                }
@@ -773,9 +753,8 @@ sub _parent_headers {
 
 # returns a string buffer via ->getline
 sub html_footer {
-       my ($ctx) = @_;
+       my ($ctx, $hdr) = @_;
        my $ibx = $ctx->{-inbox};
-       my $hdr = delete $ctx->{hdr};
        my $upfx = '../';
        my $skel = " <a\nhref=\"$upfx\">index</a>";
        my $rv = '<pre>';
@@ -787,17 +766,17 @@ sub html_footer {
                $next = $prev = '    ';
 
                if (my $n = $ctx->{next_msg}) {
-                       $n = PublicInbox::Hval->new_msgid($n)->{href};
+                       $n = mid_href($n);
                        $next = "<a\nhref=\"$upfx$n/\"\nrel=next>next</a>";
                }
                my $u;
                my $par = $ctx->{parent_msg};
                if ($par) {
-                       $u = PublicInbox::Hval->new_msgid($par)->{href};
+                       $u = mid_href($par);
                        $u = "$upfx$u/";
                }
                if (my $p = $ctx->{prev_msg}) {
-                       $prev = PublicInbox::Hval->new_msgid($p)->{href};
+                       $prev = mid_href($p);
                        if ($p && $par && $p eq $par) {
                                $prev = "<a\nhref=\"$upfx$prev/\"\n" .
                                        'rel=prev>prev parent</a>';
@@ -819,9 +798,9 @@ sub html_footer {
 }
 
 sub linkify_ref_no_over {
-       my $v = PublicInbox::Hval->new_msgid($_[0]);
-       my $html = $v->as_html;
-       my $href = $v->{href};
+       my ($mid) = @_;
+       my $href = mid_href($mid);
+       my $html = ascii_html($mid);
        "&lt;<a\nhref=\"../$href/\">$html</a>&gt;";
 }
 
@@ -833,9 +812,8 @@ sub anchor_for {
 sub ghost_parent {
        my ($upfx, $mid) = @_;
 
-       $mid = PublicInbox::Hval->new_msgid($mid);
-       my $href = $mid->{href};
-       my $html = $mid->as_html;
+       my $href = mid_href($mid);
+       my $html = ascii_html($mid);
        qq{[parent not found: &lt;<a\nhref="$upfx$href/">$html</a>&gt;]};
 }
 
@@ -904,20 +882,20 @@ sub missing_thread {
 sub dedupe_subject {
        my ($prev_subj, $subj, $val) = @_;
 
-       my $omit = ''; # '"' denotes identical text omitted
+       my $omit; # '"' denotes identical text omitted
        my (@prev_pop, @curr_pop);
        while (@$prev_subj && @$subj && $subj->[-1] eq $prev_subj->[-1]) {
                push(@prev_pop, pop(@$prev_subj));
                push(@curr_pop, pop(@$subj));
-               $omit ||= $val;
+               $omit //= $val;
        }
        pop @$subj if @$subj && $subj->[-1] =~ /^re:\s*/i;
        if (scalar(@curr_pop) == 1) {
-               $omit = '';
+               $omit = undef;
                push @$prev_subj, @prev_pop;
                push @$subj, @curr_pop;
        }
-       $omit;
+       $omit // '';
 }
 
 sub skel_dump { # walk_thread callback
@@ -932,7 +910,7 @@ sub skel_dump { # walk_thread callback
                $$skel .= delete($ctx->{sl_note}) || '';
        }
 
-       my $f = ascii_html($smsg->from_name);
+       my $f = ascii_html($smsg->{from_name});
        my $obfs_ibx = $ctx->{-obfs_ibx};
        obfuscate_addrs($obfs_ibx, $f) if $obfs_ibx;
 
@@ -971,7 +949,7 @@ sub skel_dump { # walk_thread callback
        # Subject is never undef, this mail was loaded from
        # our Xapian which would've resulted in '' if it were
        # really missing (and Filter rejects empty subjects)
-       my @subj = split(/ /, subject_normalized($smsg->subject));
+       my @subj = split(/ /, subject_normalized($smsg->{subject}));
        # remove common suffixes from the subject if it matches the previous,
        # so we do not show redundant text at the end.
        my $prev_subj = $ctx->{prev_subj} || [];
@@ -996,7 +974,7 @@ sub skel_dump { # walk_thread callback
                $map->[0] = "$d<a\nhref=\"$m\">$end";
                $id = "\nid=r".$id;
        } else {
-               $m = $ctx->{-upfx}.mid_escape($mid).'/';
+               $m = $ctx->{-upfx}.mid_href($mid).'/';
        }
        $$skel .=  $d . "<a\nhref=\"$m\"$id>" . $end;
        1;
@@ -1010,9 +988,8 @@ sub _skel_ghost {
        $d .= '    '  if exists $ctx->{searchview};
        $d .= indent_for($level) . th_pfx($level);
        my $upfx = $ctx->{-upfx};
-       my $m = PublicInbox::Hval->new_msgid($mid);
-       my $href = $upfx . $m->{href} . '/';
-       my $html = $m->as_html;
+       my $href = $upfx . mid_href($mid) . '/';
+       my $html = ascii_html($mid);
 
        my $mapping = $ctx->{mapping};
        my $map = $mapping->{$mid} if $mapping;
@@ -1029,8 +1006,8 @@ sub _skel_ghost {
 
 sub sort_ds {
        [ sort {
-               (eval { $a->topmost->{smsg}->ds } || 0) <=>
-               (eval { $b->topmost->{smsg}->ds } || 0)
+               (eval { $a->topmost->{smsg}->{ds} } || 0) <=>
+               (eval { $b->topmost->{smsg}->{ds} } || 0)
        } @{$_[0]} ];
 }
 
@@ -1041,7 +1018,7 @@ sub acc_topic { # walk_thread callback
        my $mid = $node->{id};
        my $smsg = $node->{smsg} // $ctx->{-inbox}->smsg_by_mid($mid);
        if ($smsg) {
-               my $subj = subject_normalized($smsg->subject);
+               my $subj = subject_normalized($smsg->{subject});
                $subj = '(no subject)' if $subj eq '';
                my $ds = $smsg->{ds};
                if ($level == 0) { # new, top-level topic
@@ -1073,7 +1050,7 @@ sub acc_topic { # walk_thread callback
 sub dump_topics {
        my ($ctx) = @_;
        my $order = delete $ctx->{order}; # [ ds, subj1, subj2, subj3, ... ]
-       if (!@$order) {
+       unless ($order) {
                $ctx->{-html_tip} = '<pre>[No topics in range]</pre>';
                return 404;
        }
@@ -1088,9 +1065,9 @@ sub dump_topics {
                @$topic = ();
                next unless defined $top_subj;  # ghost topic
                my $mid = delete $seen->{$top_subj};
-               my $href = mid_escape($mid);
+               my $href = mid_href($mid);
                my $prev_subj = [ split(/ /, $top_subj) ];
-               $top_subj = PublicInbox::Hval->new($top_subj)->as_html;
+               $top_subj = ascii_html($top_subj);
                $ds = fmt_ts($ds);
 
                # $n isn't the total number of posts on the topic,
@@ -1118,7 +1095,7 @@ sub dump_topics {
                        $prev_subj = \@next_prev;
                        $subj = ascii_html($subj);
                        obfuscate_addrs($obfs_ibx, $subj) if $obfs_ibx;
-                       $href = mid_escape($mid);
+                       $href = mid_href($mid);
                        $s .= indent_for($level) . TCHILD;
                        $s .= qq(<a\nhref="$href/T/#u">$subj</a>$omit\n);
                }