'; + $ctx->{-upfx} = ''; + $ctx->{-hr} = 1; + my $ibx = $ctx->{-inbox}; + PublicInbox::WwwStream->response($ctx, 200, sub { + while (my $smsg = shift @$msgs) { + my $m = $ibx->smsg_mime($smsg) or next; + my $more = scalar @$msgs; + return PublicInbox::View::index_entry($m, $ctx, $more); } - add_to_feed($feed_opts, $fh, $path, $git); + PublicInbox::View::pagination_footer($ctx, './new.html'); }); - $git = undef; # destroy pipes - _end_feed($fh); } -sub _no_thread { - my ($cb) = @_; - my $fh = $cb->([404, ['Content-Type' => 'text/plain']]); - $fh->write("No feed found for thread\n"); - $fh->close; -} - -sub _end_feed { - my ($fh) = @_; - Email::Address->purge_cache; - $fh->write('
$footer"; - } - $fh->write(dump_topics($topics)) if $topics; - $fh->write("$footer"); - $fh->close; +sub _no_thread () { + [404, ['Content-Type', 'text/plain'], ["No feed found for thread\n"]]; } -sub nav_footer { - my ($cgi, $last, $feed_opts, $state) = @_; - $cgi or return ''; - my $old_r = $cgi->param('r'); - my $head = ' '; - my $next = ' '; - my $first = $state->{first_commit}; - my $anchor = $state->{anchor_idx}; - - if ($last) { - $next = qq!next!; +sub recent_msgs { + my ($ctx) = @_; + my $ibx = $ctx->{-inbox}; + my $max = $ibx->{feedmax}; + my $qp = $ctx->{qp}; + my $v = $ibx->{version} || 1; + if ($v > 2) { + die "BUG: unsupported inbox version: $v\n"; } - if ($old_r) { - $head = $cgi->path_info; - $head = qq!head!; + if (my $srch = $ibx->search) { + return PublicInbox::View::paginate_recent($ctx); } - my $atom = "{atomurl}\">atom"; - my $permalink = "permalink"; - "page: $next $head $atom $permalink"; -} -sub each_recent_blob { - my ($ctx, $cb) = @_; - my $max = $ctx->{max} || MAX_PER_PAGE; my $hex = '[a-f0-9]'; - my $addmsg = qr!^:000000 100644 \S+ \S+ A\t(${hex}{2}/${hex}{38})$!; - my $delmsg = qr!^:100644 000000 \S+ \S+ D\t(${hex}{2}/${hex}{38})$!; + my $addmsg = qr!^:000000 100644 \S+ (\S+) A\t${hex}{2}/${hex}{38}$!; + my $delmsg = qr!^:100644 000000 (\S+) \S+ D\t(${hex}{2}/${hex}{38})$!; my $refhex = qr/(?:HEAD|${hex}{4,40})(?:~\d+)?/; - my $cgi = $ctx->{cgi}; # revision ranges may be specified my $range = 'HEAD'; - my $r = $cgi->param('r') if $cgi; + my $r = $qp->{r} if $qp; if ($r && ($r =~ /\A(?:$refhex\.\.)?$refhex\z/o)) { $range = $r; } @@ -206,225 +113,41 @@ sub each_recent_blob { # get recent messages # we could use git log -z, but, we already know ssoma will not # leave us with filenames with spaces in them.. - my @cmd = ('git', "--git-dir=$ctx->{git_dir}", - qw/log --no-notes --no-color --raw -r - --abbrev=16 --abbrev-commit/, - "--format=%h%x00%ct%x00%an%x00%s%x00"); - push @cmd, $range; - - my $pid = open(my $log, '-|', @cmd) or - die('open `'.join(' ', @cmd) . " pipe failed: $!\n"); + my $log = $ibx->git->popen(qw/log + --no-notes --no-color --raw -r + --no-abbrev --abbrev-commit/, + "--format=%h", $range); my %deleted; # only an optimization at this point my $last; - my $nr = 0; - my ($cur_commit, $first_commit, $last_commit); - my ($ts, $subj, $u); + my $last_commit; + local $/ = "\n"; + my @oids; while (defined(my $line = <$log>)) { if ($line =~ /$addmsg/o) { my $add = $1; next if $deleted{$add}; # optimization-only - $nr += $cb->($add, $cur_commit, $ts, $u, $subj); - if ($nr >= $max) { + push @oids, $add; + if (scalar(@oids) >= $max) { $last = 1; last; } } elsif ($line =~ /$delmsg/o) { $deleted{$1} = 1; - } elsif ($line =~ /^${hex}{7,40}/o) { - ($cur_commit, $ts, $u, $subj) = split("\0", $line); - unless (defined $first_commit) { - $first_commit = $cur_commit; - } } } if ($last) { + local $/ = "\n"; while (my $line = <$log>) { - if ($line =~ /^(${hex}{7,40})/o) { + if ($line =~ /^(${hex}{7,40})/) { $last_commit = $1; last; } } } - close $log; # we may EPIPE here - # for pagination - ($first_commit, $last_commit); -} - -# private functions below -sub get_feedopts { - my ($ctx) = @_; - my $pi_config = $ctx->{pi_config}; - my $listname = $ctx->{listname}; - my $cgi = $ctx->{cgi}; - my %rv; - if (open my $fh, '<', "$ctx->{git_dir}/description") { - chomp($rv{description} = <$fh>); - close $fh; - } else { - $rv{description} = '($GIT_DIR/description missing)'; - } - - if ($pi_config && defined $listname && $listname ne '') { - my $addr = $pi_config->get($listname, 'address') || ""; - $rv{address} = $addr; - $addr = $addr->[0] if ref($addr); - $rv{id_addr} = $addr; - } - $rv{id_addr} ||= 'public-inbox@example.com'; - - my $url_base; - if ($cgi) { - my $base; - if (ref($cgi) eq 'CGI') { - $base = $cgi->url(-base); - } else { - $base = $cgi->base->as_string; - $base =~ s!/\z!!; - } - $url_base = "$base/$listname"; - if (my $mid = $ctx->{mid}) { # per-thread feed: - $rv{atomurl} = "$url_base/$mid/t.atom"; - } else { - $rv{atomurl} = "$url_base/new.atom"; - } - } else { - $url_base = "http://example.com"; - $rv{atomurl} = "$url_base/new.atom"; - } - $rv{url} ||= "$url_base/"; - $rv{midurl} = "$url_base/"; - - \%rv; -} - -sub mime_header { - my ($mime, $name) = @_; - PublicInbox::Hval->new_oneline($mime->header($name))->raw; -} - -sub feed_date { - my ($date) = @_; - my @t = eval { strptime($date) }; - - scalar(@t) ? strftime(DATEFMT, @t) : 0; -} - -# returns 0 (skipped) or 1 (added) -sub add_to_feed { - my ($feed_opts, $fh, $add, $git) = @_; - - my $mime = do_cat_mail($git, $add) or return 0; - my $url = $feed_opts->{url}; - my $midurl = $feed_opts->{midurl}; - - my $header_obj = $mime->header_obj; - my $mid = $header_obj->header('Message-ID'); - defined $mid or return 0; - $mid = PublicInbox::Hval->new_msgid($mid); - my $href = $mid->as_href; - my $content = PublicInbox::View->feed_entry($mime, "$midurl$href/f/"); - defined($content) or return 0; - $mime = undef; - - my $date = $header_obj->header('Date'); - $date = PublicInbox::Hval->new_oneline($date); - $date = feed_date($date->raw) or return 0; - - my $title = mime_header($header_obj, 'Subject') or return 0; - $title = title_tag($title); - - my $from = mime_header($header_obj, 'From') or return 0; - my @from = Email::Address->parse($from) or return 0; - my $name = PublicInbox::Hval->new_oneline($from[0]->name)->as_html; - my $email = $from[0]->address; - $email = PublicInbox::Hval->new_oneline($email)->as_html; - - if (delete $feed_opts->{emit_header}) { - $fh->write(atom_header($feed_opts, $title) . - "