You may reply publically to this message via
-plain-text email using any one of the following methods:
+
Reply instructions:
+
+You may reply publically to this message via plain-text email
+using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
$p_url
-
+$info
* Reply to all the recipients using the --to, --cc,
and --in-reply-to switches of git-send-email(1):
@@ -86,7 +94,7 @@ sub in_reply_to {
my ($hdr) = @_;
my $irt = $hdr->header_raw('In-Reply-To');
- return mid_clean($irt) if (defined $irt);
+ return mid_clean($irt) if defined $irt && $irt ne '';
my $refs = $hdr->header_raw('References');
if ($refs && $refs =~ /<([^>]+)>\s*\z/s) {
@@ -117,7 +125,6 @@ sub index_entry {
my $mid_raw = mid_clean(mid_mime($mime));
my $id = id_compress($mid_raw, 1);
my $id_m = 'm'.$id;
- my $mid = PublicInbox::Hval->new_msgid($mid_raw);
my $root_anchor = $ctx->{root_anchor} || '';
my $irt = in_reply_to($hdr);
@@ -134,7 +141,7 @@ sub index_entry {
}
$rv .= "From: "._hdr_names($hdr, 'From').' @ '._msg_date($hdr)." UTC";
my $upfx = $ctx->{-upfx};
- my $mhref = $upfx . $mid->as_href . '/';
+ my $mhref = $upfx . mid_escape($mid_raw) . '/';
$rv .= qq{ (permalink / };
$rv .= qq{raw)\n};
$rv .= ' '.join('; +', @tocc) . "\n" if @tocc;
@@ -142,7 +149,7 @@ sub index_entry {
my $mapping = $ctx->{mapping};
if (!$mapping && $irt) {
my $mirt = PublicInbox::Hval->new_msgid($irt);
- my $href = $upfx . $mirt->as_href . '/';
+ my $href = $upfx . $mirt->{href}. '/';
my $html = $mirt->as_html;
$rv .= qq(In-Reply-To: <$html>\n)
}
@@ -156,13 +163,17 @@ sub index_entry {
"permalink" .
" raw" .
" reply";
+
+ my $hr;
if (my $pct = $ctx->{pct}) { # used by SearchView.pm
$rv .= "\t[relevance $pct->{$mid_raw}%]";
+ $hr = 1;
} elsif ($mapping) {
my $threaded = 'threaded';
my $flat = 'flat';
my $end = '';
if ($ctx->{flat}) {
+ $hr = 1;
$flat = "$flat";
} else {
$threaded = "$threaded";
@@ -170,9 +181,12 @@ sub index_entry {
$rv .= "\t[$flat";
$rv .= "|$threaded]";
$rv .= " $ctx->{s_nr}";
+ } else {
+ $hr = $ctx->{-hr};
}
- $rv .= $more ? "\n\n" : "\n";
+ $rv .= $more ? '
' : '
' if $hr;
+ $rv;
}
sub pad_link ($$;$) {
@@ -193,8 +207,8 @@ sub _th_index_lite {
my $nr_s = 0;
my $level = $map->[4];
my $idx = $map->[3];
- if (defined $irt) {
- my $irt_map = $mapping->{$irt};
+ my $irt_map = $mapping->{$irt} if defined $irt;
+ if (defined $irt_map) {
my $siblings = $irt_map->[0];
$nr_s = scalar(@$siblings) - 1;
$rv .= $pad . $irt_map->[1];
@@ -320,10 +334,12 @@ sub thread_html {
return missing_thread($ctx) if $nr == 0;
my $skel = '
';
$skel .= $nr == 1 ? 'only message in thread' : 'end of thread';
- $skel .= ", back to index";
- $skel .= "\n$nr+ messages in thread: (download: ";
- $skel .= "mbox.gz";
+ $skel .= ", back to index\n\n";
+ $skel .= "Thread overview: ";
+ $skel .= $nr == 1 ? '(only message)' : "$nr+ messages";
+ $skel .= " (download: mbox.gz";
$skel .= " / follow: Atom feed)\n";
+ $skel .= "-- links below jump to the message on this page --\n";
$ctx->{-upfx} = '../../';
$ctx->{cur_level} = 0;
$ctx->{dst} = \$skel;
@@ -361,7 +377,7 @@ sub thread_html {
return index_entry($mime, $ctx, scalar @$msgs);
}
$msgs = undef;
- '
'.$skel;
+ $skel;
});
}
@@ -391,14 +407,16 @@ sub flush_quote {
$$s .= qq() . $rv . ''
}
-sub attach_link ($$$$) {
- my ($upfx, $ct, $p, $fn) = @_;
+sub attach_link ($$$$;$) {
+ my ($upfx, $ct, $p, $fn, $err) = @_;
my ($part, $depth, @idx) = @$p;
my $nl = $idx[-1] > 1 ? "\n" : '';
my $idx = join('.', @idx);
my $size = bytes::length($part->body);
- $ct ||= 'text/plain';
- $ct =~ s/;.*//; # no attributes
+
+ # hide attributes normally, unless we want to aid users in
+ # spotting MUA problems:
+ $ct =~ s/;.*// unless $err;
$ct = ascii_html($ct);
my $desc = $part->header('Content-Description');
$desc = $fn unless defined $desc;
@@ -411,31 +429,50 @@ sub attach_link ($$$$) {
} else {
$sfn = 'a.bin';
}
- my @ret = qq($nl[-- Attachment #$idx: );
+ my $ret = qq($nl);
+ if ($err) {
+ $ret .=
+"[-- Warning: decoded text below may be mangled --]\n";
+ }
+ $ret .= "[-- Attachment #$idx: ";
my $ts = "Type: $ct, Size: $size bytes";
- push(@ret, ($desc eq '') ? "$ts --]" : "$desc --]\n[-- $ts --]");
- join('', @ret, "\n");
+ $ret .= ($desc eq '') ? "$ts --]" : "$desc --]\n[-- $ts --]";
+ $ret .= "\n";
}
sub add_text_body {
my ($upfx, $p) = @_; # from msg_iter: [ Email::MIME, depth, @idx ]
my ($part, $depth, @idx) = @$p;
- my $ct = $part->content_type;
+ my $ct = $part->content_type || 'text/plain';
my $fn = $part->filename;
- if (defined $ct && $ct =~ m!\btext/x?html\b!i) {
+ if ($ct =~ m!\btext/x?html\b!i) {
return attach_link($upfx, $ct, $p, $fn);
}
my $s = eval { $part->body_str };
# badly-encoded message? tell the world about it!
- return attach_link($upfx, $ct, $p, $fn) if $@;
+ my $err = $@;
+ if ($err) {
+ if ($ct =~ m!\btext/plain\b!i) {
+ # Try to assume UTF-8 because Alpine seems to
+ # do wacky things and set charset=X-UNKNOWN
+ $part->charset_set('UTF-8');
+ $s = eval { $part->body_str };
+
+ # If forcing charset=UTF-8 failed,
+ # attach_link will warn further down...
+ $s = $part->body if $@;
+ } else {
+ return attach_link($upfx, $ct, $p, $fn);
+ }
+ }
my @lines = split(/^/m, $s);
$s = '';
- if (defined($fn) || $depth > 0) {
- $s .= attach_link($upfx, $ct, $p, $fn);
+ if (defined($fn) || $depth > 0 || $err) {
+ $s .= attach_link($upfx, $ct, $p, $fn, $err);
$s .= "\n";
}
my @quot;
@@ -475,7 +512,7 @@ sub _msg_html_prepare {
$ctx->{-upfx} = '../';
}
my @title;
- my $mid = $hdr->header_raw('Message-ID');
+ my $mid = mid_clean($hdr->header_raw('Message-ID'));
$mid = PublicInbox::Hval->new_msgid($mid);
foreach my $h (qw(From To Cc Subject Date)) {
my $v = $hdr->header($h);
@@ -517,6 +554,7 @@ sub thread_skel {
qq(/ Atom feed);
my $parent = in_reply_to($hdr);
+ $$dst .= "\nThread overview: ";
if ($nr <= 1) {
if (defined $parent) {
$$dst .= "($expand)\n ";
@@ -550,7 +588,7 @@ sub _parent_headers {
if (defined $irt) {
my $v = PublicInbox::Hval->new_msgid($irt);
my $html = $v->as_html;
- my $href = $v->as_href;
+ my $href = $v->{href};
$rv .= "In-Reply-To: <";
$rv .= "$html>\n";
}
@@ -608,15 +646,15 @@ sub mailto_arg_link {
my $subj = $hdr->header('Subject') || '';
$subj = "Re: $subj" unless $subj =~ /\bRe:/i;
my $mid = $hdr->header_raw('Message-ID');
- push @arg, '--in-reply-to='.ascii_html(squote_maybe(mid_clean($mid)));
- my $irt = uri_escape_utf8($mid);
+ push @arg, '--in-reply-to='.squote_maybe(mid_clean($mid));
+ my $irt = mid_escape($mid);
delete $cc{$to};
- push @arg, '--to=' . ascii_html($to);
+ push @arg, "--to=$to";
$to = uri_escape_utf8($to);
$subj = uri_escape_utf8($subj);
- my $cc = join(',', sort values %cc);
- push @arg, '--cc=' . ascii_html($cc);
- $cc = uri_escape_utf8($cc);
+ my @cc = sort values %cc;
+ push(@arg, map { "--cc=$_" } @cc);
+ my $cc = uri_escape_utf8(join(',', @cc));
my $href = "mailto:$to?In-Reply-To=$irt&Cc=${cc}&Subject=$subj";
$href =~ s/%20/+/g;
@@ -639,17 +677,17 @@ sub html_footer {
$next = $prev = ' ';
if (my $n = $ctx->{next_msg}) {
- $n = PublicInbox::Hval->new_msgid($n)->as_href;
+ $n = PublicInbox::Hval->new_msgid($n)->{href};
$next = "next";
}
my $u;
my $par = $ctx->{parent_msg};
if ($par) {
- $u = PublicInbox::Hval->new_msgid($par)->as_href;
+ $u = PublicInbox::Hval->new_msgid($par)->{href};
$u = "$upfx$u/";
}
if (my $p = $ctx->{prev_msg}) {
- $prev = PublicInbox::Hval->new_msgid($p)->as_href;
+ $prev = PublicInbox::Hval->new_msgid($p)->{href};
if ($p && $par && $p eq $par) {
$prev = "prev parent';
@@ -674,7 +712,7 @@ sub html_footer {
sub linkify_ref_nosrch {
my $v = PublicInbox::Hval->new_msgid($_[0]);
my $html = $v->as_html;
- my $href = $v->as_href;
+ my $href = $v->{href};
"<$html>";
}
@@ -685,11 +723,9 @@ sub anchor_for {
sub ghost_parent {
my ($upfx, $mid) = @_;
- # 'subject dummy' is used internally by Mail::Thread
- return '[no common parent]' if ($mid eq 'subject dummy');
$mid = PublicInbox::Hval->new_msgid($mid);
- my $href = $mid->as_href;
+ my $href = $mid->{href};
my $html = $mid->as_html;
qq{[parent not found: <$html>]};
}
@@ -713,8 +749,8 @@ sub msg_timestamp {
sub thread_results {
my ($msgs) = @_;
- require PublicInbox::Thread;
- my $th = PublicInbox::Thread->new(@$msgs);
+ require PublicInbox::SearchThread;
+ my $th = PublicInbox::SearchThread->new($msgs);
$th->thread;
$th->order(*sort_ts);
$th
@@ -775,7 +811,7 @@ sub _skel_header {
$s = PublicInbox::Hval->new($s);
$s = $s->as_html;
}
- my $m = PublicInbox::Hval->new_msgid($mid);
+ my $m;
my $id = '';
my $mapping = $ctx->{mapping};
my $end = defined($s) ? "$s $f\n" : "$f\n";
@@ -786,7 +822,7 @@ sub _skel_header {
$map->[1] = "$d$end";
$id = "\nid=r".$id;
} else {
- $m = $ctx->{-upfx}.$m->as_href.'/';
+ $m = $ctx->{-upfx}.mid_escape($mid).'/';
}
$$dst .= $d . "" . $end;
}
@@ -800,18 +836,12 @@ sub skel_dump {
my $dst = $ctx->{dst};
my $mapping = $ctx->{mapping};
my $map = $mapping->{$mid} if $mapping;
- if ($mid eq 'subject dummy') {
- my $ncp = "\t[no common parent]\n";
- $map->[1] = $ncp if $map;
- $$dst .= $ncp;
- return;
- }
my $d = $ctx->{pct} ? ' [irrelevant] ' # search result
: ' [not found] ';
$d .= indent_for($level) . th_pfx($level);
my $upfx = $ctx->{-upfx};
my $m = PublicInbox::Hval->new_msgid($mid);
- my $href = $upfx . $m->as_href . '/';
+ my $href = $upfx . $m->{href} . '/';
my $html = $m->as_html;
if ($map) {
@@ -879,36 +909,45 @@ sub acc_topic {
sub dump_topics {
my ($ctx) = @_;
- my $order = $ctx->{order}; # [ ts, subj1, subj2, subj3, ... ]
+ my $order = delete $ctx->{order}; # [ ts, subj1, subj2, subj3, ... ]
if (!@$order) {
$ctx->{-html_tip} = '[No topics in range]
';
return 404;
}
my @out;
- foreach my $topic (@$order) {
+
+ # sort by recency, this allows new posts to "bump" old topics...
+ foreach my $topic (sort { $b->[0] <=> $a->[0] } @$order) {
my ($ts, $n, $seen, $top, @ex) = @$topic;
@$topic = ();
next unless defined $top; # ghost topic
my $mid = delete $seen->{$top};
- my $href = PublicInbox::Hval->new_msgid($mid)->as_href;
+ my $href = mid_escape($mid);
$top = PublicInbox::Hval->new($top)->as_html;
$ts = fmt_ts($ts);
# $n isn't the total number of posts on the topic,
# just the number of posts in the current results window
- $n = $n == 1 ? '' : " ($n+ messages)";
+ my $anchor;
+ if ($n == 1) {
+ $n = '';
+ $anchor = '#u'; # top of only message
+ } else {
+ $n = " ($n+ messages)";
+ $anchor = '#t'; # thread skeleton
+ }
my $mbox = qq(mbox.gz);
my $atom = qq(Atom);
- my $s = "$top\n" .
+ my $s = "$top\n" .
" $ts UTC $n - $mbox / $atom\n";
for (my $i = 0; $i < scalar(@ex); $i += 2) {
my $level = $ex[$i];
my $sub = $ex[$i + 1];
$mid = delete $seen->{$sub};
$sub = PublicInbox::Hval->new($sub)->as_html;
- $href = PublicInbox::Hval->new_msgid($mid)->as_href;
+ $href = mid_escape($mid);
$s .= indent_for($level) . TCHILD;
$s .= "$sub\n";
}