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 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);
58 my $nslash = ($key =~ tr!/!/!);
59 $ctx->{-upfx} = '../../../' . ('../' x $nslash);
60 PublicInbox::WwwStream->response($ctx, $code, \&_do_linkify);
65 return unless $nr == 1;
66 my $l = PublicInbox::Linkify->new;
67 my $txt = delete $ctx->{txt};
70 $hl->do_hl_text($txt);
72 $$txt = ascii_html($$txt);
74 '<pre>' . $l->linkify_2($$txt) . '</pre>';
77 sub _srch_prefix ($$) {
78 my ($srch, $txt) = @_;
81 my $help = $srch->help;
83 for ($i = 0; $i < @$help; $i += 2) {
84 my $pfx = $help->[$i];
86 $pad = $n if $n > $pad;
88 $htxt .= $help->[$i + 1];
92 my $padding = ' ' x ($pad + 8);
93 $htxt =~ s/^/$padding/gms;
94 $htxt =~ s/^$padding(\S+)\0/" $1".
95 (' ' x ($pad - length($1)))/egms;
96 $htxt =~ s/\f\n/\n/gs;
101 sub _colors_help ($$) {
102 my ($ctx, $txt) = @_;
103 my $ibx = $ctx->{-inbox};
104 my $env = $ctx->{env};
105 my $base_url = $ibx->base_url($env);
106 $$txt .= "color customization for $base_url\n";
109 public-inbox provides a stable set of CSS classes for users to
110 customize colors for highlighting diffs and code.
112 Users of browsers such as dillo, Firefox, or some browser
113 extensions may start by downloading the following sample CSS file
114 to control the colors they see:
116 ${base_url}userContent.css
122 $$txt .= PublicInbox::UserContent::sample($ibx, $env) . "```\n";
125 # git-config section names are quoted in the config file, so escape them
128 $name =~ s/\\/\\\\/g;
133 sub URI_PATH () { '^A-Za-z0-9\-\._~/' }
135 # n.b. this is a perfect candidate for memoization
136 sub inbox_config ($$$) {
137 my ($ctx, $hdr, $txt) = @_;
138 my $ibx = $ctx->{-inbox};
139 push @$hdr, 'Content-Disposition', 'inline; filename=inbox.config';
140 my $name = dq_escape($ibx->{name});
142 ; example public-inbox config snippet for "$name"
143 ; see public-inbox-config(5) manpage for more details:
144 ; https://public-inbox.org/public-inbox-config.html
145 [publicinbox "$name"]
146 inboxdir = /path/to/top-level-inbox
147 ; note: public-inbox before v1.2.0 used "mainrepo"
148 ; instead of "inboxdir", both remain supported after 1.2
149 mainrepo = /path/to/top-level-inbox
150 url = https://example.com/$name/
151 url = http://example.onion/$name/
153 for my $k (qw(address listid infourl)) {
154 defined(my $v = $ibx->{$k}) or next;
155 $$txt .= "\t$k = $_\n" for @$v;
158 for my $k (qw(filter 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 $urls = $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);
176 if ($urls && scalar(@$urls)) {
178 $$txt .= join(" ||\n\t;\t", map {;
180 if ($path !~ m![a-z0-9_/\.\-]!i) {
181 $cpath = dq_escape($cpath);
183 qq(git clone $_ "$cpath");
187 $$txt .= "\tdir = $path\n";
188 $$txt .= "\tcgiturl = https://example.com/";
189 $$txt .= uri_escape_utf8($cr_name, URI_PATH)."\n";
195 sub _default_text ($$$$) {
196 my ($ctx, $key, $hdr, $txt) = @_;
197 return _colors_help($ctx, $txt) if $key eq 'color';
198 return inbox_config($ctx, $hdr, $txt) if $key eq 'config';
199 return if $key ne 'help'; # TODO more keys?
201 my $ibx = $ctx->{-inbox};
202 my $base_url = $ibx->base_url($ctx->{env});
203 $$txt .= "public-inbox help for $base_url\n";
209 public-inbox uses Message-ID identifiers in URLs.
210 One may look up messages by substituting Message-IDs
211 (without the leading '<' or trailing '>') into the URL.
212 Forward slash ('/') characters in the Message-IDs
213 need to be escaped as "%2F" (without quotes).
215 Thus, it is possible to retrieve any message by its
216 Message-ID by going to:
218 $base_url<Message-ID>/
220 (without the '<' or '>')
222 Message-IDs are described at:
228 # n.b. we use the Xapian DB for any regeneratable,
229 # order-of-arrival-independent data.
230 my $srch = $ibx->search;
236 This public-inbox has search functionality provided by Xapian.
238 It supports typical AND, OR, NOT, '+', '-' queries present
239 in other search engines.
241 We also support search prefixes to limit the scope of the
242 search to certain fields.
244 Prefixes supported in this installation include:
247 _srch_prefix($srch, $txt);
251 Most prefixes are probabilistic, meaning they support stemming
252 and wildcards ('*'). Ranges (such as 'd:') and boolean prefixes
253 do not support stemming or wildcards.
254 The upstream Xapian query parser documentation fully explains
262 Message threading is enabled for this public-inbox,
263 additional endpoints for message threads are available:
265 * $base_url<Message-ID>/T/#u
267 Loads the thread belonging to the given <Message-ID>
268 in flat chronological order. The "#u" anchor
269 focuses the browser on the given <Message-ID>.
271 * $base_url<Message-ID>/t/#u
273 Loads the thread belonging to the given <Message-ID>
274 in threaded order with nesting. For deep threads,
275 this requires a wide display or horizontal scrolling.
277 Both of these HTML endpoints are suitable for offline reading
278 using the thread overview at the bottom of each page.
280 Users of feed readers may follow a particular thread using:
282 * $base_url<Message-ID>/t.atom
284 Which loads the thread in Atom Syndication Standard
285 described at Wikipedia and RFC4287:
287 $WIKI_URL/Atom_(standard)
288 https://tools.ietf.org/html/rfc4287
290 Atom Threading Extensions (RFC4685) is supported:
292 https://tools.ietf.org/html/rfc4685
294 Finally, the gzipped mbox for a thread is available for
295 downloading and importing into your favorite mail client:
297 * $base_url<Message-ID>/t.mbox.gz
299 We use the mboxrd variant of the mbox format described
307 This help text is maintained by public-inbox developers
308 reachable via plain-text email at: meta\@public-inbox.org
311 # TODO: support admin contact info in ~/.public-inbox/config