-# Copyright (C) 2014-2018 all contributors <meta@public-inbox.org>
+# Copyright (C) 2014-2019 all contributors <meta@public-inbox.org>
# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
#
# Used for displaying the HTML web interface.
use PublicInbox::ViewDiff qw(flush_diff);
require POSIX;
use Time::Local qw(timegm);
-
+use PublicInbox::SearchMsg qw(subject_normalized);
use constant COLS => 72;
use constant INDENT => ' ';
use constant TCHILD => '` ';
my $ibx = $ctx->{-inbox};
my ($first, $more);
my $smsg;
- if (my $srch = $ibx->search) {
+ if (my $over = $ibx->over) {
my ($id, $prev);
- $smsg = $srch->next_by_mid($mid, \$id, \$prev);
+ $smsg = $over->next_by_mid($mid, \$id, \$prev);
$first = $ibx->msg_by_smsg($smsg) if $smsg;
if ($first) {
- my $next = $srch->next_by_mid($mid, \$id, \$prev);
+ my $next = $over->next_by_mid($mid, \$id, \$prev);
$more = [ $id, $prev, $next ] if $next;
}
return unless $first;
my $mid = $ctx->{mid};
my $ibx = $ctx->{-inbox};
$smsg = $ibx->smsg_mime($smsg);
- my $next = $ctx->{srch}->next_by_mid($mid, \$id, \$prev);
+ my $next = $ibx->over->next_by_mid($mid, \$id, \$prev);
@$more = $next ? ($id, $prev, $next) : ();
if ($smsg) {
my $mime = $smsg->{mime};
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 ($$$) {
# this is already inside a <pre>
sub index_entry {
my ($smsg, $ctx, $more) = @_;
- my $srch = $ctx->{srch};
my $subj = $smsg->subject;
my $mid_raw = $smsg->mid;
my $id = id_compress($mid_raw, 1);
sub thread_html {
my ($ctx) = @_;
my $mid = $ctx->{mid};
- my $srch = $ctx->{srch};
- my ($nr, $msgs) = $srch->get_thread($mid);
+ my $ibx = $ctx->{-inbox};
+ my ($nr, $msgs) = $ibx->over->get_thread($mid);
return missing_thread($ctx) if $nr == 0;
my $skel = '<hr><pre>';
$skel .= $nr == 1 ? 'only message in thread' : 'end of thread';
my $rootset = thread_results($ctx, $msgs);
# reduce hash lookups in pre_thread->skel_dump
- my $ibx = $ctx->{-inbox};
$ctx->{-obfs_ibx} = $ibx->{obfuscate} ? $ibx : undef;
walk_thread($rootset, $ctx, *pre_thread);
$desc = $fn unless defined $desc;
$desc = '' unless defined $desc;
my $sfn;
- if (defined $fn && $fn =~ /\A[[:alnum:]][\w\.-]+[[:alnum:]]\z/) {
+ if (defined $fn && $fn =~ /\A$PublicInbox::Hval::FN\z/o) {
$sfn = $fn;
} elsif ($ct eq 'text/plain') {
$sfn = 'a.txt';
sub _msg_html_prepare {
my ($hdr, $ctx, $more, $nr) = @_;
- my $srch = $ctx->{srch} if $ctx;
my $atom = '';
+ my $over = $ctx->{-inbox}->over;
my $obfs_ibx = $ctx->{-obfs_ibx};
my $rv = '';
my $mids = mids($hdr);
} else {
$rv .= '<pre>';
}
- if ($srch) {
+ 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);
$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 ($srch) {
- $rv .= qq(Subject: <a\nhref="#r"\nid=t>$v</a>\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(<a\nhref="#r"\nid=t>$v</a>\n);
+ } else {
+ $rv .= "$v\n";
+ }
+ $title[0] //= $v;
}
- $title[0] = $v;
} else { # dummy anchor for thread skeleton at bottom of page
- $rv .= qq(<a\nhref="#r"\nid=t></a>) if $srch;
+ $rv .= qq(<a\nhref="#r"\nid=t></a>) 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";
$rv .= "(<a\nhref=\"raw\">raw</a>)\n";
}
}
- $rv .= _parent_headers($hdr, $srch);
+ $rv .= _parent_headers($hdr, $over);
$rv .= "\n";
}
sub thread_skel {
my ($dst, $ctx, $hdr, $tpfx) = @_;
- my $srch = $ctx->{srch};
my $mid = mids($hdr)->[0];
- my ($nr, $msgs) = $srch->get_thread($mid);
+ my $ibx = $ctx->{-inbox};
+ my ($nr, $msgs) = $ibx->over->get_thread($mid);
my $expand = qq(expand[<a\nhref="${tpfx}T/#u">flat</a>) .
qq(|<a\nhref="${tpfx}t/#u">nested</a>] ) .
qq(<a\nhref="${tpfx}t.mbox.gz">mbox.gz</a> ) .
$$dst .= "$nr+ messages / $expand";
$$dst .= qq! <a\nhref="#b">top</a>\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(/ /, $srch->subject_normalized($subj)) ];
+ $ctx->{prev_subj} = [ split(/ /, subject_normalized($subj)) ];
$ctx->{cur} = $mid;
$ctx->{prev_attr} = '';
$ctx->{prev_level} = 0;
$ctx->{dst} = $dst;
# reduce hash lookups in skel_dump
- my $ibx = $ctx->{-inbox};
$ctx->{-obfs_ibx} = $ibx->{obfuscate} ? $ibx : undef;
walk_thread(thread_results($ctx, $msgs), $ctx, *skel_dump);
}
sub _parent_headers {
- my ($hdr, $srch) = @_;
+ 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 .= "<a\nhref=\"../$href/\">$html</a>>\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 .= "<a\nhref=\"../$href/\">$html</a>>\n";
+ }
}
# do not display References: if search is present,
# we show the thread skeleton at the bottom, instead.
- return $rv if $srch;
+ return $rv if $over;
+ $refs //= references($hdr);
if (@$refs) {
- @$refs = map { linkify_ref_nosrch($_) } @$refs;
+ @$refs = map { linkify_ref_no_over($_) } @$refs;
$rv .= 'References: '. join("\n\t", @$refs) . "\n";
}
$rv;
sub html_footer {
my ($hdr, $standalone, $ctx, $rhref) = @_;
- my $srch = $ctx->{srch} if $ctx;
+ my $ibx = $ctx->{-inbox} if $ctx;
my $upfx = '../';
my $tpfx = '';
my $idx = $standalone ? " <a\nhref=\"$upfx\">index</a>" : '';
my $irt = '';
- if ($idx && $srch) {
+ if ($idx && $ibx->over) {
$idx .= "\n";
thread_skel(\$idx, $ctx, $hdr, $tpfx);
my ($next, $prev);
$irt .= $idx;
}
-sub linkify_ref_nosrch {
+sub linkify_ref_no_over {
my $v = PublicInbox::Hval->new_msgid($_[0]);
my $html = $v->as_html;
my $href = $v->{href};
# 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(/ /, $ctx->{srch}->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.
# returns 200 if done, 404 if not
sub acc_topic {
my ($ctx, $level, $node) = @_;
- my $srch = $ctx->{srch};
my $mid = $node->{id};
my $x = $node->{smsg} || $ctx->{-inbox}->smsg_by_mid($mid);
my ($subj, $ds);
my $topic;
if ($x) {
$subj = $x->subject;
- $subj = $srch->subject_normalized($subj);
+ $subj = subject_normalized($subj);
$subj = '(no subject)' if $subj eq '';
$ds = $x->ds;
if ($level == 0) {
my @out;
my $ibx = $ctx->{-inbox};
my $obfs_ibx = $ibx->{obfuscate} ? $ibx : undef;
- my $srch = $ctx->{srch};
# sort by recency, this allows new posts to "bump" old topics...
foreach my $topic (sort { $b->[0] <=> $a->[0] } @$order) {
my $level = $ex[$i];
my $subj = $ex[$i + 1];
$mid = delete $seen->{$subj};
- my @subj = split(/ /, $srch->subject_normalized($subj));
+ my @subj = split(/ /, subject_normalized($subj));
my @next_prev = @subj; # full copy
my $omit = dedupe_subject($prev_subj, \@subj, ' "');
$prev_subj = \@next_prev;
# Xapian uses '..' but '-' is perhaps friendier to URL linkifiers
# if only $after exists "YYYYMMDD.." because "." could be skipped
# if interpreted as an end-of-sentence
- $t =~ s/\A(\d{8,14})-// and $after = str2ts($1);
- $t =~ /\A(\d{8,14})\z/ and $before = str2ts($1);
+ $t =~ s/\A([0-9]{8,14})-// and $after = str2ts($1);
+ $t =~ /\A([0-9]{8,14})\z/ and $before = str2ts($1);
my $ibx = $ctx->{-inbox};
my $msgs = $ibx->recent($opts, $after, $before);