]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/WwwText.pm
2e4aeec0c19e6f85fe8782454d84f33e2aa25f8d
[public-inbox.git] / lib / PublicInbox / WwwText.pm
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>
3
4 # used for displaying help texts and other non-mail content
5 package PublicInbox::WwwText;
6 use strict;
7 use warnings;
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';
15 my $hl = eval {
16         require PublicInbox::HlMod;
17         PublicInbox::HlMod->new
18 };
19
20 # /$INBOX/_/text/$KEY/ # KEY may contain slashes
21 # For now, "help" is the only supported $KEY
22 sub get_text {
23         my ($ctx, $key) = @_;
24         my $code = 200;
25
26         $key = 'help' if !defined $key; # this 302s to _/text/help/
27
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;
31
32         my $txt = '';
33         my $hdr = [ 'Content-Type', 'text/plain', 'Content-Length', undef ];
34         if (!_default_text($ctx, $key, $hdr, \$txt)) {
35                 $code = 404;
36                 $txt = "404 Not Found ($key)\n";
37         }
38         if ($raw) {
39                 $hdr->[3] = bytes::length($txt);
40                 return [ $code, $hdr, [ $txt ] ]
41         }
42
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/";
47
48                 return [ 302, [ 'Content-Type', 'text/plain',
49                                 'Location', $url ],
50                         [ "Redirecting to $url\n" ] ];
51         }
52
53         # Follow git commit message conventions,
54         # first line is the Subject/title
55         my ($title) = ($txt =~ /\A([^\n]*)/s);
56         $ctx->{txt} = \$txt;
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);
61 }
62
63 sub _do_linkify {
64         my ($nr, $ctx) = @_;
65         return unless $nr == 1;
66         my $l = PublicInbox::Linkify->new;
67         my $txt = delete $ctx->{txt};
68         $l->linkify_1($$txt);
69         if ($hl) {
70                 $hl->do_hl_text($txt);
71         } else {
72                 $$txt = ascii_html($$txt);
73         }
74         '<pre>' . $l->linkify_2($$txt) . '</pre>';
75 }
76
77 sub _srch_prefix ($$) {
78         my ($srch, $txt) = @_;
79         my $pad = 0;
80         my $htxt = '';
81         my $help = $srch->help;
82         my $i;
83         for ($i = 0; $i < @$help; $i += 2) {
84                 my $pfx = $help->[$i];
85                 my $n = length($pfx);
86                 $pad = $n if $n > $pad;
87                 $htxt .= $pfx . "\0";
88                 $htxt .= $help->[$i + 1];
89                 $htxt .= "\f\n";
90         }
91         $pad += 2;
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;
97         $$txt .= $htxt;
98         1;
99 }
100
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";
107         $$txt .= <<EOF;
108
109 public-inbox provides a stable set of CSS classes for users to
110 customize colors for highlighting diffs and code.
111
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:
115
116         ${base_url}userContent.css
117
118 CSS sample
119 ----------
120 ```css
121 EOF
122         $$txt .= PublicInbox::UserContent::sample($ibx, $env) . "```\n";
123 }
124
125 # git-config section names are quoted in the config file, so escape them
126 sub dq_escape ($) {
127         my ($name) = @_;
128         $name =~ s/\\/\\\\/g;
129         $name =~ s/"/\\"/g;
130         $name;
131 }
132
133 sub URI_PATH () { '^A-Za-z0-9\-\._~/' }
134
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});
141         $$txt .= <<EOS;
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 EOS
151         for my $k (qw(address listid)) {
152                 defined(my $v = $ibx->{$k}) or next;
153                 $$txt .= "\t$k = $_\n" for @$v;
154         }
155
156         for my $k (qw(filter infourl newsgroup obfuscate replyto watchheader)) {
157                 defined(my $v = $ibx->{$k}) or next;
158                 $$txt .= "\t$k = $v\n";
159         }
160         $$txt .= "\tnntpmirror = $_\n" for (@{$ibx->nntp_url});
161
162         # note: this doesn't preserve cgitrc layout, since we parse cgitrc
163         # and drop the original structure
164         if (defined(my $cr = $ibx->{coderepo})) {
165                 $$txt .= "\tcoderepo = $_\n" for @$cr;
166
167                 my $pi_config = $ctx->{www}->{pi_config};
168                 for my $cr_name (@$cr) {
169                         my $url = $pi_config->{"coderepo.$cr_name.cgiturl"};
170                         my $path = "/path/to/$cr_name";
171                         $cr_name = dq_escape($cr_name);
172
173                         $$txt .= qq([coderepo "$cr_name"]\n);
174                         if (defined($url)) {
175                                 my $cpath = $path;
176                                 if ($path !~ m![a-z0-9_/\.\-]!i) {
177                                         $cpath = dq_escape($cpath);
178                                 }
179                                 $$txt .= qq(\t; git clone $url "$cpath"\n);
180                         }
181                         $$txt .= "\tdir = $path\n";
182                         $$txt .= "\tcgiturl = https://example.com/";
183                         $$txt .= uri_escape_utf8($cr_name, URI_PATH)."\n";
184                 }
185         }
186         1;
187 }
188
189 sub _default_text ($$$$) {
190         my ($ctx, $key, $hdr, $txt) = @_;
191         return _colors_help($ctx, $txt) if $key eq 'color';
192         return inbox_config($ctx, $hdr, $txt) if $key eq 'config';
193         return if $key ne 'help'; # TODO more keys?
194
195         my $ibx = $ctx->{-inbox};
196         my $base_url = $ibx->base_url($ctx->{env});
197         $$txt .= "public-inbox help for $base_url\n";
198         $$txt .= <<EOF;
199
200 overview
201 --------
202
203     public-inbox uses Message-ID identifiers in URLs.
204     One may look up messages by substituting Message-IDs
205     (without the leading '<' or trailing '>') into the URL.
206     Forward slash ('/') characters in the Message-IDs
207     need to be escaped as "%2F" (without quotes).
208
209     Thus, it is possible to retrieve any message by its
210     Message-ID by going to:
211
212         $base_url<Message-ID>/
213
214         (without the '<' or '>')
215
216     Message-IDs are described at:
217
218         $WIKI_URL/Message-ID
219
220 EOF
221
222         # n.b. we use the Xapian DB for any regeneratable,
223         # order-of-arrival-independent data.
224         my $srch = $ibx->search;
225         if ($srch) {
226                 $$txt .= <<EOF;
227 search
228 ------
229
230     This public-inbox has search functionality provided by Xapian.
231
232     It supports typical AND, OR, NOT, '+', '-' queries present
233     in other search engines.
234
235     We also support search prefixes to limit the scope of the
236     search to certain fields.
237
238     Prefixes supported in this installation include:
239
240 EOF
241                 _srch_prefix($srch, $txt);
242
243                 $$txt .= <<EOF;
244
245     Most prefixes are probabilistic, meaning they support stemming
246     and wildcards ('*').  Ranges (such as 'd:') and boolean prefixes
247     do not support stemming or wildcards.
248     The upstream Xapian query parser documentation fully explains
249     the query syntax:
250
251         $QP_URL
252
253 message threading
254 -----------------
255
256     Message threading is enabled for this public-inbox,
257     additional endpoints for message threads are available:
258
259     * $base_url<Message-ID>/T/#u
260
261       Loads the thread belonging to the given <Message-ID>
262       in flat chronological order.  The "#u" anchor
263       focuses the browser on the given <Message-ID>.
264
265     * $base_url<Message-ID>/t/#u
266
267       Loads the thread belonging to the given <Message-ID>
268       in threaded order with nesting.  For deep threads,
269       this requires a wide display or horizontal scrolling.
270
271     Both of these HTML endpoints are suitable for offline reading
272     using the thread overview at the bottom of each page.
273
274     Users of feed readers may follow a particular thread using:
275
276     * $base_url<Message-ID>/t.atom
277
278       Which loads the thread in Atom Syndication Standard
279       described at Wikipedia and RFC4287:
280
281         $WIKI_URL/Atom_(standard)
282         https://tools.ietf.org/html/rfc4287
283
284       Atom Threading Extensions (RFC4685) is supported:
285
286         https://tools.ietf.org/html/rfc4685
287
288     Finally, the gzipped mbox for a thread is available for
289     downloading and importing into your favorite mail client:
290
291     * $base_url<Message-ID>/t.mbox.gz
292
293     We use the mboxrd variant of the mbox format described
294     at:
295
296         $WIKI_URL/Mbox
297
298 contact
299 -------
300
301     This help text is maintained by public-inbox developers
302     reachable via plain-text email at: meta\@public-inbox.org
303
304 EOF
305         # TODO: support admin contact info in ~/.public-inbox/config
306         }
307         1;
308 }
309
310 1;