1 # Copyright (C) 2016-2020 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
4 # used for displaying help texts and other non-mail content
5 package PublicInbox::WwwText;
8 use bytes (); # only for bytes::length
9 use PublicInbox::Linkify;
10 use PublicInbox::WwwStream;
11 use PublicInbox::Hval qw(ascii_html);
12 use URI::Escape qw(uri_escape_utf8);
13 use PublicInbox::GzipFilter qw(gzf_maybe);
14 use Compress::Raw::Zlib qw(Z_FINISH Z_OK);
15 our $QP_URL = 'https://xapian.org/docs/queryparser.html';
16 our $WIKI_URL = 'https://en.wikipedia.org/wiki';
18 require PublicInbox::HlMod;
19 PublicInbox::HlMod->new
22 # /$INBOX/_/text/$KEY/ # KEY may contain slashes
23 # For now, "help" is the only supported $KEY
28 $key = 'help' if !defined $key; # this 302s to _/text/help/
30 # get the raw text the same way we get mboxrds
31 my $raw = ($key =~ s!/raw\z!!);
32 my $have_tslash = ($key =~ s!/\z!!) if !$raw;
35 my $hdr = [ 'Content-Type', 'text/plain', 'Content-Length', undef ];
36 if (!_default_text($ctx, $key, $hdr, \$txt)) {
38 $txt = "404 Not Found ($key)\n";
40 my $env = $ctx->{env};
43 if (my $gzf = $code == 200 ? gzf_maybe($hdr, $env) : undef) {
44 my $zbuf = $gzf->translate($txt);
46 $body = [ $zbuf .= $gzf->translate(undef) ];
50 $hdr->[3] = bytes::length($body->[0]);
51 return [ $code, $hdr, $body ]
54 # enforce trailing slash for "wget -r" compatibility
55 if (!$have_tslash && $code == 200) {
56 my $url = $ctx->{-inbox}->base_url($env);
57 $url .= "_/text/$key/";
59 return [ 302, [ 'Content-Type', 'text/plain',
61 [ "Redirecting to $url\n" ] ];
64 # Follow git commit message conventions,
65 # first line is the Subject/title
66 my ($title) = ($txt =~ /\A([^\n]*)/s);
67 $ctx->{-title_html} = ascii_html($title);
68 my $nslash = ($key =~ tr!/!/!);
69 $ctx->{-upfx} = '../../../' . ('../' x $nslash);
70 my $l = PublicInbox::Linkify->new;
73 $hl->do_hl_text(\$txt);
75 $txt = ascii_html($txt);
77 $txt = '<pre>' . $l->linkify_2($txt) . '</pre>';
78 PublicInbox::WwwStream::html_oneshot($ctx, $code, \$txt);
81 sub _srch_prefix ($$) {
82 my ($srch, $txt) = @_;
85 my $help = $srch->help;
87 for ($i = 0; $i < @$help; $i += 2) {
88 my $pfx = $help->[$i];
90 $pad = $n if $n > $pad;
92 $htxt .= $help->[$i + 1];
96 my $padding = ' ' x ($pad + 8);
97 $htxt =~ s/^/$padding/gms;
98 $htxt =~ s/^$padding(\S+)\0/" $1".
99 (' ' x ($pad - length($1)))/egms;
100 $htxt =~ s/\f\n/\n/gs;
105 sub _colors_help ($$) {
106 my ($ctx, $txt) = @_;
107 my $ibx = $ctx->{-inbox};
108 my $env = $ctx->{env};
109 my $base_url = $ibx->base_url($env);
110 $$txt .= "color customization for $base_url\n";
113 public-inbox provides a stable set of CSS classes for users to
114 customize colors for highlighting diffs and code.
116 Users of browsers such as dillo, Firefox, or some browser
117 extensions may start by downloading the following sample CSS file
118 to control the colors they see:
120 ${base_url}userContent.css
126 $$txt .= PublicInbox::UserContent::sample($ibx, $env) . "```\n";
129 # git-config section names are quoted in the config file, so escape them
132 $name =~ s/\\/\\\\/g;
137 sub URI_PATH () { '^A-Za-z0-9\-\._~/' }
139 # n.b. this is a perfect candidate for memoization
140 sub inbox_config ($$$) {
141 my ($ctx, $hdr, $txt) = @_;
142 my $ibx = $ctx->{-inbox};
143 push @$hdr, 'Content-Disposition', 'inline; filename=inbox.config';
144 my $name = dq_escape($ibx->{name});
145 my $inboxdir = '/path/to/top-level-inbox';
147 ; example public-inbox config snippet for "$name"
148 ; see public-inbox-config(5) manpage for more details:
149 ; https://public-inbox.org/public-inbox-config.html
150 [publicinbox "$name"]
152 ; note: public-inbox before v1.2.0 used "mainrepo"
153 ; instead of "inboxdir", both remain supported after 1.2
155 url = https://example.com/$name/
156 url = http://example.onion/$name/
158 for my $k (qw(address listid infourl watchheader)) {
159 defined(my $v = $ibx->{$k}) or next;
160 $$txt .= "\t$k = $_\n" for @$v;
162 if (my $altid = $ibx->{altid}) {
163 my $base_url = $ibx->base_url($ctx->{env});
164 my $altid_map = $ibx->altid_map;
166 ; altid DBs may be used to provide numeric article ID lookup from
167 ; old, pre-existing sources. You can recreate them via curl(1),
168 ; gzip(1), and sqlite3(1) as documented:
170 for (sort keys %$altid_map) {
171 $$txt .= "\t;\tcurl -XPOST $base_url$_.sql.gz | \\\n" .
172 "\t;\tgzip -dc | \\\n" .
173 "\t;\tsqlite3 $inboxdir/$_.sqlite3\n";
174 $$txt .= "\taltid = serial:$_:file=$_.sqlite3\n";
178 for my $k (qw(filter newsgroup obfuscate replyto)) {
179 defined(my $v = $ibx->{$k}) or next;
180 $$txt .= "\t$k = $v\n";
182 $$txt .= "\tnntpmirror = $_\n" for (@{$ibx->nntp_url});
184 # note: this doesn't preserve cgitrc layout, since we parse cgitrc
185 # and drop the original structure
186 if (defined(my $cr = $ibx->{coderepo})) {
187 $$txt .= "\tcoderepo = $_\n" for @$cr;
189 my $pi_config = $ctx->{www}->{pi_config};
190 for my $cr_name (@$cr) {
191 my $urls = $pi_config->{"coderepo.$cr_name.cgiturl"};
192 my $path = "/path/to/$cr_name";
193 $cr_name = dq_escape($cr_name);
195 $$txt .= qq([coderepo "$cr_name"]\n);
196 if ($urls && scalar(@$urls)) {
198 $$txt .= join(" ||\n\t;\t", map {;
200 if ($path !~ m![a-z0-9_/\.\-]!i) {
201 $cpath = dq_escape($cpath);
203 qq(git clone $_ "$cpath");
207 $$txt .= "\tdir = $path\n";
208 $$txt .= "\tcgiturl = https://example.com/";
209 $$txt .= uri_escape_utf8($cr_name, URI_PATH)."\n";
215 sub _default_text ($$$$) {
216 my ($ctx, $key, $hdr, $txt) = @_;
217 return _colors_help($ctx, $txt) if $key eq 'color';
218 return inbox_config($ctx, $hdr, $txt) if $key eq 'config';
219 return if $key ne 'help'; # TODO more keys?
221 my $ibx = $ctx->{-inbox};
222 my $base_url = $ibx->base_url($ctx->{env});
223 $$txt .= "public-inbox help for $base_url\n";
229 public-inbox uses Message-ID identifiers in URLs.
230 One may look up messages by substituting Message-IDs
231 (without the leading '<' or trailing '>') into the URL.
232 Forward slash ('/') characters in the Message-IDs
233 need to be escaped as "%2F" (without quotes).
235 Thus, it is possible to retrieve any message by its
236 Message-ID by going to:
238 $base_url<Message-ID>/
240 (without the '<' or '>')
242 Message-IDs are described at:
248 # n.b. we use the Xapian DB for any regeneratable,
249 # order-of-arrival-independent data.
250 my $srch = $ibx->search;
256 This public-inbox has search functionality provided by Xapian.
258 It supports typical AND, OR, NOT, '+', '-' queries present
259 in other search engines.
261 We also support search prefixes to limit the scope of the
262 search to certain fields.
264 Prefixes supported in this installation include:
267 _srch_prefix($srch, $txt);
271 Most prefixes are probabilistic, meaning they support stemming
272 and wildcards ('*'). Ranges (such as 'd:') and boolean prefixes
273 do not support stemming or wildcards.
274 The upstream Xapian query parser documentation fully explains
281 my $over = $ibx->over;
287 Message threading is enabled for this public-inbox,
288 additional endpoints for message threads are available:
290 * $base_url<Message-ID>/T/#u
292 Loads the thread belonging to the given <Message-ID>
293 in flat chronological order. The "#u" anchor
294 focuses the browser on the given <Message-ID>.
296 * $base_url<Message-ID>/t/#u
298 Loads the thread belonging to the given <Message-ID>
299 in threaded order with nesting. For deep threads,
300 this requires a wide display or horizontal scrolling.
302 Both of these HTML endpoints are suitable for offline reading
303 using the thread overview at the bottom of each page.
305 Users of feed readers may follow a particular thread using:
307 * $base_url<Message-ID>/t.atom
309 Which loads the thread in Atom Syndication Standard
310 described at Wikipedia and RFC4287:
312 $WIKI_URL/Atom_(standard)
313 https://tools.ietf.org/html/rfc4287
315 Atom Threading Extensions (RFC4685) is supported:
317 https://tools.ietf.org/html/rfc4685
319 Finally, the gzipped mbox for a thread is available for
320 downloading and importing into your favorite mail client:
322 * $base_url<Message-ID>/t.mbox.gz
324 We use the mboxrd variant of the mbox format described
336 This help text is maintained by public-inbox developers
337 reachable via plain-text email at: meta\@public-inbox.org
340 # TODO: support admin contact info in ~/.public-inbox/config