sub th_pfx ($) { $_[0] == 0 ? '' : TCHILD };
sub msg_page_i {
- my ($nr, $ctx) = @_;
- 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, $hdr) };
- } else {
- undef
+ my ($ctx, $eml) = @_;
+ if ($eml) { # called by WwwStream::async_eml or getline
+ my $smsg = $ctx->{smsg};
+ $ctx->{smsg} = $ctx->{over}->next_by_mid(@{$ctx->{next_arg}});
+ $ctx->{mhref} = ($ctx->{nr} || $ctx->{smsg}) ?
+ "../${\mid_href($smsg->{mid})}/" : '';
+ my $hdr = $eml->header_obj;
+ my $obuf = $ctx->{obuf} = _msg_page_prepare_obuf($hdr, $ctx);
+ multipart_text_as_html($eml, $ctx);
+ delete $ctx->{obuf};
+ $$obuf .= '</pre><hr>';
+ $$obuf .= html_footer($ctx, $ctx->{first_hdr}) if !$ctx->{smsg};
+ $$obuf;
+ } else { # called by WwwStream::async_next or getline
+ $ctx->{smsg}; # may be undef
}
}
+# /$INBOX/$MESSAGE_ID/ for unindexed v1 inboxes
+sub no_over_i {
+ my ($ctx) = @_;
+ my $eml = delete $ctx->{eml} or return;
+ my $hdr = $eml->header_obj;
+ $ctx->{mhref} = '';
+ my $obuf = $ctx->{obuf} = _msg_page_prepare_obuf($hdr, $ctx);
+ multipart_text_as_html($eml, $ctx);
+ delete $ctx->{obuf};
+ $$obuf .= '</pre><hr>';
+ eval { $$obuf .= html_footer($ctx, $hdr) };
+ $$obuf
+}
+
+sub no_over_html ($) {
+ my ($ctx) = @_;
+ my $bref = $ctx->{-inbox}->msg_by_mid($ctx->{mid}) or return; # 404
+ $ctx->{eml} = PublicInbox::Eml->new($bref);
+ PublicInbox::WwwStream::response($ctx, 200, \&no_over_i);
+}
+
# public functions: (unstable)
sub msg_page {
my ($ctx) = @_;
- my $mid = $ctx->{mid};
my $ibx = $ctx->{-inbox};
- my ($smsg, $first, $next);
- if (my $over = $ibx->over) {
- my ($id, $prev);
- $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 = PublicInbox::Eml->new($first);
$ctx->{-obfs_ibx} = $ibx->{obfuscate} ? $ibx : undef;
- my $hdr = $ctx->{hdr} = $mime->header_obj;
- $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 { # cold
- my ($ctx, $nr, $id, $prev, $smsg) = @_;
- my $ibx = $ctx->{-inbox};
- my $next = $ibx->over->next_by_mid($ctx->{mid}, \$id, \$prev);
- $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};
- $ctx->{obuf} = _msg_page_prepare_obuf($mime->header_obj, $ctx, $nr);
- multipart_text_as_html($mime, $ctx);
- ${delete $ctx->{obuf}} .= '</pre><hr>';
+ my $over = $ctx->{over} = $ibx->over or return no_over_html($ctx);
+ my ($id, $prev);
+ my $next_arg = $ctx->{next_arg} = [ $ctx->{mid}, \$id, \$prev ];
+ $ctx->{smsg} = $over->next_by_mid(@$next_arg) or return; # undef == 404
+ PublicInbox::WwwStream::aresponse($ctx, 200, \&msg_page_i);
}
# /$INBOX/$MESSAGE_ID/#R
# human-friendly format
sub fmt_ts ($) { strftime('%Y-%m-%d %k:%M', gmtime($_[0])) }
+# Displays the text of of the message for /$INBOX/$MSGID/[Tt]/ endpoint
# this is already inside a <pre>
-sub index_entry {
- my ($smsg, $ctx, $more) = @_;
- my $subj = $smsg->subject;
- my $mid_raw = $smsg->mid;
+sub eml_entry {
+ my ($ctx, $smsg, $eml, $more) = @_;
+ my $subj = delete $smsg->{subject};
+ my $mid_raw = $smsg->{mid};
my $id = id_compress($mid_raw, 1);
my $id_m = 'm'.$id;
-
my $root_anchor = $ctx->{root_anchor} || '';
my $irt;
my $obfs_ibx = $ctx->{-obfs_ibx};
$rv .= $subj . "\n";
$rv .= _th_index_lite($mid_raw, \$irt, $id, $ctx);
my @tocc;
- my $ds = $smsg->ds; # for v1 non-Xapian/SQLite users
- # deleting {mime} is critical to memory use,
- # the rest of the fields saves about 400K as we iterate across 1K msgs
- my ($mime) = delete @$smsg{qw(mime ds ts blob subject)};
+ my $ds = delete $smsg->{ds}; # for v1 non-Xapian/SQLite users
+
+ # Deleting these fields saves about 400K as we iterate across 1K msgs
+ delete @$smsg{qw(ts blob)};
- my $hdr = $mime->header_obj;
+ my $hdr = $eml->header_obj;
my $from = _hdr_names_html($hdr, 'From');
obfuscate_addrs($obfs_ibx, $from) if $obfs_ibx;
$rv .= "From: $from @ ".fmt_ts($ds)." UTC";
# scan through all parts, looking for displayable text
$ctx->{mhref} = $mhref;
$ctx->{obuf} = \$rv;
- $mime->each_part(\&add_text_body, $ctx, 1);
+ $eml->each_part(\&add_text_body, $ctx, 1);
delete $ctx->{obuf};
# add the footer
skel_dump($ctx, $level, $node);
}
-sub thread_index_entry {
- my ($ctx, $level, $smsg) = @_;
+sub thread_eml_entry {
+ my ($ctx, $level, $smsg, $eml) = @_;
my ($beg, $end) = thread_adj_level($ctx, $level);
- $beg . '<pre>' . index_entry($smsg, $ctx, 0) . '</pre>' . $end;
+ $beg . '<pre>' . eml_entry($ctx, $smsg, $eml, 0) . '</pre>' . $end;
}
-sub stream_thread_i { # PublicInbox::WwwStream::getline callback
- my ($nr, $ctx) = @_;
- return unless exists($ctx->{skel});
- my $q = $ctx->{-queue};
+sub next_in_queue ($;$) {
+ my ($q, $ghost_ok) = @_;
while (@$q) {
- my $level = shift @$q;
- my $node = shift @$q or next;
+ my ($level, $smsg) = splice(@$q, 0, 2);
my $cl = $level + 1;
- unshift @$q, map { ($cl, $_) } @{$node->{children}};
- if ($ctx->{-inbox}->smsg_mime($node)) {
- return thread_index_entry($ctx, $level, $node);
- } else {
- return ghost_index_entry($ctx, $level, $node);
- }
+ unshift @$q, map { ($cl, $_) } @{$smsg->{children}};
+ return ($level, $smsg) if $ghost_ok || exists($smsg->{blob});
}
- join('', thread_adj_level($ctx, 0)) . ${delete $ctx->{skel}};
+ undef;
}
-sub stream_thread ($$) {
- my ($rootset, $ctx) = @_;
- my $ibx = $ctx->{-inbox};
- my @q = map { (0, $_) } @$rootset;
- my ($smsg, $level);
- while (@q) {
- $level = shift @q;
- my $node = shift @q or next;
- my $cl = $level + 1;
- unshift @q, map { ($cl, $_) } @{$node->{children}};
- $smsg = $ibx->smsg_mime($node) and last;
+sub stream_thread_i { # PublicInbox::WwwStream::getline callback
+ my ($ctx) = @_;
+ return unless exists($ctx->{skel});
+ my $nr = $ctx->{nr}++;
+ my ($level, $smsg) = next_in_queue($ctx->{-queue}, $nr);
+
+ $smsg or return
+ join('', thread_adj_level($ctx, 0)) . ${delete $ctx->{skel}};
+
+ my $eml = $ctx->{-inbox}->smsg_eml($smsg) or return
+ ghost_index_entry($ctx, $level, $smsg);
+
+ if ($nr == 0) {
+ $ctx->{-title_html} = ascii_html($smsg->{subject});
+ $ctx->html_top . thread_eml_entry($ctx, $level, $smsg, $eml);
+ } else {
+ thread_eml_entry($ctx, $level, $smsg, $eml);
}
- return missing_thread($ctx) unless $smsg;
+}
- $ctx->{-obfs_ibx} = $ibx->{obfuscate} ? $ibx : undef;
- $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);
+sub stream_thread ($$) {
+ my ($rootset, $ctx) = @_;
+ $ctx->{-queue} = [ map { (0, $_) } @$rootset ];
+ PublicInbox::WwwStream::response($ctx, 200, \&stream_thread_i);
}
# /$INBOX/$MESSAGE_ID/t/
return stream_thread($rootset, $ctx) unless $ctx->{flat};
# flat display: lazy load the full message from smsg
- my $smsg;
- while (my $m = shift @$msgs) {
- $smsg = $ibx->smsg_mime($m) and last;
- }
- return missing_thread($ctx) unless $smsg;
- $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);
+ $ctx->{-html_tip} = '<pre>';
+ PublicInbox::WwwStream::response($ctx, 200, \&thread_html_i);
}
sub thread_html_i { # PublicInbox::WwwStream::getline callback
- my ($nr, $ctx) = @_;
+ my ($ctx) = @_;
my $msgs = $ctx->{msgs} or return;
while (my $smsg = shift @$msgs) {
- $ctx->{-inbox}->smsg_mime($smsg) or next;
- return index_entry($smsg, $ctx, scalar @$msgs);
+ my $eml = $ctx->{-inbox}->smsg_eml($smsg) or next;
+ if (exists $ctx->{-html_tip}) {
+ $ctx->{-title_html} = ascii_html($smsg->{subject});
+ return $ctx->html_top .
+ eml_entry($ctx, $smsg, $eml, scalar @$msgs);
+ }
+ return eml_entry($ctx, $smsg, $eml, scalar @$msgs);
}
my ($skel) = delete @$ctx{qw(skel msgs)};
$$skel;
}
sub _msg_page_prepare_obuf {
- my ($hdr, $ctx, $nr) = @_;
+ my ($hdr, $ctx) = @_;
my $over = $ctx->{-inbox}->over;
my $obfs_ibx = $ctx->{-obfs_ibx};
my $rv = '';
my $mids = mids_for_index($hdr);
- if ($nr == 0) {
- if ($ctx->{more}) {
+ my $nr = $ctx->{nr}++;
+ if ($nr) { # unlikely
+ $rv .= '<pre>';
+ } else {
+ $ctx->{first_hdr} = $hdr;
+ if ($ctx->{smsg}) {
$rv .=
"<pre>WARNING: multiple messages have this Message-ID\n</pre>";
}
$rv .= "<pre\nid=b>"; # anchor for body start
- } else {
- $rv .= '<pre>';
- }
- if ($over) {
- $ctx->{-upfx} = '../';
}
+ $ctx->{-upfx} = '../' if $over;
my @title; # (Subject[0], From[0])
for my $v ($hdr->header('From')) {
my @n = PublicInbox::Address::names($v);
obfuscate_addrs($obfs_ibx, $v) if $obfs_ibx; # possible :P
$rv .= "Date: $v\n";
}
- $ctx->{-title_html} = join(' - ', @title);
+ if (!$nr) { # first (and only) message, common case
+ $ctx->{-title_html} = join(' - ', @title);
+ $rv = $ctx->html_top . $rv;
+ }
if (scalar(@$mids) == 1) { # common case
my $mhtml = ascii_html($mids->[0]);
$rv .= "Message-ID: <$mhtml> ";
"<hr><pre>page: $next$prev</pre>";
}
-sub index_nav { # callback for WwwStream
- my (undef, $ctx) = @_;
+sub index_nav { # callback for WwwStream::getline
+ my ($ctx) = @_;
+ return $ctx->html_top if exists $ctx->{-html_tip};
pagination_footer($ctx, '.')
}
if (@$msgs) {
walk_thread(thread_results($ctx, $msgs), $ctx, \&acc_topic);
}
- PublicInbox::WwwStream->response($ctx, dump_topics($ctx), \&index_nav);
+ PublicInbox::WwwStream::response($ctx, dump_topics($ctx), \&index_nav);
}
sub thread_adj_level {