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 our $QP_URL = 'https://xapian.org/docs/queryparser.html';
15 our $WIKI_URL = 'https://en.wikipedia.org/wiki';
17 require PublicInbox::HlMod;
18 PublicInbox::HlMod->new
21 # /$INBOX/_/text/$KEY/ # KEY may contain slashes
22 # For now, "help" is the only supported $KEY
27 $key = 'help' if !defined $key; # this 302s to _/text/help/
29 # get the raw text the same way we get mboxrds
30 my $raw = ($key =~ s!/raw\z!!);
31 my $have_tslash = ($key =~ s!/\z!!) if !$raw;
34 my $hdr = [ 'Content-Type', 'text/plain', 'Content-Length', undef ];
35 if (!_default_text($ctx, $key, $hdr, \$txt)) {
37 $txt = "404 Not Found ($key)\n";
39 my $env = $ctx->{env};
42 if (my $gzf = $code == 200 ? gzf_maybe($hdr, $env) : undef) {
43 my $zbuf = $gzf->translate($txt);
45 $body = [ $zbuf .= $gzf->translate(undef) ];
49 $hdr->[3] = bytes::length($body->[0]);
50 return [ $code, $hdr, $body ]
53 # enforce trailing slash for "wget -r" compatibility
54 if (!$have_tslash && $code == 200) {
55 my $url = $ctx->{-inbox}->base_url($env);
56 $url .= "_/text/$key/";
58 return [ 302, [ 'Content-Type', 'text/plain',
60 [ "Redirecting to $url\n" ] ];
63 # Follow git commit message conventions,
64 # first line is the Subject/title
65 my ($title) = ($txt =~ /\A([^\n]*)/s);
66 $ctx->{-title_html} = ascii_html($title);
67 my $nslash = ($key =~ tr!/!/!);
68 $ctx->{-upfx} = '../../../' . ('../' x $nslash);
69 my $l = PublicInbox::Linkify->new;
72 $hl->do_hl_text(\$txt);
74 $txt = ascii_html($txt);
76 $txt = '<pre>' . $l->linkify_2($txt) . '</pre>';
77 PublicInbox::WwwStream::html_oneshot($ctx, $code, \$txt);
80 sub _srch_prefix ($$) {
81 my ($srch, $txt) = @_;
84 my $help = $srch->help;
86 for ($i = 0; $i < @$help; $i += 2) {
87 my $pfx = $help->[$i];
89 $pad = $n if $n > $pad;
91 $htxt .= $help->[$i + 1];
95 my $padding = ' ' x ($pad + 8);
96 $htxt =~ s/^/$padding/gms;
97 $htxt =~ s/^$padding(\S+)\0/" $1".
98 (' ' x ($pad - length($1)))/egms;
99 $htxt =~ s/\f\n/\n/gs;
104 sub _colors_help ($$) {
105 my ($ctx, $txt) = @_;
106 my $ibx = $ctx->{-inbox};
107 my $env = $ctx->{env};
108 my $base_url = $ibx->base_url($env);
109 $$txt .= "color customization for $base_url\n";
112 public-inbox provides a stable set of CSS classes for users to
113 customize colors for highlighting diffs and code.
115 Users of browsers such as dillo, Firefox, or some browser
116 extensions may start by downloading the following sample CSS file
117 to control the colors they see:
119 ${base_url}userContent.css
125 $$txt .= PublicInbox::UserContent::sample($ibx, $env) . "```\n";
128 # git-config section names are quoted in the config file, so escape them
131 $name =~ s/\\/\\\\/g;
136 sub URI_PATH () { '^A-Za-z0-9\-\._~/' }
138 # n.b. this is a perfect candidate for memoization
139 sub inbox_config ($$$) {
140 my ($ctx, $hdr, $txt) = @_;
141 my $ibx = $ctx->{-inbox};
142 push @$hdr, 'Content-Disposition', 'inline; filename=inbox.config';
143 my $name = dq_escape($ibx->{name});
144 my $inboxdir = '/path/to/top-level-inbox';
146 ; example public-inbox config snippet for "$name"
147 ; see public-inbox-config(5) manpage for more details:
148 ; https://public-inbox.org/public-inbox-config.html
149 [publicinbox "$name"]
151 ; note: public-inbox before v1.2.0 used "mainrepo"
152 ; instead of "inboxdir", both remain supported after 1.2
154 url = https://example.com/$name/
155 url = http://example.onion/$name/
157 for my $k (qw(address listid infourl watchheader)) {
158 defined(my $v = $ibx->{$k}) or next;
159 $$txt .= "\t$k = $_\n" for @$v;
161 if (my $altid = $ibx->{altid}) {
162 my $base_url = $ibx->base_url($ctx->{env});
163 my $altid_map = $ibx->altid_map;
165 ; altid DBs may be used to provide numeric article ID lookup from
166 ; old, pre-existing sources. You can recreate them via curl(1),
167 ; gzip(1), and sqlite3(1) as documented:
169 for (sort keys %$altid_map) {
170 $$txt .= "\t;\tcurl -XPOST $base_url$_.sql.gz | \\\n" .
171 "\t;\tgzip -dc | \\\n" .
172 "\t;\tsqlite3 $inboxdir/$_.sqlite3\n";
173 $$txt .= "\taltid = serial:$_:file=$_.sqlite3\n";
177 for my $k (qw(filter newsgroup obfuscate replyto)) {
178 defined(my $v = $ibx->{$k}) or next;
179 $$txt .= "\t$k = $v\n";
181 $$txt .= "\tnntpmirror = $_\n" for (@{$ibx->nntp_url});
183 # note: this doesn't preserve cgitrc layout, since we parse cgitrc
184 # and drop the original structure
185 if (defined(my $cr = $ibx->{coderepo})) {
186 $$txt .= "\tcoderepo = $_\n" for @$cr;
188 my $pi_config = $ctx->{www}->{pi_config};
189 for my $cr_name (@$cr) {
190 my $urls = $pi_config->{"coderepo.$cr_name.cgiturl"};
191 my $path = "/path/to/$cr_name";
192 $cr_name = dq_escape($cr_name);
194 $$txt .= qq([coderepo "$cr_name"]\n);
195 if ($urls && scalar(@$urls)) {
197 $$txt .= join(" ||\n\t;\t", map {;
199 if ($path !~ m![a-z0-9_/\.\-]!i) {
200 $cpath = dq_escape($cpath);
202 qq(git clone $_ "$cpath");
206 $$txt .= "\tdir = $path\n";
207 $$txt .= "\tcgiturl = https://example.com/";
208 $$txt .= uri_escape_utf8($cr_name, URI_PATH)."\n";
214 sub _default_text ($$$$) {
215 my ($ctx, $key, $hdr, $txt) = @_;
216 return _colors_help($ctx, $txt) if $key eq 'color';
217 return inbox_config($ctx, $hdr, $txt) if $key eq 'config';
218 return if $key ne 'help'; # TODO more keys?
220 my $ibx = $ctx->{-inbox};
221 my $base_url = $ibx->base_url($ctx->{env});
222 $$txt .= "public-inbox help for $base_url\n";
228 public-inbox uses Message-ID identifiers in URLs.
229 One may look up messages by substituting Message-IDs
230 (without the leading '<' or trailing '>') into the URL.
231 Forward slash ('/') characters in the Message-IDs
232 need to be escaped as "%2F" (without quotes).
234 Thus, it is possible to retrieve any message by its
235 Message-ID by going to:
237 $base_url<Message-ID>/
239 (without the '<' or '>')
241 Message-IDs are described at:
247 # n.b. we use the Xapian DB for any regeneratable,
248 # order-of-arrival-independent data.
249 my $srch = $ibx->search;
255 This public-inbox has search functionality provided by Xapian.
257 It supports typical AND, OR, NOT, '+', '-' queries present
258 in other search engines.
260 We also support search prefixes to limit the scope of the
261 search to certain fields.
263 Prefixes supported in this installation include:
266 _srch_prefix($srch, $txt);
270 Most prefixes are probabilistic, meaning they support stemming
271 and wildcards ('*'). Ranges (such as 'd:') and boolean prefixes
272 do not support stemming or wildcards.
273 The upstream Xapian query parser documentation fully explains
280 my $over = $ibx->over;
286 Message threading is enabled for this public-inbox,
287 additional endpoints for message threads are available:
289 * $base_url<Message-ID>/T/#u
291 Loads the thread belonging to the given <Message-ID>
292 in flat chronological order. The "#u" anchor
293 focuses the browser on the given <Message-ID>.
295 * $base_url<Message-ID>/t/#u
297 Loads the thread belonging to the given <Message-ID>
298 in threaded order with nesting. For deep threads,
299 this requires a wide display or horizontal scrolling.
301 Both of these HTML endpoints are suitable for offline reading
302 using the thread overview at the bottom of each page.
304 Users of feed readers may follow a particular thread using:
306 * $base_url<Message-ID>/t.atom
308 Which loads the thread in Atom Syndication Standard
309 described at Wikipedia and RFC4287:
311 $WIKI_URL/Atom_(standard)
312 https://tools.ietf.org/html/rfc4287
314 Atom Threading Extensions (RFC4685) is supported:
316 https://tools.ietf.org/html/rfc4685
318 Finally, the gzipped mbox for a thread is available for
319 downloading and importing into your favorite mail client:
321 * $base_url<Message-ID>/t.mbox.gz
323 We use the mboxrd variant of the mbox format described
335 This help text is maintained by public-inbox developers
336 reachable via plain-text email at: meta\@public-inbox.org
339 # TODO: support admin contact info in ~/.public-inbox/config