]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/WwwText.pm
wwwtext: describe the use of `coderepo' entries
[public-inbox.git] / lib / PublicInbox / WwwText.pm
index b8beb97c6d5983e9c319a2f7733a4e232fc7f884..81c0ee4abfc889fbb9939498296e4d6cff4118a6 100644 (file)
@@ -1,15 +1,22 @@
-# Copyright (C) 2016 all contributors <meta@public-inbox.org>
+# Copyright (C) 2016-2020 all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
-#
-# serves the /$INBOX/_/* endpoints from :text/* of the git tree
+
+# used for displaying help texts and other non-mail content
 package PublicInbox::WwwText;
 use strict;
 use warnings;
 package PublicInbox::WwwText;
 use strict;
 use warnings;
+use bytes (); # only for bytes::length
 use PublicInbox::Linkify;
 use PublicInbox::WwwStream;
 use PublicInbox::Hval qw(ascii_html);
 use PublicInbox::Linkify;
 use PublicInbox::WwwStream;
 use PublicInbox::Hval qw(ascii_html);
+use URI::Escape qw(uri_escape_utf8);
+use PublicInbox::GzipFilter qw(gzf_maybe);
 our $QP_URL = 'https://xapian.org/docs/queryparser.html';
 our $WIKI_URL = 'https://en.wikipedia.org/wiki';
 our $QP_URL = 'https://xapian.org/docs/queryparser.html';
 our $WIKI_URL = 'https://en.wikipedia.org/wiki';
+my $hl = eval {
+       require PublicInbox::HlMod;
+       PublicInbox::HlMod->new
+};
 
 # /$INBOX/_/text/$KEY/ # KEY may contain slashes
 # For now, "help" is the only supported $KEY
 
 # /$INBOX/_/text/$KEY/ # KEY may contain slashes
 # For now, "help" is the only supported $KEY
@@ -24,19 +31,25 @@ sub get_text {
        my $have_tslash = ($key =~ s!/\z!!) if !$raw;
 
        my $txt = '';
        my $have_tslash = ($key =~ s!/\z!!) if !$raw;
 
        my $txt = '';
-       if (!_default_text($ctx, $key, \$txt)) {
+       my $hdr = [ 'Content-Type', 'text/plain', 'Content-Length', undef ];
+       if (!_default_text($ctx, $key, $hdr, \$txt)) {
                $code = 404;
                $txt = "404 Not Found ($key)\n";
        }
                $code = 404;
                $txt = "404 Not Found ($key)\n";
        }
+       my $env = $ctx->{env};
        if ($raw) {
        if ($raw) {
-               return [ $code, [ 'Content-Type', 'text/plain',
-                                 'Content-Length', bytes::length($txt) ],
-                       [ $txt ] ]
+               if ($code == 200) {
+                       my $gzf = gzf_maybe($hdr, $env);
+                       $txt = $gzf->translate($txt);
+                       $txt .= $gzf->zflush;
+               }
+               $hdr->[3] = bytes::length($txt);
+               return [ $code, $hdr, [ $txt ] ]
        }
 
        # enforce trailing slash for "wget -r" compatibility
        if (!$have_tslash && $code == 200) {
        }
 
        # enforce trailing slash for "wget -r" compatibility
        if (!$have_tslash && $code == 200) {
-               my $url = $ctx->{-inbox}->base_url($ctx->{env});
+               my $url = $ctx->{-inbox}->base_url($env);
                $url .= "_/text/$key/";
 
                return [ 302, [ 'Content-Type', 'text/plain',
                $url .= "_/text/$key/";
 
                return [ 302, [ 'Content-Type', 'text/plain',
@@ -47,21 +60,18 @@ sub get_text {
        # Follow git commit message conventions,
        # first line is the Subject/title
        my ($title) = ($txt =~ /\A([^\n]*)/s);
        # Follow git commit message conventions,
        # first line is the Subject/title
        my ($title) = ($txt =~ /\A([^\n]*)/s);
-       _do_linkify($txt);
        $ctx->{-title_html} = ascii_html($title);
        $ctx->{-title_html} = ascii_html($title);
-
        my $nslash = ($key =~ tr!/!/!);
        $ctx->{-upfx} = '../../../' . ('../' x $nslash);
        my $nslash = ($key =~ tr!/!/!);
        $ctx->{-upfx} = '../../../' . ('../' x $nslash);
-
-       PublicInbox::WwwStream->response($ctx, $code, sub {
-               my ($nr, undef) = @_;
-               $nr == 1 ? '<pre>'.$txt.'</pre>' : undef
-       });
-}
-
-sub _do_linkify {
        my $l = PublicInbox::Linkify->new;
        my $l = PublicInbox::Linkify->new;
-       $_[0] = $l->linkify_2(ascii_html($l->linkify_1($_[0])));
+       $l->linkify_1($txt);
+       if ($hl) {
+               $hl->do_hl_text(\$txt);
+       } else {
+               $txt = ascii_html($txt);
+       }
+       $txt = '<pre>' . $l->linkify_2($txt) . '</pre>';
+       PublicInbox::WwwStream::html_oneshot($ctx, $code, \$txt);
 }
 
 sub _srch_prefix ($$) {
 }
 
 sub _srch_prefix ($$) {
@@ -88,9 +98,126 @@ sub _srch_prefix ($$) {
        1;
 }
 
        1;
 }
 
+sub _colors_help ($$) {
+       my ($ctx, $txt) = @_;
+       my $ibx = $ctx->{-inbox};
+       my $env = $ctx->{env};
+       my $base_url = $ibx->base_url($env);
+       $$txt .= "color customization for $base_url\n";
+       $$txt .= <<EOF;
+
+public-inbox provides a stable set of CSS classes for users to
+customize colors for highlighting diffs and code.
+
+Users of browsers such as dillo, Firefox, or some browser
+extensions may start by downloading the following sample CSS file
+to control the colors they see:
+
+       ${base_url}userContent.css
+
+CSS sample
+----------
+```css
+EOF
+       $$txt .= PublicInbox::UserContent::sample($ibx, $env) . "```\n";
+}
+
+# git-config section names are quoted in the config file, so escape them
+sub dq_escape ($) {
+       my ($name) = @_;
+       $name =~ s/\\/\\\\/g;
+       $name =~ s/"/\\"/g;
+       $name;
+}
+
+sub URI_PATH () { '^A-Za-z0-9\-\._~/' }
+
+# n.b. this is a perfect candidate for memoization
+sub inbox_config ($$$) {
+       my ($ctx, $hdr, $txt) = @_;
+       my $ibx = $ctx->{-inbox};
+       push @$hdr, 'Content-Disposition', 'inline; filename=inbox.config';
+       my $name = dq_escape($ibx->{name});
+       my $inboxdir = '/path/to/top-level-inbox';
+       $$txt .= <<EOS;
+; example public-inbox config snippet for "$name"
+; see public-inbox-config(5) manpage for more details:
+; https://public-inbox.org/public-inbox-config.html
+[publicinbox "$name"]
+       inboxdir = $inboxdir
+       ; note: public-inbox before v1.2.0 used "mainrepo"
+       ; instead of "inboxdir", both remain supported after 1.2
+       mainrepo = $inboxdir
+       url = https://example.com/$name/
+       url = http://example.onion/$name/
+EOS
+       for my $k (qw(address listid infourl watchheader)) {
+               defined(my $v = $ibx->{$k}) or next;
+               $$txt .= "\t$k = $_\n" for @$v;
+       }
+       if (my $altid = $ibx->{altid}) {
+               my $base_url = $ibx->base_url($ctx->{env});
+               my $altid_map = $ibx->altid_map;
+               $$txt .= <<EOF;
+       ; altid DBs may be used to provide numeric article ID lookup from
+       ; old, pre-existing sources.  You can recreate them via curl(1),
+       ; gzip(1), and sqlite3(1) as documented:
+EOF
+               for (sort keys %$altid_map) {
+                       $$txt .= "\t;\tcurl -XPOST $base_url$_.sql.gz | \\\n" .
+                               "\t;\tgzip -dc | \\\n" .
+                               "\t;\tsqlite3 $inboxdir/$_.sqlite3\n";
+                       $$txt .= "\taltid = serial:$_:file=$_.sqlite3\n";
+               }
+       }
+
+       for my $k (qw(filter newsgroup obfuscate replyto)) {
+               defined(my $v = $ibx->{$k}) or next;
+               $$txt .= "\t$k = $v\n";
+       }
+       $$txt .= "\tnntpmirror = $_\n" for (@{$ibx->nntp_url});
+
+       # note: this doesn't preserve cgitrc layout, since we parse cgitrc
+       # and drop the original structure
+       if (defined(my $cr = $ibx->{coderepo})) {
+               $$txt .= "\tcoderepo = $_\n" for @$cr;
+               $$txt .= <<'EOF';
+
+; `coderepo' entries allows blob reconstruction via patch emails if
+; the inbox is indexed with Xapian.  `@@ <from-range> <to-range> @@'
+; line number ranges in `[PATCH]' emails link to /$INBOX_NAME/$OID/s/,
+; an HTTP endpoint which reconstructs git blobs via git-apply(1).
+EOF
+               my $pi_config = $ctx->{www}->{pi_config};
+               for my $cr_name (@$cr) {
+                       my $urls = $pi_config->{"coderepo.$cr_name.cgiturl"};
+                       my $path = "/path/to/$cr_name";
+                       $cr_name = dq_escape($cr_name);
+
+                       $$txt .= qq([coderepo "$cr_name"]\n);
+                       if ($urls && scalar(@$urls)) {
+                               $$txt .= "\t; ";
+                               $$txt .= join(" ||\n\t;\t", map {;
+                                       my $cpath = $path;
+                                       if ($path !~ m![a-z0-9_/\.\-]!i) {
+                                               $cpath = dq_escape($cpath);
+                                       }
+                                       qq(git clone $_ "$cpath");
+                               } @$urls);
+                               $$txt .= "\n";
+                       }
+                       $$txt .= "\tdir = $path\n";
+                       $$txt .= "\tcgiturl = https://example.com/";
+                       $$txt .= uri_escape_utf8($cr_name, URI_PATH)."\n";
+               }
+       }
+       1;
+}
 
 
-sub _default_text ($$$) {
-       my ($ctx, $key, $txt) = @_;
+sub _default_text ($$$$) {
+       my ($ctx, $key, $hdr, $txt) = @_;
+       return _colors_help($ctx, $txt) if $key eq 'color';
+       return inbox_config($ctx, $hdr, $txt) if $key eq 'config';
        return if $key ne 'help'; # TODO more keys?
 
        my $ibx = $ctx->{-inbox};
        return if $key ne 'help'; # TODO more keys?
 
        my $ibx = $ctx->{-inbox};
@@ -134,7 +261,7 @@ search
     in other search engines.
 
     We also support search prefixes to limit the scope of the
     in other search engines.
 
     We also support search prefixes to limit the scope of the
-    search to certain fields using prefixes.
+    search to certain fields.
 
     Prefixes supported in this installation include:
 
 
     Prefixes supported in this installation include:
 
@@ -142,11 +269,20 @@ EOF
                _srch_prefix($srch, $txt);
 
                $$txt .= <<EOF;
                _srch_prefix($srch, $txt);
 
                $$txt .= <<EOF;
+
+    Most prefixes are probabilistic, meaning they support stemming
+    and wildcards ('*').  Ranges (such as 'd:') and boolean prefixes
+    do not support stemming or wildcards.
     The upstream Xapian query parser documentation fully explains
     the query syntax:
 
        $QP_URL
 
     The upstream Xapian query parser documentation fully explains
     the query syntax:
 
        $QP_URL
 
+EOF
+       } # $srch
+       my $over = $ibx->over;
+       if ($over) {
+               $$txt .= <<EOF;
 message threading
 -----------------
 
 message threading
 -----------------
 
@@ -178,6 +314,10 @@ message threading
        $WIKI_URL/Atom_(standard)
        https://tools.ietf.org/html/rfc4287
 
        $WIKI_URL/Atom_(standard)
        https://tools.ietf.org/html/rfc4287
 
+      Atom Threading Extensions (RFC4685) is supported:
+
+       https://tools.ietf.org/html/rfc4685
+
     Finally, the gzipped mbox for a thread is available for
     downloading and importing into your favorite mail client:
 
     Finally, the gzipped mbox for a thread is available for
     downloading and importing into your favorite mail client:
 
@@ -188,6 +328,10 @@ message threading
 
        $WIKI_URL/Mbox
 
 
        $WIKI_URL/Mbox
 
+EOF
+       } # $over
+
+       $$txt .= <<EOF;
 contact
 -------
 
 contact
 -------
 
@@ -196,7 +340,6 @@ contact
 
 EOF
        # TODO: support admin contact info in ~/.public-inbox/config
 
 EOF
        # TODO: support admin contact info in ~/.public-inbox/config
-       }
        1;
 }
 
        1;
 }