1 # Copyright (C) 2016-2019 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 our $QP_URL = 'https://xapian.org/docs/queryparser.html';
14 our $WIKI_URL = 'https://en.wikipedia.org/wiki';
16 require PublicInbox::HlMod;
17 PublicInbox::HlMod->new
20 # /$INBOX/_/text/$KEY/ # KEY may contain slashes
21 # For now, "help" is the only supported $KEY
26 $key = 'help' if !defined $key; # this 302s to _/text/help/
28 # get the raw text the same way we get mboxrds
29 my $raw = ($key =~ s!/raw\z!!);
30 my $have_tslash = ($key =~ s!/\z!!) if !$raw;
33 my $hdr = [ 'Content-Type', 'text/plain', 'Content-Length', undef ];
34 if (!_default_text($ctx, $key, $hdr, \$txt)) {
36 $txt = "404 Not Found ($key)\n";
39 $hdr->[3] = bytes::length($txt);
40 return [ $code, $hdr, [ $txt ] ]
43 # enforce trailing slash for "wget -r" compatibility
44 if (!$have_tslash && $code == 200) {
45 my $url = $ctx->{-inbox}->base_url($ctx->{env});
46 $url .= "_/text/$key/";
48 return [ 302, [ 'Content-Type', 'text/plain',
50 [ "Redirecting to $url\n" ] ];
53 # Follow git commit message conventions,
54 # first line is the Subject/title
55 my ($title) = ($txt =~ /\A([^\n]*)/s);
57 $ctx->{-title_html} = ascii_html($title);
59 my $nslash = ($key =~ tr!/!/!);
60 $ctx->{-upfx} = '../../../' . ('../' x $nslash);
62 PublicInbox::WwwStream->response($ctx, $code, sub {
64 $nr == 1 ? '<pre>'.$txt.'</pre>' : undef
69 my $l = PublicInbox::Linkify->new;
72 $hl->do_hl_text(\($_[0]));
74 $_[0] = ascii_html($_[0]);
76 $_[0] = $l->linkify_2($_[0]);
79 sub _srch_prefix ($$) {
80 my ($srch, $txt) = @_;
83 my $help = $srch->help;
85 for ($i = 0; $i < @$help; $i += 2) {
86 my $pfx = $help->[$i];
88 $pad = $n if $n > $pad;
90 $htxt .= $help->[$i + 1];
94 my $padding = ' ' x ($pad + 8);
95 $htxt =~ s/^/$padding/gms;
96 $htxt =~ s/^$padding(\S+)\0/" $1".
97 (' ' x ($pad - length($1)))/egms;
98 $htxt =~ s/\f\n/\n/gs;
103 sub _colors_help ($$) {
104 my ($ctx, $txt) = @_;
105 my $ibx = $ctx->{-inbox};
106 my $env = $ctx->{env};
107 my $base_url = $ibx->base_url($env);
108 $$txt .= "color customization for $base_url\n";
111 public-inbox provides a stable set of CSS classes for users to
112 customize colors for highlighting diffs and code.
114 Users of browsers such as dillo, Firefox, or some browser
115 extensions may start by downloading the following sample CSS file
116 to control the colors they see:
118 ${base_url}userContent.css
124 $$txt .= PublicInbox::UserContent::sample($ibx, $env) . "```\n";
127 # git-config section names are quoted in the config file, so escape them
130 $name =~ s/\\/\\\\/g;
135 sub URI_PATH () { '^A-Za-z0-9\-\._~/' }
137 # n.b. this is a perfect candidate for memoization
138 sub inbox_config ($$$) {
139 my ($ctx, $hdr, $txt) = @_;
140 my $ibx = $ctx->{-inbox};
141 push @$hdr, 'Content-Disposition', 'inline; filename=inbox.config';
142 my $name = dq_escape($ibx->{name});
144 ; example public-inbox config snippet for "$name"
145 ; see public-inbox-config(5) manpage for more details:
146 ; https://public-inbox.org/public-inbox-config.html
147 [publicinbox "$name"]
148 mainrepo = /path/to/top-level-inbox
150 for my $k (qw(address)) {
151 defined(my $v = $ibx->{$k}) or next;
152 $$txt .= "\t$k = $_\n" for @$v;
155 for my $k (qw(filter infourl newsgroup obfuscate replyto watchheader)) {
156 defined(my $v = $ibx->{$k}) or next;
157 $$txt .= "\t$k = $v\n";
159 $$txt .= "\tnntpmirror = $_\n" for (@{$ibx->nntp_url});
161 # note: this doesn't preserve cgitrc layout, since we parse cgitrc
162 # and drop the original structure
163 if (defined(my $cr = $ibx->{coderepo})) {
164 $$txt .= "\tcoderepo = $_\n" for @$cr;
166 my $pi_config = $ctx->{www}->{pi_config};
167 for my $cr_name (@$cr) {
168 my $url = $pi_config->{"coderepo.$cr_name.cgiturl"};
169 my $path = "/path/to/$cr_name";
170 $cr_name = dq_escape($cr_name);
172 $$txt .= qq([coderepo "$cr_name"]\n);
175 if ($path !~ m![a-z0-9_/\.\-]!i) {
176 $cpath = dq_escape($cpath);
178 $$txt .= qq(\t; git clone $url "$cpath"\n);
180 $$txt .= "\tdir = $path\n";
181 $$txt .= "\tcgiturl = https://example.com/";
182 $$txt .= uri_escape_utf8($cr_name, URI_PATH)."\n";
188 sub _default_text ($$$$) {
189 my ($ctx, $key, $hdr, $txt) = @_;
190 return _colors_help($ctx, $txt) if $key eq 'color';
191 return inbox_config($ctx, $hdr, $txt) if $key eq 'config';
192 return if $key ne 'help'; # TODO more keys?
194 my $ibx = $ctx->{-inbox};
195 my $base_url = $ibx->base_url($ctx->{env});
196 $$txt .= "public-inbox help for $base_url\n";
202 public-inbox uses Message-ID identifiers in URLs.
203 One may look up messages by substituting Message-IDs
204 (without the leading '<' or trailing '>') into the URL.
205 Forward slash ('/') characters in the Message-IDs
206 need to be escaped as "%2F" (without quotes).
208 Thus, it is possible to retrieve any message by its
209 Message-ID by going to:
211 $base_url<Message-ID>/
213 (without the '<' or '>')
215 Message-IDs are described at:
221 # n.b. we use the Xapian DB for any regeneratable,
222 # order-of-arrival-independent data.
223 my $srch = $ibx->search;
229 This public-inbox has search functionality provided by Xapian.
231 It supports typical AND, OR, NOT, '+', '-' queries present
232 in other search engines.
234 We also support search prefixes to limit the scope of the
235 search to certain fields.
237 Prefixes supported in this installation include:
240 _srch_prefix($srch, $txt);
244 Most prefixes are probabilistic, meaning they support stemming
245 and wildcards ('*'). Ranges (such as 'd:') and boolean prefixes
246 do not support stemming or wildcards.
247 The upstream Xapian query parser documentation fully explains
255 Message threading is enabled for this public-inbox,
256 additional endpoints for message threads are available:
258 * $base_url<Message-ID>/T/#u
260 Loads the thread belonging to the given <Message-ID>
261 in flat chronological order. The "#u" anchor
262 focuses the browser on the given <Message-ID>.
264 * $base_url<Message-ID>/t/#u
266 Loads the thread belonging to the given <Message-ID>
267 in threaded order with nesting. For deep threads,
268 this requires a wide display or horizontal scrolling.
270 Both of these HTML endpoints are suitable for offline reading
271 using the thread overview at the bottom of each page.
273 Users of feed readers may follow a particular thread using:
275 * $base_url<Message-ID>/t.atom
277 Which loads the thread in Atom Syndication Standard
278 described at Wikipedia and RFC4287:
280 $WIKI_URL/Atom_(standard)
281 https://tools.ietf.org/html/rfc4287
283 Atom Threading Extensions (RFC4685) is supported:
285 https://tools.ietf.org/html/rfc4685
287 Finally, the gzipped mbox for a thread is available for
288 downloading and importing into your favorite mail client:
290 * $base_url<Message-ID>/t.mbox.gz
292 We use the mboxrd variant of the mbox format described
300 This help text is maintained by public-inbox developers
301 reachable via plain-text email at: meta\@public-inbox.org
304 # TODO: support admin contact info in ~/.public-inbox/config