+sub ctx_get {
+ my ($ctx, $key) = @_;
+ my $val = $ctx->{$key};
+ (defined $val && length $val) or die "BUG: bad ctx, $key unusable\n";
+ $val;
+}
+
+sub try_cat {
+ my ($path) = @_;
+ my $rv;
+ if (open(my $fh, '<', $path)) {
+ local $/;
+ $rv = <$fh>;
+ close $fh;
+ }
+ $rv;
+}
+
+sub footer {
+ my ($ctx) = @_;
+ return '' unless $ctx;
+ my $git_dir = ctx_get($ctx, 'git_dir');
+
+ # favor user-supplied footer
+ my $footer = try_cat("$git_dir/public-inbox/footer.html");
+ if (defined $footer) {
+ chomp $footer;
+ $ctx->{footer} = $footer;
+ return $footer;
+ }
+
+ # auto-generate a footer
+ my $listname = ctx_get($ctx, 'listname');
+ my $desc = try_cat("$git_dir/description");
+ $desc = '$GIT_DIR/description missing' unless defined $desc;
+ chomp $desc;
+
+ my $urls = try_cat("$git_dir/cloneurl");
+ my @urls = split(/\r?\n/, $urls || '');
+ my $nurls = scalar @urls;
+ if ($nurls == 0) {
+ $urls = '($GIT_DIR/cloneurl missing)';
+ } elsif ($nurls == 1) {
+ $urls = "git URL for <a\nhref=\"" . SSOMA_URL .
+ '">ssoma</a>: ' . $urls[0];
+ } else {
+ $urls = "git URLs for <a\nhref=\"" . SSOMA_URL .
+ "\">ssoma</a>:\n" . join("\n", map { "\t$_" } @urls);
+ }
+
+ my $addr = $pi_config->get($listname, 'address');
+ if (ref($addr) eq 'ARRAY') {
+ $addr = $addr->[0]; # first address is primary
+ }
+
+ $addr = "<a\nhref=\"mailto:$addr\">$addr</a>";
+
+ $ctx->{footer} = join("\n",
+ '- ' . $desc,
+ "A <a\nhref=\"" . PI_URL . '">public-inbox</a>, ' .
+ 'anybody may post in plain-text (not HTML):',
+ $addr,
+ $urls
+ );
+}
+
+# search support is optional, returns undef if Xapian is not installed
+# or not configured for the given GIT_DIR
+sub searcher {
+ my ($ctx) = @_;
+ eval {
+ require PublicInbox::Search;
+ $ctx->{srch} = PublicInbox::Search->new($ctx->{git_dir});
+ };
+}
+
+sub need_search {
+ my ($ctx) = @_;
+ my $msg = <<EOF;
+<html><head><title>Search not available for this
+public-inbox</title><body><pre>Search is not available for this public-inbox
+<a href="../">Return to index</a></pre></body></html>
+EOF
+ [ 501, [ 'Content-Type' => 'text/html; charset=UTF-8' ], [ $msg ] ];
+}
+
+sub msg_pfx {
+ my ($ctx) = @_;
+ my $href = PublicInbox::Hval::ascii_html(uri_escape_utf8($ctx->{mid}));
+ "../f/$href.html";
+}
+
+# /$LISTNAME/t/$MESSAGE_ID.mbox.gz -> search results as gzipped mbox
+# note: I'm not a big fan of other compression formats since they're
+# significantly more expensive on CPU than gzip and less-widely available,
+# especially on older systems. Stick to zlib since that's what git uses.
+sub get_thread_mbox {
+ my ($ctx, $cgi) = @_;
+ my $srch = searcher($ctx) or return need_search($ctx);
+ require PublicInbox::Mbox;
+ PublicInbox::Mbox::thread_mbox($ctx, $srch);
+}
+