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 inboxdir = /path/to/top-level-inbox
149 ; note: public-inbox before v1.2.0 used "mainrepo"
150 ; instead of "inboxdir", both remain supported after 1.2
151 mainrepo = /path/to/top-level-inbox
153 for my $k (qw(address listid)) {
154 defined(my $v = $ibx->{$k}) or next;
155 $$txt .= "\t$k = $_\n" for @$v;
158 for my $k (qw(filter infourl newsgroup obfuscate replyto watchheader)) {
159 defined(my $v = $ibx->{$k}) or next;
160 $$txt .= "\t$k = $v\n";
162 $$txt .= "\tnntpmirror = $_\n" for (@{$ibx->nntp_url});
164 # note: this doesn't preserve cgitrc layout, since we parse cgitrc
165 # and drop the original structure
166 if (defined(my $cr = $ibx->{coderepo})) {
167 $$txt .= "\tcoderepo = $_\n" for @$cr;
169 my $pi_config = $ctx->{www}->{pi_config};
170 for my $cr_name (@$cr) {
171 my $url = $pi_config->{"coderepo.$cr_name.cgiturl"};
172 my $path = "/path/to/$cr_name";
173 $cr_name = dq_escape($cr_name);
175 $$txt .= qq([coderepo "$cr_name"]\n);
178 if ($path !~ m![a-z0-9_/\.\-]!i) {
179 $cpath = dq_escape($cpath);
181 $$txt .= qq(\t; git clone $url "$cpath"\n);
183 $$txt .= "\tdir = $path\n";
184 $$txt .= "\tcgiturl = https://example.com/";
185 $$txt .= uri_escape_utf8($cr_name, URI_PATH)."\n";
191 sub _default_text ($$$$) {
192 my ($ctx, $key, $hdr, $txt) = @_;
193 return _colors_help($ctx, $txt) if $key eq 'color';
194 return inbox_config($ctx, $hdr, $txt) if $key eq 'config';
195 return if $key ne 'help'; # TODO more keys?
197 my $ibx = $ctx->{-inbox};
198 my $base_url = $ibx->base_url($ctx->{env});
199 $$txt .= "public-inbox help for $base_url\n";
205 public-inbox uses Message-ID identifiers in URLs.
206 One may look up messages by substituting Message-IDs
207 (without the leading '<' or trailing '>') into the URL.
208 Forward slash ('/') characters in the Message-IDs
209 need to be escaped as "%2F" (without quotes).
211 Thus, it is possible to retrieve any message by its
212 Message-ID by going to:
214 $base_url<Message-ID>/
216 (without the '<' or '>')
218 Message-IDs are described at:
224 # n.b. we use the Xapian DB for any regeneratable,
225 # order-of-arrival-independent data.
226 my $srch = $ibx->search;
232 This public-inbox has search functionality provided by Xapian.
234 It supports typical AND, OR, NOT, '+', '-' queries present
235 in other search engines.
237 We also support search prefixes to limit the scope of the
238 search to certain fields.
240 Prefixes supported in this installation include:
243 _srch_prefix($srch, $txt);
247 Most prefixes are probabilistic, meaning they support stemming
248 and wildcards ('*'). Ranges (such as 'd:') and boolean prefixes
249 do not support stemming or wildcards.
250 The upstream Xapian query parser documentation fully explains
258 Message threading is enabled for this public-inbox,
259 additional endpoints for message threads are available:
261 * $base_url<Message-ID>/T/#u
263 Loads the thread belonging to the given <Message-ID>
264 in flat chronological order. The "#u" anchor
265 focuses the browser on the given <Message-ID>.
267 * $base_url<Message-ID>/t/#u
269 Loads the thread belonging to the given <Message-ID>
270 in threaded order with nesting. For deep threads,
271 this requires a wide display or horizontal scrolling.
273 Both of these HTML endpoints are suitable for offline reading
274 using the thread overview at the bottom of each page.
276 Users of feed readers may follow a particular thread using:
278 * $base_url<Message-ID>/t.atom
280 Which loads the thread in Atom Syndication Standard
281 described at Wikipedia and RFC4287:
283 $WIKI_URL/Atom_(standard)
284 https://tools.ietf.org/html/rfc4287
286 Atom Threading Extensions (RFC4685) is supported:
288 https://tools.ietf.org/html/rfc4685
290 Finally, the gzipped mbox for a thread is available for
291 downloading and importing into your favorite mail client:
293 * $base_url<Message-ID>/t.mbox.gz
295 We use the mboxrd variant of the mbox format described
303 This help text is maintained by public-inbox developers
304 reachable via plain-text email at: meta\@public-inbox.org
307 # TODO: support admin contact info in ~/.public-inbox/config