]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/WwwText.pm
wwwtext: support $INBOX_URL/_/text/config/raw
[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         _do_linkify($txt);
57         $ctx->{-title_html} = ascii_html($title);
58
59         my $nslash = ($key =~ tr!/!/!);
60         $ctx->{-upfx} = '../../../' . ('../' x $nslash);
61
62         PublicInbox::WwwStream->response($ctx, $code, sub {
63                 my ($nr, undef) = @_;
64                 $nr == 1 ? '<pre>'.$txt.'</pre>' : undef
65         });
66 }
67
68 sub _do_linkify {
69         my $l = PublicInbox::Linkify->new;
70         $l->linkify_1($_[0]);
71         if ($hl) {
72                 $hl->do_hl_text(\($_[0]));
73         } else {
74                 $_[0] = ascii_html($_[0]);
75         }
76         $_[0] = $l->linkify_2($_[0]);
77 }
78
79 sub _srch_prefix ($$) {
80         my ($srch, $txt) = @_;
81         my $pad = 0;
82         my $htxt = '';
83         my $help = $srch->help;
84         my $i;
85         for ($i = 0; $i < @$help; $i += 2) {
86                 my $pfx = $help->[$i];
87                 my $n = length($pfx);
88                 $pad = $n if $n > $pad;
89                 $htxt .= $pfx . "\0";
90                 $htxt .= $help->[$i + 1];
91                 $htxt .= "\f\n";
92         }
93         $pad += 2;
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;
99         $$txt .= $htxt;
100         1;
101 }
102
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";
109         $$txt .= <<EOF;
110
111 public-inbox provides a stable set of CSS classes for users to
112 customize colors for highlighting diffs and code.
113
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:
117
118         ${base_url}userContent.css
119
120 CSS sample
121 ----------
122 ```css
123 EOF
124         $$txt .= PublicInbox::UserContent::sample($ibx, $env) . "```\n";
125 }
126
127 # git-config section names are quoted in the config file, so escape them
128 sub dq_escape ($) {
129         my ($name) = @_;
130         $name =~ s/\\/\\\\/g;
131         $name =~ s/"/\\"/g;
132         $name;
133 }
134
135 sub URI_PATH () { '^A-Za-z0-9\-\._~/' }
136
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});
143         $$txt .= <<EOS;
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
149 EOS
150         for my $k (qw(address)) {
151                 defined(my $v = $ibx->{$k}) or next;
152                 $$txt .= "\t$k = $_\n" for @$v;
153         }
154
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";
158         }
159         $$txt .= "\tnntpmirror = $_\n" for (@{$ibx->nntp_url});
160
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;
165
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);
171
172                         $$txt .= qq([coderepo "$cr_name"]\n);
173                         if (defined($url)) {
174                                 my $cpath = $path;
175                                 if ($path !~ m![a-z0-9_/\.\-]!i) {
176                                         $cpath = dq_escape($cpath);
177                                 }
178                                 $$txt .= qq(\t; git clone $url "$cpath"\n);
179                         }
180                         $$txt .= "\tdir = $path\n";
181                         $$txt .= "\tcgiturl = https://example.com/";
182                         $$txt .= uri_escape_utf8($cr_name, URI_PATH)."\n";
183                 }
184         }
185         1;
186 }
187
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?
193
194         my $ibx = $ctx->{-inbox};
195         my $base_url = $ibx->base_url($ctx->{env});
196         $$txt .= "public-inbox help for $base_url\n";
197         $$txt .= <<EOF;
198
199 overview
200 --------
201
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).
207
208     Thus, it is possible to retrieve any message by its
209     Message-ID by going to:
210
211         $base_url<Message-ID>/
212
213         (without the '<' or '>')
214
215     Message-IDs are described at:
216
217         $WIKI_URL/Message-ID
218
219 EOF
220
221         # n.b. we use the Xapian DB for any regeneratable,
222         # order-of-arrival-independent data.
223         my $srch = $ibx->search;
224         if ($srch) {
225                 $$txt .= <<EOF;
226 search
227 ------
228
229     This public-inbox has search functionality provided by Xapian.
230
231     It supports typical AND, OR, NOT, '+', '-' queries present
232     in other search engines.
233
234     We also support search prefixes to limit the scope of the
235     search to certain fields.
236
237     Prefixes supported in this installation include:
238
239 EOF
240                 _srch_prefix($srch, $txt);
241
242                 $$txt .= <<EOF;
243
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
248     the query syntax:
249
250         $QP_URL
251
252 message threading
253 -----------------
254
255     Message threading is enabled for this public-inbox,
256     additional endpoints for message threads are available:
257
258     * $base_url<Message-ID>/T/#u
259
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>.
263
264     * $base_url<Message-ID>/t/#u
265
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.
269
270     Both of these HTML endpoints are suitable for offline reading
271     using the thread overview at the bottom of each page.
272
273     Users of feed readers may follow a particular thread using:
274
275     * $base_url<Message-ID>/t.atom
276
277       Which loads the thread in Atom Syndication Standard
278       described at Wikipedia and RFC4287:
279
280         $WIKI_URL/Atom_(standard)
281         https://tools.ietf.org/html/rfc4287
282
283       Atom Threading Extensions (RFC4685) is supported:
284
285         https://tools.ietf.org/html/rfc4685
286
287     Finally, the gzipped mbox for a thread is available for
288     downloading and importing into your favorite mail client:
289
290     * $base_url<Message-ID>/t.mbox.gz
291
292     We use the mboxrd variant of the mbox format described
293     at:
294
295         $WIKI_URL/Mbox
296
297 contact
298 -------
299
300     This help text is maintained by public-inbox developers
301     reachable via plain-text email at: meta\@public-inbox.org
302
303 EOF
304         # TODO: support admin contact info in ~/.public-inbox/config
305         }
306         1;
307 }
308
309 1;