- require PublicInbox::GitCatFile;
- my $git = PublicInbox::GitCatFile->new($ctx->{git_dir});
- if ($flat) {
- pre_anchor_entry($seen, $_) for (@$msgs);
- __thread_entry(\$cb, $git, $state, $_, 0) for (@$msgs);
- } else {
- my $th = thread_results($msgs);
- thread_entry(\$cb, $git, $state, $_, 0) for $th->rootset;
- }
- $git = undef;
- Email::Address->purge_cache;
-
- # there could be a race due to a message being deleted in git
- # but still being in the Xapian index:
- return missing_thread($cb, $ctx) if ($orig_cb eq $cb);
-
- my $final_anchor = $state->{anchor_idx};
- my $next = "<a\nid=\"s$final_anchor\">";
- $next .= $final_anchor == 1 ? 'only message in' : 'end of';
- $next .= " thread</a>, back to <a\nhref=\"../../\">index</a>";
- $next .= "\ndownload thread: ";
- $next .= "<a\n$MBOX_TITLE\nhref=\"../t.mbox.gz\">mbox.gz</a>";
- $next .= " / follow: <a\nhref=\"../t.atom\">Atom feed</a>";
- $cb->write("<hr />" . PRE_WRAP . $next . "\n\n". $foot .
- "</pre></body></html>");
- $cb->close;
-}
-
-sub index_walk {
- my ($fh, $part, $enc, $part_nr, $fhref, $more) = @_;
- my $s = add_text_body($enc, $part, $part_nr, $fhref);
-
- if ($more) {
- # drop the remainder of git patches, they're usually better
- # to review when the full message is viewed
- $s =~ s!^---+\n.*\z!!ms and $$more = 'more...';
-
- # Drop signatures
- $s =~ s/^-- \n.*\z//ms and $$more = 'more...';
- }
-
- # kill any leading or trailing whitespace lines
- $s =~ s/^\s*$//sgm;
- $s =~ s/\s+\z//s;
-
- if ($s ne '') {
- # kill per-line trailing whitespace
- $s =~ s/[ \t]+$//sgm;
- $s .= "\n" unless $s =~ /\n\z/s;
- }
- $fh->write($s);
-}
-
-sub enc_for {
- my ($ct, $default) = @_;
- $default ||= $enc_utf8;
- defined $ct or return $default;
- my $ct_parsed = parse_content_type($ct);
- if ($ct_parsed) {
- if (my $charset = $ct_parsed->{attributes}->{charset}) {
- my $enc = find_encoding($charset);
- return $enc if $enc;
+sub stream_thread ($$) {
+ my ($rootset, $ctx) = @_;
+ my $inbox = $ctx->{-inbox};
+ my @q = map { (0, $_) } @$rootset;
+ my $level;
+ my $smsg;
+ while (@q) {
+ $level = shift @q;
+ my $node = shift @q or next;
+ my $cl = $level + 1;
+ unshift @q, map { ($cl, $_) } @{$node->{children}};
+ $smsg = $inbox->smsg_mime($node->{smsg}) and last;
+ }
+ return missing_thread($ctx) unless $smsg;
+
+ $ctx->{-obfs_ibx} = $inbox->{obfuscate} ? $inbox : undef;
+ $ctx->{-title_html} = ascii_html($smsg->subject);
+ $ctx->{-html_tip} = thread_index_entry($ctx, $level, $smsg);
+ $smsg = undef;
+ PublicInbox::WwwStream->response($ctx, 200, sub {
+ return unless $ctx;
+ while (@q) {
+ $level = shift @q;
+ my $node = shift @q or next;
+ my $cl = $level + 1;
+ unshift @q, map { ($cl, $_) } @{$node->{children}};
+ if ($smsg = $inbox->smsg_mime($node->{smsg})) {
+ return thread_index_entry($ctx, $level, $smsg);
+ } else {
+ return ghost_index_entry($ctx, $level, $node);
+ }