about summary refs log tree commit
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2019-05-05 23:28:02 +0000
committerEric Wong <e@80x24.org>2019-05-05 23:28:02 +0000
commite1ed79d1e99e2c58b3edb370e60904cf656dd823 (patch)
tree30607048c2f16a2610ad0774dcb959025357d8af
parent632ed77815b8ad0f683abe78f32544c7ca7b9aed (diff)
parent7a3946ef122e8218c6ce3355d7f968562212d53b (diff)
downloadpublic-inbox-e1ed79d1e99e2c58b3edb370e60904cf656dd823.tar.gz
* origin/wwwlisting:
  www: support listing of inboxes
  start depending on Perl 5.10.1+
-rw-r--r--Documentation/public-inbox-config.pod37
-rw-r--r--INSTALL2
-rw-r--r--MANIFEST1
-rw-r--r--lib/PublicInbox/Config.pm2
-rw-r--r--lib/PublicInbox/Inbox.pm9
-rw-r--r--lib/PublicInbox/WWW.pm16
-rw-r--r--lib/PublicInbox/WwwListing.pm104
-rw-r--r--lib/PublicInbox/WwwStream.pm10
8 files changed, 173 insertions, 8 deletions
diff --git a/Documentation/public-inbox-config.pod b/Documentation/public-inbox-config.pod
index 29150220..d44c8f30 100644
--- a/Documentation/public-inbox-config.pod
+++ b/Documentation/public-inbox-config.pod
@@ -188,6 +188,14 @@ be treated as the default value.
 
 Default: 25
 
+=item publicinbox.<name>.hide
+
+A comma-delimited list of listings to hide the inbox from.
+
+Valid values are currently "www".
+
+Default: none
+
 =item coderepo.<nick>.dir
 
 The path to a git repository for "publicinbox.<name>.coderepo"
@@ -226,6 +234,35 @@ C<publicinbox.cgitbin>, but may be overridden.
 Default: basename of C<publicinbox.cgitbin>, /var/www/htdocs/cgit/
 or /usr/share/cgit/
 
+=item publicinbox.wwwlisting
+
+Enable a HTML listing style when the root path of the URL '/' is accessed.
+Valid values are:
+
+=over 8
+
+=item all
+
+Show all inboxes
+
+=item 404
+
+Return a 404 page.  This is useful to allow customization with
+L<Plack::App::Cascade(3pm)>
+
+=item match=domain
+
+Only show inboxes with URLs which belong to the domain of the HTTP
+request
+
+=for TODO comment
+
+support showing cgit listing
+
+=back
+
+Default: 404
+
 =back
 
 =head2 NAMED LIMITER (PSGI)
diff --git a/INSTALL b/INSTALL
index 9470d834..b22d8484 100644
--- a/INSTALL
+++ b/INSTALL
@@ -22,7 +22,7 @@ public-inbox requires a number of other packages to access its full
 functionality.  The core tools are, of course:
 
 * Git (1.8.0+, 2.6+ for writing v2 repositories)
-* Perl 5.8+
+* Perl 5.10.1+
 * SQLite (needed for Xapian use)
 
 To accept incoming mail into a public inbox, you'll likely want:
diff --git a/MANIFEST b/MANIFEST
index ed8ff491..4bdcda3c 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -129,6 +129,7 @@ lib/PublicInbox/WatchMaildir.pm
 lib/PublicInbox/WwwAtomStream.pm
 lib/PublicInbox/WwwAttach.pm
 lib/PublicInbox/WwwHighlight.pm
+lib/PublicInbox/WwwListing.pm
 lib/PublicInbox/WwwStream.pm
 lib/PublicInbox/WwwText.pm
 sa_config/Makefile
diff --git a/lib/PublicInbox/Config.pm b/lib/PublicInbox/Config.pm
index 631c7880..09f9179b 100644
--- a/lib/PublicInbox/Config.pm
+++ b/lib/PublicInbox/Config.pm
@@ -389,7 +389,7 @@ sub _fill {
         }
         # TODO: more arrays, we should support multi-value for
         # more things to encourage decentralization
-        foreach my $k (qw(address altid nntpmirror coderepo)) {
+        foreach my $k (qw(address altid nntpmirror coderepo hide)) {
                 if (defined(my $v = $self->{"$pfx.$k"})) {
                         $ibx->{$k} = _array($v);
                 }
diff --git a/lib/PublicInbox/Inbox.pm b/lib/PublicInbox/Inbox.pm
index 0d28dd04..286555f6 100644
--- a/lib/PublicInbox/Inbox.pm
+++ b/lib/PublicInbox/Inbox.pm
@@ -95,6 +95,15 @@ sub new {
         if (defined $dir && -f "$dir/inbox.lock") {
                 $opts->{version} = 2;
         }
+
+        # allow any combination of multi-line or comma-delimited hide entries
+        my $hide = {};
+        if (defined(my $h = $opts->{hide})) {
+                foreach my $v (@$h) {
+                        $hide->{$_} = 1 foreach (split(/\s*,\s*/, $v));
+                }
+                $opts->{-hide} = $hide;
+        }
         bless $opts, $class;
 }
 
diff --git a/lib/PublicInbox/WWW.pm b/lib/PublicInbox/WWW.pm
index 6e69001c..1f3ca157 100644
--- a/lib/PublicInbox/WWW.pm
+++ b/lib/PublicInbox/WWW.pm
@@ -11,7 +11,7 @@
 # - Must not rely on static content
 # - UTF-8 is only for user-content, 7-bit US-ASCII for us
 package PublicInbox::WWW;
-use 5.008;
+use 5.010_001;
 use strict;
 use warnings;
 use bytes (); # only for bytes::length
@@ -68,8 +68,9 @@ sub call {
         } split(/[&;]+/, $env->{QUERY_STRING});
         $ctx->{qp} = \%qp;
 
-        # not using $env->{PATH_INFO} here since that's already decoded
+        # avoiding $env->{PATH_INFO} here since that's already decoded
         my ($path_info) = ($env->{REQUEST_URI} =~ path_re($env));
+        $path_info //= $env->{PATH_INFO};
         my $method = $env->{REQUEST_METHOD};
 
         if ($method eq 'POST') {
@@ -87,7 +88,7 @@ sub call {
 
         # top-level indices and feeds
         if ($path_info eq '/') {
-                r404();
+                www_listing($self)->call($env);
         } elsif ($path_info =~ m!$INBOX_RE\z!o) {
                 invalid_inbox($ctx, $1) || r301($ctx, $1);
         } elsif ($path_info =~ m!$INBOX_RE(?:/|/index\.html)?\z!o) {
@@ -157,6 +158,7 @@ sub preload {
         if (ref($self)) {
                 $self->cgit;
                 $self->stylesheets_prepare($_) for ('', '../', '../../');
+                $self->www_listing;
         }
 }
 
@@ -489,6 +491,14 @@ sub cgit {
         }
 }
 
+sub www_listing {
+        my ($self) = @_;
+        $self->{www_listing} ||= do {
+                require PublicInbox::WwwListing;
+                PublicInbox::WwwListing->new($self);
+        }
+}
+
 sub get_attach {
         my ($ctx, $idx, $fn) = @_;
         require PublicInbox::WwwAttach;
diff --git a/lib/PublicInbox/WwwListing.pm b/lib/PublicInbox/WwwListing.pm
new file mode 100644
index 00000000..e8dad4b8
--- /dev/null
+++ b/lib/PublicInbox/WwwListing.pm
@@ -0,0 +1,104 @@
+# Copyright (C) 2019 all contributors <meta@public-inbox.org>
+# License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
+
+# Provide an HTTP-accessible listing of inboxes.
+# Used by PublicInbox::WWW
+package PublicInbox::WwwListing;
+use strict;
+use warnings;
+use PublicInbox::Hval qw(ascii_html);
+use PublicInbox::Linkify;
+use PublicInbox::View;
+
+sub list_all ($$) {
+        my ($self, undef) = @_;
+        my @list;
+        $self->{pi_config}->each_inbox(sub {
+                my ($ibx) = @_;
+                push @list, $ibx unless $ibx->{-hide}->{www};
+        });
+        \@list;
+}
+
+sub list_match_domain ($$) {
+        my ($self, $env) = @_;
+        my @list;
+        my $host = $env->{HTTP_HOST} // $env->{SERVER_NAME};
+        $host =~ s/:\d+\z//;
+        my $re = qr!\A(?:https?:)?//\Q$host\E(?::\d+)?/!i;
+        $self->{pi_config}->each_inbox(sub {
+                my ($ibx) = @_;
+                push @list, $ibx if !$ibx->{-hide}->{www} && $ibx->{url} =~ $re;
+        });
+        \@list;
+}
+
+sub list_404 ($$) { [] }
+
+# TODO: +cgit
+my %VALID = (
+        all => *list_all,
+        'match=domain' => *list_match_domain,
+        404 => *list_404,
+);
+
+sub new {
+        my ($class, $www) = @_;
+        my $k = 'publicinbox.wwwListing';
+        my $pi_config = $www->{pi_config};
+        my $v = $pi_config->{lc($k)} // 404;
+        bless {
+                pi_config => $pi_config,
+                style => $www->style("\0"),
+                list_cb => $VALID{$v} || do {
+                        warn <<"";
+`$v' is not a valid value for `$k'
+$k be one of `all', `match=domain', or `404'
+
+                        *list_404;
+                },
+        }, $class;
+}
+
+sub ibx_entry {
+        my ($mtime, $ibx, $env) = @_;
+        my $ts = PublicInbox::View::fmt_ts($mtime);
+        my $url = PublicInbox::Hval::prurl($env, $ibx->{url});
+        my $tmp = <<"";
+* $ts - $url
+  ${\$ibx->description}
+
+        if (defined(my $info_url = $ibx->{info_url})) {
+                $tmp .= "\n$info_url";
+        }
+        $tmp;
+}
+
+# not really a stand-alone PSGI app, but maybe it could be...
+sub call {
+        my ($self, $env) = @_;
+        my $h = [ 'Content-Type', 'text/html; charset=UTF-8' ];
+        my $list = $self->{list_cb}->($self, $env);
+        my $code = 404;
+        my $title = 'public-inbox';
+        my $out = '';
+        if (@$list) {
+                # Swartzian transform since ->modified is expensive
+                @$list = sort {
+                        $b->[0] <=> $a->[0]
+                } map { [ $_->modified, $_ ] } @$list;
+
+                $code = 200;
+                $title .= ' - listing';
+                my $tmp = join("\n", map { ibx_entry(@$_, $env) } @$list);
+                my $l = PublicInbox::Linkify->new;
+                $l->linkify_1($tmp);
+                $out = '<pre>'.$l->linkify_2(ascii_html($tmp)).'</pre><hr>';
+        }
+        $out = "<html><head><title>$title</title></head><body>" . $out;
+        $out .= '<pre>'. PublicInbox::WwwStream::code_footer($env) .
+                '</pre></body></html>';
+        [ $code, $h, [ $out ] ]
+}
+
+1;
diff --git a/lib/PublicInbox/WwwStream.pm b/lib/PublicInbox/WwwStream.pm
index ea7aaad0..2893138d 100644
--- a/lib/PublicInbox/WwwStream.pm
+++ b/lib/PublicInbox/WwwStream.pm
@@ -70,6 +70,12 @@ sub _html_top ($) {
                 "</head><body>". $top . $tip;
 }
 
+sub code_footer ($) {
+        my ($env) = @_;
+        my $u = PublicInbox::Hval::prurl($env, $CODE_URL);
+        qq(AGPL code for this site: git clone <a\nhref="$u">$u</a> $PROJECT)
+}
+
 sub _html_end {
         my ($self) = @_;
         my $urls = 'Archives are clonable:';
@@ -134,12 +140,10 @@ EOF
                 $urls .= "\n note: .onion URLs require Tor: ";
                 $urls .= qq[<a\nhref="$TOR_URL">$TOR_URL</a>];
         }
-        my $url = PublicInbox::Hval::prurl($ctx->{env}, $CODE_URL);
         '<hr><pre>'.join("\n\n",
                 $desc,
                 $urls,
-                'AGPL code for this site: '.
-                qq(git clone <a\nhref="$url">$url</a> $PROJECT)
+                code_footer($ctx->{env})
         ).'</pre></body></html>';
 }