From f3b60bf095846ce9290b94a7b1d700ed7bf0f316 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 17 Apr 2019 22:05:38 +0000 Subject: start depending on Perl 5.10.1+ I mainly want to start using the '//' (defined-or) operator to simplify code, and Perl 5.10.1 is roughly a decade old at this point. "given/when" would've be nice, but it's future is in doubt AFAIK. I also started using the 'parent' module in WwwHighlight, and 'autodie' in UserContent.pm, both of which were only distributed with Perl since 5.10.1; and testing with ancient versions/distros is time-consuming. Anyways, I think this a small-enough jump to not break any existing installations, given we already depend on fairly recent versions of git and Xapian. Maybe we can use more newish Perl features in the future... --- INSTALL | 2 +- lib/PublicInbox/WWW.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/lib/PublicInbox/WWW.pm b/lib/PublicInbox/WWW.pm index 6e69001c..268c5b8c 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 -- cgit v1.2.3-24-ge0c7 From 7a3946ef122e8218c6ce3355d7f968562212d53b Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 18 Apr 2019 08:25:56 +0000 Subject: www: support listing of inboxes We will still return a 404 by default to '/' for compatibility with users of Plack::App::Cascade or similar. Inboxes are sorted by modification times to help users detect activity (similar to the /$INBOX/ topic view). New configuration options: * publicinbox.wwwlisting - configure the listing type * publicinbox..hide - hide a particular inbox from the listing See changes to public-inbox-config.pod for full descriptions of the new options. Requested-by: Leah Neukirchen https://public-inbox.org/meta/871sdfzy80.fsf@gmail.com/ --- Documentation/public-inbox-config.pod | 37 ++++++++++++ MANIFEST | 1 + lib/PublicInbox/Config.pm | 2 +- lib/PublicInbox/Inbox.pm | 9 +++ lib/PublicInbox/WWW.pm | 14 ++++- lib/PublicInbox/WwwListing.pm | 104 ++++++++++++++++++++++++++++++++++ lib/PublicInbox/WwwStream.pm | 10 +++- 7 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 lib/PublicInbox/WwwListing.pm diff --git a/Documentation/public-inbox-config.pod b/Documentation/public-inbox-config.pod index f894eb3d..17b8bac7 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..hide + +A comma-delimited list of listings to hide the inbox from. + +Valid values are currently "www". + +Default: none + =item coderepo..dir The path to a git repository for "publicinbox..coderepo" @@ -217,6 +225,35 @@ directive is configured. Default: /var/www/htdocs/cgit/cgit.cgi or /usr/lib/cgit/cgit.cgi +=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 + +=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/MANIFEST b/MANIFEST index 150e337f..881d2f07 100644 --- a/MANIFEST +++ b/MANIFEST @@ -127,6 +127,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 268c5b8c..1f3ca157 100644 --- a/lib/PublicInbox/WWW.pm +++ b/lib/PublicInbox/WWW.pm @@ -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 +# License: AGPL-3.0+ + +# 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 = '
'.$l->linkify_2(ascii_html($tmp)).'

'; + } + $out = "$title" . $out; + $out .= '
'. PublicInbox::WwwStream::code_footer($env) .
+		'
'; + [ $code, $h, [ $out ] ] +} + +1; diff --git a/lib/PublicInbox/WwwStream.pm b/lib/PublicInbox/WwwStream.pm index 8b79923b..c708c21f 100644 --- a/lib/PublicInbox/WwwStream.pm +++ b/lib/PublicInbox/WwwStream.pm @@ -71,6 +71,12 @@ sub _html_top ($) { "". $top . $tip; } +sub code_footer ($) { + my ($env) = @_; + my $u = PublicInbox::Hval::prurl($env, $CODE_URL); + qq(AGPL code for this site: git clone $u $PROJECT) +} + sub _html_end { my ($self) = @_; my $urls = 'Archives are clonable:'; @@ -139,12 +145,10 @@ EOF $urls .= qq[$TOR2WEB_URL]; } } - my $url = PublicInbox::Hval::prurl($ctx->{env}, $CODE_URL); '
'.join("\n\n",
 		$desc,
 		$urls,
-		'AGPL code for this site: '.
-		qq(git clone $url $PROJECT)
+		code_footer($ctx->{env})
 	).'
'; } -- cgit v1.2.3-24-ge0c7