xref: /freebsd/contrib/mandoc/cgi.c (revision 61d06d6bd19dafe8ea971dd43e8328fa1b473456)
1*61d06d6bSBaptiste Daroussin /*	$Id: cgi.c,v 1.158 2018/05/29 20:32:45 schwarze Exp $ */
2*61d06d6bSBaptiste Daroussin /*
3*61d06d6bSBaptiste Daroussin  * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4*61d06d6bSBaptiste Daroussin  * Copyright (c) 2014, 2015, 2016, 2017 Ingo Schwarze <schwarze@usta.de>
5*61d06d6bSBaptiste Daroussin  *
6*61d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
7*61d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
8*61d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
9*61d06d6bSBaptiste Daroussin  *
10*61d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11*61d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12*61d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13*61d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14*61d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15*61d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16*61d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*61d06d6bSBaptiste Daroussin  */
18*61d06d6bSBaptiste Daroussin #include "config.h"
19*61d06d6bSBaptiste Daroussin 
20*61d06d6bSBaptiste Daroussin #include <sys/types.h>
21*61d06d6bSBaptiste Daroussin #include <sys/time.h>
22*61d06d6bSBaptiste Daroussin 
23*61d06d6bSBaptiste Daroussin #include <ctype.h>
24*61d06d6bSBaptiste Daroussin #if HAVE_ERR
25*61d06d6bSBaptiste Daroussin #include <err.h>
26*61d06d6bSBaptiste Daroussin #endif
27*61d06d6bSBaptiste Daroussin #include <errno.h>
28*61d06d6bSBaptiste Daroussin #include <fcntl.h>
29*61d06d6bSBaptiste Daroussin #include <limits.h>
30*61d06d6bSBaptiste Daroussin #include <stdint.h>
31*61d06d6bSBaptiste Daroussin #include <stdio.h>
32*61d06d6bSBaptiste Daroussin #include <stdlib.h>
33*61d06d6bSBaptiste Daroussin #include <string.h>
34*61d06d6bSBaptiste Daroussin #include <unistd.h>
35*61d06d6bSBaptiste Daroussin 
36*61d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
37*61d06d6bSBaptiste Daroussin #include "mandoc.h"
38*61d06d6bSBaptiste Daroussin #include "roff.h"
39*61d06d6bSBaptiste Daroussin #include "mdoc.h"
40*61d06d6bSBaptiste Daroussin #include "man.h"
41*61d06d6bSBaptiste Daroussin #include "main.h"
42*61d06d6bSBaptiste Daroussin #include "manconf.h"
43*61d06d6bSBaptiste Daroussin #include "mansearch.h"
44*61d06d6bSBaptiste Daroussin #include "cgi.h"
45*61d06d6bSBaptiste Daroussin 
46*61d06d6bSBaptiste Daroussin /*
47*61d06d6bSBaptiste Daroussin  * A query as passed to the search function.
48*61d06d6bSBaptiste Daroussin  */
49*61d06d6bSBaptiste Daroussin struct	query {
50*61d06d6bSBaptiste Daroussin 	char		*manpath; /* desired manual directory */
51*61d06d6bSBaptiste Daroussin 	char		*arch; /* architecture */
52*61d06d6bSBaptiste Daroussin 	char		*sec; /* manual section */
53*61d06d6bSBaptiste Daroussin 	char		*query; /* unparsed query expression */
54*61d06d6bSBaptiste Daroussin 	int		 equal; /* match whole names, not substrings */
55*61d06d6bSBaptiste Daroussin };
56*61d06d6bSBaptiste Daroussin 
57*61d06d6bSBaptiste Daroussin struct	req {
58*61d06d6bSBaptiste Daroussin 	struct query	  q;
59*61d06d6bSBaptiste Daroussin 	char		**p; /* array of available manpaths */
60*61d06d6bSBaptiste Daroussin 	size_t		  psz; /* number of available manpaths */
61*61d06d6bSBaptiste Daroussin 	int		  isquery; /* QUERY_STRING used, not PATH_INFO */
62*61d06d6bSBaptiste Daroussin };
63*61d06d6bSBaptiste Daroussin 
64*61d06d6bSBaptiste Daroussin enum	focus {
65*61d06d6bSBaptiste Daroussin 	FOCUS_NONE = 0,
66*61d06d6bSBaptiste Daroussin 	FOCUS_QUERY
67*61d06d6bSBaptiste Daroussin };
68*61d06d6bSBaptiste Daroussin 
69*61d06d6bSBaptiste Daroussin static	void		 html_print(const char *);
70*61d06d6bSBaptiste Daroussin static	void		 html_putchar(char);
71*61d06d6bSBaptiste Daroussin static	int		 http_decode(char *);
72*61d06d6bSBaptiste Daroussin static	void		 parse_manpath_conf(struct req *);
73*61d06d6bSBaptiste Daroussin static	void		 parse_path_info(struct req *req, const char *path);
74*61d06d6bSBaptiste Daroussin static	void		 parse_query_string(struct req *, const char *);
75*61d06d6bSBaptiste Daroussin static	void		 pg_error_badrequest(const char *);
76*61d06d6bSBaptiste Daroussin static	void		 pg_error_internal(void);
77*61d06d6bSBaptiste Daroussin static	void		 pg_index(const struct req *);
78*61d06d6bSBaptiste Daroussin static	void		 pg_noresult(const struct req *, const char *);
79*61d06d6bSBaptiste Daroussin static	void		 pg_redirect(const struct req *, const char *);
80*61d06d6bSBaptiste Daroussin static	void		 pg_search(const struct req *);
81*61d06d6bSBaptiste Daroussin static	void		 pg_searchres(const struct req *,
82*61d06d6bSBaptiste Daroussin 				struct manpage *, size_t);
83*61d06d6bSBaptiste Daroussin static	void		 pg_show(struct req *, const char *);
84*61d06d6bSBaptiste Daroussin static	void		 resp_begin_html(int, const char *, const char *);
85*61d06d6bSBaptiste Daroussin static	void		 resp_begin_http(int, const char *);
86*61d06d6bSBaptiste Daroussin static	void		 resp_catman(const struct req *, const char *);
87*61d06d6bSBaptiste Daroussin static	void		 resp_copy(const char *);
88*61d06d6bSBaptiste Daroussin static	void		 resp_end_html(void);
89*61d06d6bSBaptiste Daroussin static	void		 resp_format(const struct req *, const char *);
90*61d06d6bSBaptiste Daroussin static	void		 resp_searchform(const struct req *, enum focus);
91*61d06d6bSBaptiste Daroussin static	void		 resp_show(const struct req *, const char *);
92*61d06d6bSBaptiste Daroussin static	void		 set_query_attr(char **, char **);
93*61d06d6bSBaptiste Daroussin static	int		 validate_filename(const char *);
94*61d06d6bSBaptiste Daroussin static	int		 validate_manpath(const struct req *, const char *);
95*61d06d6bSBaptiste Daroussin static	int		 validate_urifrag(const char *);
96*61d06d6bSBaptiste Daroussin 
97*61d06d6bSBaptiste Daroussin static	const char	 *scriptname = SCRIPT_NAME;
98*61d06d6bSBaptiste Daroussin 
99*61d06d6bSBaptiste Daroussin static	const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
100*61d06d6bSBaptiste Daroussin static	const char *const sec_numbers[] = {
101*61d06d6bSBaptiste Daroussin     "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
102*61d06d6bSBaptiste Daroussin };
103*61d06d6bSBaptiste Daroussin static	const char *const sec_names[] = {
104*61d06d6bSBaptiste Daroussin     "All Sections",
105*61d06d6bSBaptiste Daroussin     "1 - General Commands",
106*61d06d6bSBaptiste Daroussin     "2 - System Calls",
107*61d06d6bSBaptiste Daroussin     "3 - Library Functions",
108*61d06d6bSBaptiste Daroussin     "3p - Perl Library",
109*61d06d6bSBaptiste Daroussin     "4 - Device Drivers",
110*61d06d6bSBaptiste Daroussin     "5 - File Formats",
111*61d06d6bSBaptiste Daroussin     "6 - Games",
112*61d06d6bSBaptiste Daroussin     "7 - Miscellaneous Information",
113*61d06d6bSBaptiste Daroussin     "8 - System Manager\'s Manual",
114*61d06d6bSBaptiste Daroussin     "9 - Kernel Developer\'s Manual"
115*61d06d6bSBaptiste Daroussin };
116*61d06d6bSBaptiste Daroussin static	const int sec_MAX = sizeof(sec_names) / sizeof(char *);
117*61d06d6bSBaptiste Daroussin 
118*61d06d6bSBaptiste Daroussin static	const char *const arch_names[] = {
119*61d06d6bSBaptiste Daroussin     "amd64",       "alpha",       "armv7",	"arm64",
120*61d06d6bSBaptiste Daroussin     "hppa",        "i386",        "landisk",
121*61d06d6bSBaptiste Daroussin     "loongson",    "luna88k",     "macppc",      "mips64",
122*61d06d6bSBaptiste Daroussin     "octeon",      "sgi",         "socppc",      "sparc64",
123*61d06d6bSBaptiste Daroussin     "amiga",       "arc",         "armish",      "arm32",
124*61d06d6bSBaptiste Daroussin     "atari",       "aviion",      "beagle",      "cats",
125*61d06d6bSBaptiste Daroussin     "hppa64",      "hp300",
126*61d06d6bSBaptiste Daroussin     "ia64",        "mac68k",      "mvme68k",     "mvme88k",
127*61d06d6bSBaptiste Daroussin     "mvmeppc",     "palm",        "pc532",       "pegasos",
128*61d06d6bSBaptiste Daroussin     "pmax",        "powerpc",     "solbourne",   "sparc",
129*61d06d6bSBaptiste Daroussin     "sun3",        "vax",         "wgrisc",      "x68k",
130*61d06d6bSBaptiste Daroussin     "zaurus"
131*61d06d6bSBaptiste Daroussin };
132*61d06d6bSBaptiste Daroussin static	const int arch_MAX = sizeof(arch_names) / sizeof(char *);
133*61d06d6bSBaptiste Daroussin 
134*61d06d6bSBaptiste Daroussin /*
135*61d06d6bSBaptiste Daroussin  * Print a character, escaping HTML along the way.
136*61d06d6bSBaptiste Daroussin  * This will pass non-ASCII straight to output: be warned!
137*61d06d6bSBaptiste Daroussin  */
138*61d06d6bSBaptiste Daroussin static void
139*61d06d6bSBaptiste Daroussin html_putchar(char c)
140*61d06d6bSBaptiste Daroussin {
141*61d06d6bSBaptiste Daroussin 
142*61d06d6bSBaptiste Daroussin 	switch (c) {
143*61d06d6bSBaptiste Daroussin 	case '"':
144*61d06d6bSBaptiste Daroussin 		printf("&quot;");
145*61d06d6bSBaptiste Daroussin 		break;
146*61d06d6bSBaptiste Daroussin 	case '&':
147*61d06d6bSBaptiste Daroussin 		printf("&amp;");
148*61d06d6bSBaptiste Daroussin 		break;
149*61d06d6bSBaptiste Daroussin 	case '>':
150*61d06d6bSBaptiste Daroussin 		printf("&gt;");
151*61d06d6bSBaptiste Daroussin 		break;
152*61d06d6bSBaptiste Daroussin 	case '<':
153*61d06d6bSBaptiste Daroussin 		printf("&lt;");
154*61d06d6bSBaptiste Daroussin 		break;
155*61d06d6bSBaptiste Daroussin 	default:
156*61d06d6bSBaptiste Daroussin 		putchar((unsigned char)c);
157*61d06d6bSBaptiste Daroussin 		break;
158*61d06d6bSBaptiste Daroussin 	}
159*61d06d6bSBaptiste Daroussin }
160*61d06d6bSBaptiste Daroussin 
161*61d06d6bSBaptiste Daroussin /*
162*61d06d6bSBaptiste Daroussin  * Call through to html_putchar().
163*61d06d6bSBaptiste Daroussin  * Accepts NULL strings.
164*61d06d6bSBaptiste Daroussin  */
165*61d06d6bSBaptiste Daroussin static void
166*61d06d6bSBaptiste Daroussin html_print(const char *p)
167*61d06d6bSBaptiste Daroussin {
168*61d06d6bSBaptiste Daroussin 
169*61d06d6bSBaptiste Daroussin 	if (NULL == p)
170*61d06d6bSBaptiste Daroussin 		return;
171*61d06d6bSBaptiste Daroussin 	while ('\0' != *p)
172*61d06d6bSBaptiste Daroussin 		html_putchar(*p++);
173*61d06d6bSBaptiste Daroussin }
174*61d06d6bSBaptiste Daroussin 
175*61d06d6bSBaptiste Daroussin /*
176*61d06d6bSBaptiste Daroussin  * Transfer the responsibility for the allocated string *val
177*61d06d6bSBaptiste Daroussin  * to the query structure.
178*61d06d6bSBaptiste Daroussin  */
179*61d06d6bSBaptiste Daroussin static void
180*61d06d6bSBaptiste Daroussin set_query_attr(char **attr, char **val)
181*61d06d6bSBaptiste Daroussin {
182*61d06d6bSBaptiste Daroussin 
183*61d06d6bSBaptiste Daroussin 	free(*attr);
184*61d06d6bSBaptiste Daroussin 	if (**val == '\0') {
185*61d06d6bSBaptiste Daroussin 		*attr = NULL;
186*61d06d6bSBaptiste Daroussin 		free(*val);
187*61d06d6bSBaptiste Daroussin 	} else
188*61d06d6bSBaptiste Daroussin 		*attr = *val;
189*61d06d6bSBaptiste Daroussin 	*val = NULL;
190*61d06d6bSBaptiste Daroussin }
191*61d06d6bSBaptiste Daroussin 
192*61d06d6bSBaptiste Daroussin /*
193*61d06d6bSBaptiste Daroussin  * Parse the QUERY_STRING for key-value pairs
194*61d06d6bSBaptiste Daroussin  * and store the values into the query structure.
195*61d06d6bSBaptiste Daroussin  */
196*61d06d6bSBaptiste Daroussin static void
197*61d06d6bSBaptiste Daroussin parse_query_string(struct req *req, const char *qs)
198*61d06d6bSBaptiste Daroussin {
199*61d06d6bSBaptiste Daroussin 	char		*key, *val;
200*61d06d6bSBaptiste Daroussin 	size_t		 keysz, valsz;
201*61d06d6bSBaptiste Daroussin 
202*61d06d6bSBaptiste Daroussin 	req->isquery	= 1;
203*61d06d6bSBaptiste Daroussin 	req->q.manpath	= NULL;
204*61d06d6bSBaptiste Daroussin 	req->q.arch	= NULL;
205*61d06d6bSBaptiste Daroussin 	req->q.sec	= NULL;
206*61d06d6bSBaptiste Daroussin 	req->q.query	= NULL;
207*61d06d6bSBaptiste Daroussin 	req->q.equal	= 1;
208*61d06d6bSBaptiste Daroussin 
209*61d06d6bSBaptiste Daroussin 	key = val = NULL;
210*61d06d6bSBaptiste Daroussin 	while (*qs != '\0') {
211*61d06d6bSBaptiste Daroussin 
212*61d06d6bSBaptiste Daroussin 		/* Parse one key. */
213*61d06d6bSBaptiste Daroussin 
214*61d06d6bSBaptiste Daroussin 		keysz = strcspn(qs, "=;&");
215*61d06d6bSBaptiste Daroussin 		key = mandoc_strndup(qs, keysz);
216*61d06d6bSBaptiste Daroussin 		qs += keysz;
217*61d06d6bSBaptiste Daroussin 		if (*qs != '=')
218*61d06d6bSBaptiste Daroussin 			goto next;
219*61d06d6bSBaptiste Daroussin 
220*61d06d6bSBaptiste Daroussin 		/* Parse one value. */
221*61d06d6bSBaptiste Daroussin 
222*61d06d6bSBaptiste Daroussin 		valsz = strcspn(++qs, ";&");
223*61d06d6bSBaptiste Daroussin 		val = mandoc_strndup(qs, valsz);
224*61d06d6bSBaptiste Daroussin 		qs += valsz;
225*61d06d6bSBaptiste Daroussin 
226*61d06d6bSBaptiste Daroussin 		/* Decode and catch encoding errors. */
227*61d06d6bSBaptiste Daroussin 
228*61d06d6bSBaptiste Daroussin 		if ( ! (http_decode(key) && http_decode(val)))
229*61d06d6bSBaptiste Daroussin 			goto next;
230*61d06d6bSBaptiste Daroussin 
231*61d06d6bSBaptiste Daroussin 		/* Handle key-value pairs. */
232*61d06d6bSBaptiste Daroussin 
233*61d06d6bSBaptiste Daroussin 		if ( ! strcmp(key, "query"))
234*61d06d6bSBaptiste Daroussin 			set_query_attr(&req->q.query, &val);
235*61d06d6bSBaptiste Daroussin 
236*61d06d6bSBaptiste Daroussin 		else if ( ! strcmp(key, "apropos"))
237*61d06d6bSBaptiste Daroussin 			req->q.equal = !strcmp(val, "0");
238*61d06d6bSBaptiste Daroussin 
239*61d06d6bSBaptiste Daroussin 		else if ( ! strcmp(key, "manpath")) {
240*61d06d6bSBaptiste Daroussin #ifdef COMPAT_OLDURI
241*61d06d6bSBaptiste Daroussin 			if ( ! strncmp(val, "OpenBSD ", 8)) {
242*61d06d6bSBaptiste Daroussin 				val[7] = '-';
243*61d06d6bSBaptiste Daroussin 				if ('C' == val[8])
244*61d06d6bSBaptiste Daroussin 					val[8] = 'c';
245*61d06d6bSBaptiste Daroussin 			}
246*61d06d6bSBaptiste Daroussin #endif
247*61d06d6bSBaptiste Daroussin 			set_query_attr(&req->q.manpath, &val);
248*61d06d6bSBaptiste Daroussin 		}
249*61d06d6bSBaptiste Daroussin 
250*61d06d6bSBaptiste Daroussin 		else if ( ! (strcmp(key, "sec")
251*61d06d6bSBaptiste Daroussin #ifdef COMPAT_OLDURI
252*61d06d6bSBaptiste Daroussin 		    && strcmp(key, "sektion")
253*61d06d6bSBaptiste Daroussin #endif
254*61d06d6bSBaptiste Daroussin 		    )) {
255*61d06d6bSBaptiste Daroussin 			if ( ! strcmp(val, "0"))
256*61d06d6bSBaptiste Daroussin 				*val = '\0';
257*61d06d6bSBaptiste Daroussin 			set_query_attr(&req->q.sec, &val);
258*61d06d6bSBaptiste Daroussin 		}
259*61d06d6bSBaptiste Daroussin 
260*61d06d6bSBaptiste Daroussin 		else if ( ! strcmp(key, "arch")) {
261*61d06d6bSBaptiste Daroussin 			if ( ! strcmp(val, "default"))
262*61d06d6bSBaptiste Daroussin 				*val = '\0';
263*61d06d6bSBaptiste Daroussin 			set_query_attr(&req->q.arch, &val);
264*61d06d6bSBaptiste Daroussin 		}
265*61d06d6bSBaptiste Daroussin 
266*61d06d6bSBaptiste Daroussin 		/*
267*61d06d6bSBaptiste Daroussin 		 * The key must be freed in any case.
268*61d06d6bSBaptiste Daroussin 		 * The val may have been handed over to the query
269*61d06d6bSBaptiste Daroussin 		 * structure, in which case it is now NULL.
270*61d06d6bSBaptiste Daroussin 		 */
271*61d06d6bSBaptiste Daroussin next:
272*61d06d6bSBaptiste Daroussin 		free(key);
273*61d06d6bSBaptiste Daroussin 		key = NULL;
274*61d06d6bSBaptiste Daroussin 		free(val);
275*61d06d6bSBaptiste Daroussin 		val = NULL;
276*61d06d6bSBaptiste Daroussin 
277*61d06d6bSBaptiste Daroussin 		if (*qs != '\0')
278*61d06d6bSBaptiste Daroussin 			qs++;
279*61d06d6bSBaptiste Daroussin 	}
280*61d06d6bSBaptiste Daroussin }
281*61d06d6bSBaptiste Daroussin 
282*61d06d6bSBaptiste Daroussin /*
283*61d06d6bSBaptiste Daroussin  * HTTP-decode a string.  The standard explanation is that this turns
284*61d06d6bSBaptiste Daroussin  * "%4e+foo" into "n foo" in the regular way.  This is done in-place
285*61d06d6bSBaptiste Daroussin  * over the allocated string.
286*61d06d6bSBaptiste Daroussin  */
287*61d06d6bSBaptiste Daroussin static int
288*61d06d6bSBaptiste Daroussin http_decode(char *p)
289*61d06d6bSBaptiste Daroussin {
290*61d06d6bSBaptiste Daroussin 	char             hex[3];
291*61d06d6bSBaptiste Daroussin 	char		*q;
292*61d06d6bSBaptiste Daroussin 	int              c;
293*61d06d6bSBaptiste Daroussin 
294*61d06d6bSBaptiste Daroussin 	hex[2] = '\0';
295*61d06d6bSBaptiste Daroussin 
296*61d06d6bSBaptiste Daroussin 	q = p;
297*61d06d6bSBaptiste Daroussin 	for ( ; '\0' != *p; p++, q++) {
298*61d06d6bSBaptiste Daroussin 		if ('%' == *p) {
299*61d06d6bSBaptiste Daroussin 			if ('\0' == (hex[0] = *(p + 1)))
300*61d06d6bSBaptiste Daroussin 				return 0;
301*61d06d6bSBaptiste Daroussin 			if ('\0' == (hex[1] = *(p + 2)))
302*61d06d6bSBaptiste Daroussin 				return 0;
303*61d06d6bSBaptiste Daroussin 			if (1 != sscanf(hex, "%x", &c))
304*61d06d6bSBaptiste Daroussin 				return 0;
305*61d06d6bSBaptiste Daroussin 			if ('\0' == c)
306*61d06d6bSBaptiste Daroussin 				return 0;
307*61d06d6bSBaptiste Daroussin 
308*61d06d6bSBaptiste Daroussin 			*q = (char)c;
309*61d06d6bSBaptiste Daroussin 			p += 2;
310*61d06d6bSBaptiste Daroussin 		} else
311*61d06d6bSBaptiste Daroussin 			*q = '+' == *p ? ' ' : *p;
312*61d06d6bSBaptiste Daroussin 	}
313*61d06d6bSBaptiste Daroussin 
314*61d06d6bSBaptiste Daroussin 	*q = '\0';
315*61d06d6bSBaptiste Daroussin 	return 1;
316*61d06d6bSBaptiste Daroussin }
317*61d06d6bSBaptiste Daroussin 
318*61d06d6bSBaptiste Daroussin static void
319*61d06d6bSBaptiste Daroussin resp_begin_http(int code, const char *msg)
320*61d06d6bSBaptiste Daroussin {
321*61d06d6bSBaptiste Daroussin 
322*61d06d6bSBaptiste Daroussin 	if (200 != code)
323*61d06d6bSBaptiste Daroussin 		printf("Status: %d %s\r\n", code, msg);
324*61d06d6bSBaptiste Daroussin 
325*61d06d6bSBaptiste Daroussin 	printf("Content-Type: text/html; charset=utf-8\r\n"
326*61d06d6bSBaptiste Daroussin 	     "Cache-Control: no-cache\r\n"
327*61d06d6bSBaptiste Daroussin 	     "Pragma: no-cache\r\n"
328*61d06d6bSBaptiste Daroussin 	     "\r\n");
329*61d06d6bSBaptiste Daroussin 
330*61d06d6bSBaptiste Daroussin 	fflush(stdout);
331*61d06d6bSBaptiste Daroussin }
332*61d06d6bSBaptiste Daroussin 
333*61d06d6bSBaptiste Daroussin static void
334*61d06d6bSBaptiste Daroussin resp_copy(const char *filename)
335*61d06d6bSBaptiste Daroussin {
336*61d06d6bSBaptiste Daroussin 	char	 buf[4096];
337*61d06d6bSBaptiste Daroussin 	ssize_t	 sz;
338*61d06d6bSBaptiste Daroussin 	int	 fd;
339*61d06d6bSBaptiste Daroussin 
340*61d06d6bSBaptiste Daroussin 	if ((fd = open(filename, O_RDONLY)) != -1) {
341*61d06d6bSBaptiste Daroussin 		fflush(stdout);
342*61d06d6bSBaptiste Daroussin 		while ((sz = read(fd, buf, sizeof(buf))) > 0)
343*61d06d6bSBaptiste Daroussin 			write(STDOUT_FILENO, buf, sz);
344*61d06d6bSBaptiste Daroussin 		close(fd);
345*61d06d6bSBaptiste Daroussin 	}
346*61d06d6bSBaptiste Daroussin }
347*61d06d6bSBaptiste Daroussin 
348*61d06d6bSBaptiste Daroussin static void
349*61d06d6bSBaptiste Daroussin resp_begin_html(int code, const char *msg, const char *file)
350*61d06d6bSBaptiste Daroussin {
351*61d06d6bSBaptiste Daroussin 	char	*cp;
352*61d06d6bSBaptiste Daroussin 
353*61d06d6bSBaptiste Daroussin 	resp_begin_http(code, msg);
354*61d06d6bSBaptiste Daroussin 
355*61d06d6bSBaptiste Daroussin 	printf("<!DOCTYPE html>\n"
356*61d06d6bSBaptiste Daroussin 	       "<html>\n"
357*61d06d6bSBaptiste Daroussin 	       "<head>\n"
358*61d06d6bSBaptiste Daroussin 	       "  <meta charset=\"UTF-8\"/>\n"
359*61d06d6bSBaptiste Daroussin 	       "  <meta name=\"viewport\""
360*61d06d6bSBaptiste Daroussin 		      " content=\"width=device-width, initial-scale=1.0\">\n"
361*61d06d6bSBaptiste Daroussin 	       "  <link rel=\"stylesheet\" href=\"%s/mandoc.css\""
362*61d06d6bSBaptiste Daroussin 	       " type=\"text/css\" media=\"all\">\n"
363*61d06d6bSBaptiste Daroussin 	       "  <title>",
364*61d06d6bSBaptiste Daroussin 	       CSS_DIR);
365*61d06d6bSBaptiste Daroussin 	if (file != NULL) {
366*61d06d6bSBaptiste Daroussin 		if ((cp = strrchr(file, '/')) != NULL)
367*61d06d6bSBaptiste Daroussin 			file = cp + 1;
368*61d06d6bSBaptiste Daroussin 		if ((cp = strrchr(file, '.')) != NULL) {
369*61d06d6bSBaptiste Daroussin 			printf("%.*s(%s) - ", (int)(cp - file), file, cp + 1);
370*61d06d6bSBaptiste Daroussin 		} else
371*61d06d6bSBaptiste Daroussin 			printf("%s - ", file);
372*61d06d6bSBaptiste Daroussin 	}
373*61d06d6bSBaptiste Daroussin 	printf("%s</title>\n"
374*61d06d6bSBaptiste Daroussin 	       "</head>\n"
375*61d06d6bSBaptiste Daroussin 	       "<body>\n",
376*61d06d6bSBaptiste Daroussin 	       CUSTOMIZE_TITLE);
377*61d06d6bSBaptiste Daroussin 
378*61d06d6bSBaptiste Daroussin 	resp_copy(MAN_DIR "/header.html");
379*61d06d6bSBaptiste Daroussin }
380*61d06d6bSBaptiste Daroussin 
381*61d06d6bSBaptiste Daroussin static void
382*61d06d6bSBaptiste Daroussin resp_end_html(void)
383*61d06d6bSBaptiste Daroussin {
384*61d06d6bSBaptiste Daroussin 
385*61d06d6bSBaptiste Daroussin 	resp_copy(MAN_DIR "/footer.html");
386*61d06d6bSBaptiste Daroussin 
387*61d06d6bSBaptiste Daroussin 	puts("</body>\n"
388*61d06d6bSBaptiste Daroussin 	     "</html>");
389*61d06d6bSBaptiste Daroussin }
390*61d06d6bSBaptiste Daroussin 
391*61d06d6bSBaptiste Daroussin static void
392*61d06d6bSBaptiste Daroussin resp_searchform(const struct req *req, enum focus focus)
393*61d06d6bSBaptiste Daroussin {
394*61d06d6bSBaptiste Daroussin 	int		 i;
395*61d06d6bSBaptiste Daroussin 
396*61d06d6bSBaptiste Daroussin 	printf("<form action=\"/%s\" method=\"get\">\n"
397*61d06d6bSBaptiste Daroussin 	       "  <fieldset>\n"
398*61d06d6bSBaptiste Daroussin 	       "    <legend>Manual Page Search Parameters</legend>\n",
399*61d06d6bSBaptiste Daroussin 	       scriptname);
400*61d06d6bSBaptiste Daroussin 
401*61d06d6bSBaptiste Daroussin 	/* Write query input box. */
402*61d06d6bSBaptiste Daroussin 
403*61d06d6bSBaptiste Daroussin 	printf("    <input type=\"search\" name=\"query\" value=\"");
404*61d06d6bSBaptiste Daroussin 	if (req->q.query != NULL)
405*61d06d6bSBaptiste Daroussin 		html_print(req->q.query);
406*61d06d6bSBaptiste Daroussin 	printf( "\" size=\"40\"");
407*61d06d6bSBaptiste Daroussin 	if (focus == FOCUS_QUERY)
408*61d06d6bSBaptiste Daroussin 		printf(" autofocus");
409*61d06d6bSBaptiste Daroussin 	puts(">");
410*61d06d6bSBaptiste Daroussin 
411*61d06d6bSBaptiste Daroussin 	/* Write submission buttons. */
412*61d06d6bSBaptiste Daroussin 
413*61d06d6bSBaptiste Daroussin 	printf(	"    <button type=\"submit\" name=\"apropos\" value=\"0\">"
414*61d06d6bSBaptiste Daroussin 		"man</button>\n"
415*61d06d6bSBaptiste Daroussin 		"    <button type=\"submit\" name=\"apropos\" value=\"1\">"
416*61d06d6bSBaptiste Daroussin 		"apropos</button>\n"
417*61d06d6bSBaptiste Daroussin 		"    <br/>\n");
418*61d06d6bSBaptiste Daroussin 
419*61d06d6bSBaptiste Daroussin 	/* Write section selector. */
420*61d06d6bSBaptiste Daroussin 
421*61d06d6bSBaptiste Daroussin 	puts("    <select name=\"sec\">");
422*61d06d6bSBaptiste Daroussin 	for (i = 0; i < sec_MAX; i++) {
423*61d06d6bSBaptiste Daroussin 		printf("      <option value=\"%s\"", sec_numbers[i]);
424*61d06d6bSBaptiste Daroussin 		if (NULL != req->q.sec &&
425*61d06d6bSBaptiste Daroussin 		    0 == strcmp(sec_numbers[i], req->q.sec))
426*61d06d6bSBaptiste Daroussin 			printf(" selected=\"selected\"");
427*61d06d6bSBaptiste Daroussin 		printf(">%s</option>\n", sec_names[i]);
428*61d06d6bSBaptiste Daroussin 	}
429*61d06d6bSBaptiste Daroussin 	puts("    </select>");
430*61d06d6bSBaptiste Daroussin 
431*61d06d6bSBaptiste Daroussin 	/* Write architecture selector. */
432*61d06d6bSBaptiste Daroussin 
433*61d06d6bSBaptiste Daroussin 	printf(	"    <select name=\"arch\">\n"
434*61d06d6bSBaptiste Daroussin 		"      <option value=\"default\"");
435*61d06d6bSBaptiste Daroussin 	if (NULL == req->q.arch)
436*61d06d6bSBaptiste Daroussin 		printf(" selected=\"selected\"");
437*61d06d6bSBaptiste Daroussin 	puts(">All Architectures</option>");
438*61d06d6bSBaptiste Daroussin 	for (i = 0; i < arch_MAX; i++) {
439*61d06d6bSBaptiste Daroussin 		printf("      <option");
440*61d06d6bSBaptiste Daroussin 		if (NULL != req->q.arch &&
441*61d06d6bSBaptiste Daroussin 		    0 == strcmp(arch_names[i], req->q.arch))
442*61d06d6bSBaptiste Daroussin 			printf(" selected=\"selected\"");
443*61d06d6bSBaptiste Daroussin 		printf(">%s</option>\n", arch_names[i]);
444*61d06d6bSBaptiste Daroussin 	}
445*61d06d6bSBaptiste Daroussin 	puts("    </select>");
446*61d06d6bSBaptiste Daroussin 
447*61d06d6bSBaptiste Daroussin 	/* Write manpath selector. */
448*61d06d6bSBaptiste Daroussin 
449*61d06d6bSBaptiste Daroussin 	if (req->psz > 1) {
450*61d06d6bSBaptiste Daroussin 		puts("    <select name=\"manpath\">");
451*61d06d6bSBaptiste Daroussin 		for (i = 0; i < (int)req->psz; i++) {
452*61d06d6bSBaptiste Daroussin 			printf("      <option");
453*61d06d6bSBaptiste Daroussin 			if (strcmp(req->q.manpath, req->p[i]) == 0)
454*61d06d6bSBaptiste Daroussin 				printf(" selected=\"selected\"");
455*61d06d6bSBaptiste Daroussin 			printf(">");
456*61d06d6bSBaptiste Daroussin 			html_print(req->p[i]);
457*61d06d6bSBaptiste Daroussin 			puts("</option>");
458*61d06d6bSBaptiste Daroussin 		}
459*61d06d6bSBaptiste Daroussin 		puts("    </select>");
460*61d06d6bSBaptiste Daroussin 	}
461*61d06d6bSBaptiste Daroussin 
462*61d06d6bSBaptiste Daroussin 	puts("  </fieldset>\n"
463*61d06d6bSBaptiste Daroussin 	     "</form>");
464*61d06d6bSBaptiste Daroussin }
465*61d06d6bSBaptiste Daroussin 
466*61d06d6bSBaptiste Daroussin static int
467*61d06d6bSBaptiste Daroussin validate_urifrag(const char *frag)
468*61d06d6bSBaptiste Daroussin {
469*61d06d6bSBaptiste Daroussin 
470*61d06d6bSBaptiste Daroussin 	while ('\0' != *frag) {
471*61d06d6bSBaptiste Daroussin 		if ( ! (isalnum((unsigned char)*frag) ||
472*61d06d6bSBaptiste Daroussin 		    '-' == *frag || '.' == *frag ||
473*61d06d6bSBaptiste Daroussin 		    '/' == *frag || '_' == *frag))
474*61d06d6bSBaptiste Daroussin 			return 0;
475*61d06d6bSBaptiste Daroussin 		frag++;
476*61d06d6bSBaptiste Daroussin 	}
477*61d06d6bSBaptiste Daroussin 	return 1;
478*61d06d6bSBaptiste Daroussin }
479*61d06d6bSBaptiste Daroussin 
480*61d06d6bSBaptiste Daroussin static int
481*61d06d6bSBaptiste Daroussin validate_manpath(const struct req *req, const char* manpath)
482*61d06d6bSBaptiste Daroussin {
483*61d06d6bSBaptiste Daroussin 	size_t	 i;
484*61d06d6bSBaptiste Daroussin 
485*61d06d6bSBaptiste Daroussin 	for (i = 0; i < req->psz; i++)
486*61d06d6bSBaptiste Daroussin 		if ( ! strcmp(manpath, req->p[i]))
487*61d06d6bSBaptiste Daroussin 			return 1;
488*61d06d6bSBaptiste Daroussin 
489*61d06d6bSBaptiste Daroussin 	return 0;
490*61d06d6bSBaptiste Daroussin }
491*61d06d6bSBaptiste Daroussin 
492*61d06d6bSBaptiste Daroussin static int
493*61d06d6bSBaptiste Daroussin validate_filename(const char *file)
494*61d06d6bSBaptiste Daroussin {
495*61d06d6bSBaptiste Daroussin 
496*61d06d6bSBaptiste Daroussin 	if ('.' == file[0] && '/' == file[1])
497*61d06d6bSBaptiste Daroussin 		file += 2;
498*61d06d6bSBaptiste Daroussin 
499*61d06d6bSBaptiste Daroussin 	return ! (strstr(file, "../") || strstr(file, "/..") ||
500*61d06d6bSBaptiste Daroussin 	    (strncmp(file, "man", 3) && strncmp(file, "cat", 3)));
501*61d06d6bSBaptiste Daroussin }
502*61d06d6bSBaptiste Daroussin 
503*61d06d6bSBaptiste Daroussin static void
504*61d06d6bSBaptiste Daroussin pg_index(const struct req *req)
505*61d06d6bSBaptiste Daroussin {
506*61d06d6bSBaptiste Daroussin 
507*61d06d6bSBaptiste Daroussin 	resp_begin_html(200, NULL, NULL);
508*61d06d6bSBaptiste Daroussin 	resp_searchform(req, FOCUS_QUERY);
509*61d06d6bSBaptiste Daroussin 	printf("<p>\n"
510*61d06d6bSBaptiste Daroussin 	       "This web interface is documented in the\n"
511*61d06d6bSBaptiste Daroussin 	       "<a class=\"Xr\" href=\"/%s%sman.cgi.8\">man.cgi(8)</a>\n"
512*61d06d6bSBaptiste Daroussin 	       "manual, and the\n"
513*61d06d6bSBaptiste Daroussin 	       "<a class=\"Xr\" href=\"/%s%sapropos.1\">apropos(1)</a>\n"
514*61d06d6bSBaptiste Daroussin 	       "manual explains the query syntax.\n"
515*61d06d6bSBaptiste Daroussin 	       "</p>\n",
516*61d06d6bSBaptiste Daroussin 	       scriptname, *scriptname == '\0' ? "" : "/",
517*61d06d6bSBaptiste Daroussin 	       scriptname, *scriptname == '\0' ? "" : "/");
518*61d06d6bSBaptiste Daroussin 	resp_end_html();
519*61d06d6bSBaptiste Daroussin }
520*61d06d6bSBaptiste Daroussin 
521*61d06d6bSBaptiste Daroussin static void
522*61d06d6bSBaptiste Daroussin pg_noresult(const struct req *req, const char *msg)
523*61d06d6bSBaptiste Daroussin {
524*61d06d6bSBaptiste Daroussin 	resp_begin_html(200, NULL, NULL);
525*61d06d6bSBaptiste Daroussin 	resp_searchform(req, FOCUS_QUERY);
526*61d06d6bSBaptiste Daroussin 	puts("<p>");
527*61d06d6bSBaptiste Daroussin 	puts(msg);
528*61d06d6bSBaptiste Daroussin 	puts("</p>");
529*61d06d6bSBaptiste Daroussin 	resp_end_html();
530*61d06d6bSBaptiste Daroussin }
531*61d06d6bSBaptiste Daroussin 
532*61d06d6bSBaptiste Daroussin static void
533*61d06d6bSBaptiste Daroussin pg_error_badrequest(const char *msg)
534*61d06d6bSBaptiste Daroussin {
535*61d06d6bSBaptiste Daroussin 
536*61d06d6bSBaptiste Daroussin 	resp_begin_html(400, "Bad Request", NULL);
537*61d06d6bSBaptiste Daroussin 	puts("<h1>Bad Request</h1>\n"
538*61d06d6bSBaptiste Daroussin 	     "<p>\n");
539*61d06d6bSBaptiste Daroussin 	puts(msg);
540*61d06d6bSBaptiste Daroussin 	printf("Try again from the\n"
541*61d06d6bSBaptiste Daroussin 	       "<a href=\"/%s\">main page</a>.\n"
542*61d06d6bSBaptiste Daroussin 	       "</p>", scriptname);
543*61d06d6bSBaptiste Daroussin 	resp_end_html();
544*61d06d6bSBaptiste Daroussin }
545*61d06d6bSBaptiste Daroussin 
546*61d06d6bSBaptiste Daroussin static void
547*61d06d6bSBaptiste Daroussin pg_error_internal(void)
548*61d06d6bSBaptiste Daroussin {
549*61d06d6bSBaptiste Daroussin 	resp_begin_html(500, "Internal Server Error", NULL);
550*61d06d6bSBaptiste Daroussin 	puts("<p>Internal Server Error</p>");
551*61d06d6bSBaptiste Daroussin 	resp_end_html();
552*61d06d6bSBaptiste Daroussin }
553*61d06d6bSBaptiste Daroussin 
554*61d06d6bSBaptiste Daroussin static void
555*61d06d6bSBaptiste Daroussin pg_redirect(const struct req *req, const char *name)
556*61d06d6bSBaptiste Daroussin {
557*61d06d6bSBaptiste Daroussin 	printf("Status: 303 See Other\r\n"
558*61d06d6bSBaptiste Daroussin 	    "Location: /");
559*61d06d6bSBaptiste Daroussin 	if (*scriptname != '\0')
560*61d06d6bSBaptiste Daroussin 		printf("%s/", scriptname);
561*61d06d6bSBaptiste Daroussin 	if (strcmp(req->q.manpath, req->p[0]))
562*61d06d6bSBaptiste Daroussin 		printf("%s/", req->q.manpath);
563*61d06d6bSBaptiste Daroussin 	if (req->q.arch != NULL)
564*61d06d6bSBaptiste Daroussin 		printf("%s/", req->q.arch);
565*61d06d6bSBaptiste Daroussin 	printf("%s", name);
566*61d06d6bSBaptiste Daroussin 	if (req->q.sec != NULL)
567*61d06d6bSBaptiste Daroussin 		printf(".%s", req->q.sec);
568*61d06d6bSBaptiste Daroussin 	printf("\r\nContent-Type: text/html; charset=utf-8\r\n\r\n");
569*61d06d6bSBaptiste Daroussin }
570*61d06d6bSBaptiste Daroussin 
571*61d06d6bSBaptiste Daroussin static void
572*61d06d6bSBaptiste Daroussin pg_searchres(const struct req *req, struct manpage *r, size_t sz)
573*61d06d6bSBaptiste Daroussin {
574*61d06d6bSBaptiste Daroussin 	char		*arch, *archend;
575*61d06d6bSBaptiste Daroussin 	const char	*sec;
576*61d06d6bSBaptiste Daroussin 	size_t		 i, iuse;
577*61d06d6bSBaptiste Daroussin 	int		 archprio, archpriouse;
578*61d06d6bSBaptiste Daroussin 	int		 prio, priouse;
579*61d06d6bSBaptiste Daroussin 
580*61d06d6bSBaptiste Daroussin 	for (i = 0; i < sz; i++) {
581*61d06d6bSBaptiste Daroussin 		if (validate_filename(r[i].file))
582*61d06d6bSBaptiste Daroussin 			continue;
583*61d06d6bSBaptiste Daroussin 		warnx("invalid filename %s in %s database",
584*61d06d6bSBaptiste Daroussin 		    r[i].file, req->q.manpath);
585*61d06d6bSBaptiste Daroussin 		pg_error_internal();
586*61d06d6bSBaptiste Daroussin 		return;
587*61d06d6bSBaptiste Daroussin 	}
588*61d06d6bSBaptiste Daroussin 
589*61d06d6bSBaptiste Daroussin 	if (req->isquery && sz == 1) {
590*61d06d6bSBaptiste Daroussin 		/*
591*61d06d6bSBaptiste Daroussin 		 * If we have just one result, then jump there now
592*61d06d6bSBaptiste Daroussin 		 * without any delay.
593*61d06d6bSBaptiste Daroussin 		 */
594*61d06d6bSBaptiste Daroussin 		printf("Status: 303 See Other\r\n"
595*61d06d6bSBaptiste Daroussin 		    "Location: /");
596*61d06d6bSBaptiste Daroussin 		if (*scriptname != '\0')
597*61d06d6bSBaptiste Daroussin 			printf("%s/", scriptname);
598*61d06d6bSBaptiste Daroussin 		if (strcmp(req->q.manpath, req->p[0]))
599*61d06d6bSBaptiste Daroussin 			printf("%s/", req->q.manpath);
600*61d06d6bSBaptiste Daroussin 		printf("%s\r\n"
601*61d06d6bSBaptiste Daroussin 		    "Content-Type: text/html; charset=utf-8\r\n\r\n",
602*61d06d6bSBaptiste Daroussin 		    r[0].file);
603*61d06d6bSBaptiste Daroussin 		return;
604*61d06d6bSBaptiste Daroussin 	}
605*61d06d6bSBaptiste Daroussin 
606*61d06d6bSBaptiste Daroussin 	/*
607*61d06d6bSBaptiste Daroussin 	 * In man(1) mode, show one of the pages
608*61d06d6bSBaptiste Daroussin 	 * even if more than one is found.
609*61d06d6bSBaptiste Daroussin 	 */
610*61d06d6bSBaptiste Daroussin 
611*61d06d6bSBaptiste Daroussin 	iuse = 0;
612*61d06d6bSBaptiste Daroussin 	if (req->q.equal || sz == 1) {
613*61d06d6bSBaptiste Daroussin 		priouse = 20;
614*61d06d6bSBaptiste Daroussin 		archpriouse = 3;
615*61d06d6bSBaptiste Daroussin 		for (i = 0; i < sz; i++) {
616*61d06d6bSBaptiste Daroussin 			sec = r[i].file;
617*61d06d6bSBaptiste Daroussin 			sec += strcspn(sec, "123456789");
618*61d06d6bSBaptiste Daroussin 			if (sec[0] == '\0')
619*61d06d6bSBaptiste Daroussin 				continue;
620*61d06d6bSBaptiste Daroussin 			prio = sec_prios[sec[0] - '1'];
621*61d06d6bSBaptiste Daroussin 			if (sec[1] != '/')
622*61d06d6bSBaptiste Daroussin 				prio += 10;
623*61d06d6bSBaptiste Daroussin 			if (req->q.arch == NULL) {
624*61d06d6bSBaptiste Daroussin 				archprio =
625*61d06d6bSBaptiste Daroussin 				    ((arch = strchr(sec + 1, '/'))
626*61d06d6bSBaptiste Daroussin 					== NULL) ? 3 :
627*61d06d6bSBaptiste Daroussin 				    ((archend = strchr(arch + 1, '/'))
628*61d06d6bSBaptiste Daroussin 					== NULL) ? 0 :
629*61d06d6bSBaptiste Daroussin 				    strncmp(arch, "amd64/",
630*61d06d6bSBaptiste Daroussin 					archend - arch) ? 2 : 1;
631*61d06d6bSBaptiste Daroussin 				if (archprio < archpriouse) {
632*61d06d6bSBaptiste Daroussin 					archpriouse = archprio;
633*61d06d6bSBaptiste Daroussin 					priouse = prio;
634*61d06d6bSBaptiste Daroussin 					iuse = i;
635*61d06d6bSBaptiste Daroussin 					continue;
636*61d06d6bSBaptiste Daroussin 				}
637*61d06d6bSBaptiste Daroussin 				if (archprio > archpriouse)
638*61d06d6bSBaptiste Daroussin 					continue;
639*61d06d6bSBaptiste Daroussin 			}
640*61d06d6bSBaptiste Daroussin 			if (prio >= priouse)
641*61d06d6bSBaptiste Daroussin 				continue;
642*61d06d6bSBaptiste Daroussin 			priouse = prio;
643*61d06d6bSBaptiste Daroussin 			iuse = i;
644*61d06d6bSBaptiste Daroussin 		}
645*61d06d6bSBaptiste Daroussin 		resp_begin_html(200, NULL, r[iuse].file);
646*61d06d6bSBaptiste Daroussin 	} else
647*61d06d6bSBaptiste Daroussin 		resp_begin_html(200, NULL, NULL);
648*61d06d6bSBaptiste Daroussin 
649*61d06d6bSBaptiste Daroussin 	resp_searchform(req,
650*61d06d6bSBaptiste Daroussin 	    req->q.equal || sz == 1 ? FOCUS_NONE : FOCUS_QUERY);
651*61d06d6bSBaptiste Daroussin 
652*61d06d6bSBaptiste Daroussin 	if (sz > 1) {
653*61d06d6bSBaptiste Daroussin 		puts("<table class=\"results\">");
654*61d06d6bSBaptiste Daroussin 		for (i = 0; i < sz; i++) {
655*61d06d6bSBaptiste Daroussin 			printf("  <tr>\n"
656*61d06d6bSBaptiste Daroussin 			       "    <td>"
657*61d06d6bSBaptiste Daroussin 			       "<a class=\"Xr\" href=\"/");
658*61d06d6bSBaptiste Daroussin 			if (*scriptname != '\0')
659*61d06d6bSBaptiste Daroussin 				printf("%s/", scriptname);
660*61d06d6bSBaptiste Daroussin 			if (strcmp(req->q.manpath, req->p[0]))
661*61d06d6bSBaptiste Daroussin 				printf("%s/", req->q.manpath);
662*61d06d6bSBaptiste Daroussin 			printf("%s\">", r[i].file);
663*61d06d6bSBaptiste Daroussin 			html_print(r[i].names);
664*61d06d6bSBaptiste Daroussin 			printf("</a></td>\n"
665*61d06d6bSBaptiste Daroussin 			       "    <td><span class=\"Nd\">");
666*61d06d6bSBaptiste Daroussin 			html_print(r[i].output);
667*61d06d6bSBaptiste Daroussin 			puts("</span></td>\n"
668*61d06d6bSBaptiste Daroussin 			     "  </tr>");
669*61d06d6bSBaptiste Daroussin 		}
670*61d06d6bSBaptiste Daroussin 		puts("</table>");
671*61d06d6bSBaptiste Daroussin 	}
672*61d06d6bSBaptiste Daroussin 
673*61d06d6bSBaptiste Daroussin 	if (req->q.equal || sz == 1) {
674*61d06d6bSBaptiste Daroussin 		puts("<hr>");
675*61d06d6bSBaptiste Daroussin 		resp_show(req, r[iuse].file);
676*61d06d6bSBaptiste Daroussin 	}
677*61d06d6bSBaptiste Daroussin 
678*61d06d6bSBaptiste Daroussin 	resp_end_html();
679*61d06d6bSBaptiste Daroussin }
680*61d06d6bSBaptiste Daroussin 
681*61d06d6bSBaptiste Daroussin static void
682*61d06d6bSBaptiste Daroussin resp_catman(const struct req *req, const char *file)
683*61d06d6bSBaptiste Daroussin {
684*61d06d6bSBaptiste Daroussin 	FILE		*f;
685*61d06d6bSBaptiste Daroussin 	char		*p;
686*61d06d6bSBaptiste Daroussin 	size_t		 sz;
687*61d06d6bSBaptiste Daroussin 	ssize_t		 len;
688*61d06d6bSBaptiste Daroussin 	int		 i;
689*61d06d6bSBaptiste Daroussin 	int		 italic, bold;
690*61d06d6bSBaptiste Daroussin 
691*61d06d6bSBaptiste Daroussin 	if ((f = fopen(file, "r")) == NULL) {
692*61d06d6bSBaptiste Daroussin 		puts("<p>You specified an invalid manual file.</p>");
693*61d06d6bSBaptiste Daroussin 		return;
694*61d06d6bSBaptiste Daroussin 	}
695*61d06d6bSBaptiste Daroussin 
696*61d06d6bSBaptiste Daroussin 	puts("<div class=\"catman\">\n"
697*61d06d6bSBaptiste Daroussin 	     "<pre>");
698*61d06d6bSBaptiste Daroussin 
699*61d06d6bSBaptiste Daroussin 	p = NULL;
700*61d06d6bSBaptiste Daroussin 	sz = 0;
701*61d06d6bSBaptiste Daroussin 
702*61d06d6bSBaptiste Daroussin 	while ((len = getline(&p, &sz, f)) != -1) {
703*61d06d6bSBaptiste Daroussin 		bold = italic = 0;
704*61d06d6bSBaptiste Daroussin 		for (i = 0; i < len - 1; i++) {
705*61d06d6bSBaptiste Daroussin 			/*
706*61d06d6bSBaptiste Daroussin 			 * This means that the catpage is out of state.
707*61d06d6bSBaptiste Daroussin 			 * Ignore it and keep going (although the
708*61d06d6bSBaptiste Daroussin 			 * catpage is bogus).
709*61d06d6bSBaptiste Daroussin 			 */
710*61d06d6bSBaptiste Daroussin 
711*61d06d6bSBaptiste Daroussin 			if ('\b' == p[i] || '\n' == p[i])
712*61d06d6bSBaptiste Daroussin 				continue;
713*61d06d6bSBaptiste Daroussin 
714*61d06d6bSBaptiste Daroussin 			/*
715*61d06d6bSBaptiste Daroussin 			 * Print a regular character.
716*61d06d6bSBaptiste Daroussin 			 * Close out any bold/italic scopes.
717*61d06d6bSBaptiste Daroussin 			 * If we're in back-space mode, make sure we'll
718*61d06d6bSBaptiste Daroussin 			 * have something to enter when we backspace.
719*61d06d6bSBaptiste Daroussin 			 */
720*61d06d6bSBaptiste Daroussin 
721*61d06d6bSBaptiste Daroussin 			if ('\b' != p[i + 1]) {
722*61d06d6bSBaptiste Daroussin 				if (italic)
723*61d06d6bSBaptiste Daroussin 					printf("</i>");
724*61d06d6bSBaptiste Daroussin 				if (bold)
725*61d06d6bSBaptiste Daroussin 					printf("</b>");
726*61d06d6bSBaptiste Daroussin 				italic = bold = 0;
727*61d06d6bSBaptiste Daroussin 				html_putchar(p[i]);
728*61d06d6bSBaptiste Daroussin 				continue;
729*61d06d6bSBaptiste Daroussin 			} else if (i + 2 >= len)
730*61d06d6bSBaptiste Daroussin 				continue;
731*61d06d6bSBaptiste Daroussin 
732*61d06d6bSBaptiste Daroussin 			/* Italic mode. */
733*61d06d6bSBaptiste Daroussin 
734*61d06d6bSBaptiste Daroussin 			if ('_' == p[i]) {
735*61d06d6bSBaptiste Daroussin 				if (bold)
736*61d06d6bSBaptiste Daroussin 					printf("</b>");
737*61d06d6bSBaptiste Daroussin 				if ( ! italic)
738*61d06d6bSBaptiste Daroussin 					printf("<i>");
739*61d06d6bSBaptiste Daroussin 				bold = 0;
740*61d06d6bSBaptiste Daroussin 				italic = 1;
741*61d06d6bSBaptiste Daroussin 				i += 2;
742*61d06d6bSBaptiste Daroussin 				html_putchar(p[i]);
743*61d06d6bSBaptiste Daroussin 				continue;
744*61d06d6bSBaptiste Daroussin 			}
745*61d06d6bSBaptiste Daroussin 
746*61d06d6bSBaptiste Daroussin 			/*
747*61d06d6bSBaptiste Daroussin 			 * Handle funny behaviour troff-isms.
748*61d06d6bSBaptiste Daroussin 			 * These grok'd from the original man2html.c.
749*61d06d6bSBaptiste Daroussin 			 */
750*61d06d6bSBaptiste Daroussin 
751*61d06d6bSBaptiste Daroussin 			if (('+' == p[i] && 'o' == p[i + 2]) ||
752*61d06d6bSBaptiste Daroussin 					('o' == p[i] && '+' == p[i + 2]) ||
753*61d06d6bSBaptiste Daroussin 					('|' == p[i] && '=' == p[i + 2]) ||
754*61d06d6bSBaptiste Daroussin 					('=' == p[i] && '|' == p[i + 2]) ||
755*61d06d6bSBaptiste Daroussin 					('*' == p[i] && '=' == p[i + 2]) ||
756*61d06d6bSBaptiste Daroussin 					('=' == p[i] && '*' == p[i + 2]) ||
757*61d06d6bSBaptiste Daroussin 					('*' == p[i] && '|' == p[i + 2]) ||
758*61d06d6bSBaptiste Daroussin 					('|' == p[i] && '*' == p[i + 2]))  {
759*61d06d6bSBaptiste Daroussin 				if (italic)
760*61d06d6bSBaptiste Daroussin 					printf("</i>");
761*61d06d6bSBaptiste Daroussin 				if (bold)
762*61d06d6bSBaptiste Daroussin 					printf("</b>");
763*61d06d6bSBaptiste Daroussin 				italic = bold = 0;
764*61d06d6bSBaptiste Daroussin 				putchar('*');
765*61d06d6bSBaptiste Daroussin 				i += 2;
766*61d06d6bSBaptiste Daroussin 				continue;
767*61d06d6bSBaptiste Daroussin 			} else if (('|' == p[i] && '-' == p[i + 2]) ||
768*61d06d6bSBaptiste Daroussin 					('-' == p[i] && '|' == p[i + 1]) ||
769*61d06d6bSBaptiste Daroussin 					('+' == p[i] && '-' == p[i + 1]) ||
770*61d06d6bSBaptiste Daroussin 					('-' == p[i] && '+' == p[i + 1]) ||
771*61d06d6bSBaptiste Daroussin 					('+' == p[i] && '|' == p[i + 1]) ||
772*61d06d6bSBaptiste Daroussin 					('|' == p[i] && '+' == p[i + 1]))  {
773*61d06d6bSBaptiste Daroussin 				if (italic)
774*61d06d6bSBaptiste Daroussin 					printf("</i>");
775*61d06d6bSBaptiste Daroussin 				if (bold)
776*61d06d6bSBaptiste Daroussin 					printf("</b>");
777*61d06d6bSBaptiste Daroussin 				italic = bold = 0;
778*61d06d6bSBaptiste Daroussin 				putchar('+');
779*61d06d6bSBaptiste Daroussin 				i += 2;
780*61d06d6bSBaptiste Daroussin 				continue;
781*61d06d6bSBaptiste Daroussin 			}
782*61d06d6bSBaptiste Daroussin 
783*61d06d6bSBaptiste Daroussin 			/* Bold mode. */
784*61d06d6bSBaptiste Daroussin 
785*61d06d6bSBaptiste Daroussin 			if (italic)
786*61d06d6bSBaptiste Daroussin 				printf("</i>");
787*61d06d6bSBaptiste Daroussin 			if ( ! bold)
788*61d06d6bSBaptiste Daroussin 				printf("<b>");
789*61d06d6bSBaptiste Daroussin 			bold = 1;
790*61d06d6bSBaptiste Daroussin 			italic = 0;
791*61d06d6bSBaptiste Daroussin 			i += 2;
792*61d06d6bSBaptiste Daroussin 			html_putchar(p[i]);
793*61d06d6bSBaptiste Daroussin 		}
794*61d06d6bSBaptiste Daroussin 
795*61d06d6bSBaptiste Daroussin 		/*
796*61d06d6bSBaptiste Daroussin 		 * Clean up the last character.
797*61d06d6bSBaptiste Daroussin 		 * We can get to a newline; don't print that.
798*61d06d6bSBaptiste Daroussin 		 */
799*61d06d6bSBaptiste Daroussin 
800*61d06d6bSBaptiste Daroussin 		if (italic)
801*61d06d6bSBaptiste Daroussin 			printf("</i>");
802*61d06d6bSBaptiste Daroussin 		if (bold)
803*61d06d6bSBaptiste Daroussin 			printf("</b>");
804*61d06d6bSBaptiste Daroussin 
805*61d06d6bSBaptiste Daroussin 		if (i == len - 1 && p[i] != '\n')
806*61d06d6bSBaptiste Daroussin 			html_putchar(p[i]);
807*61d06d6bSBaptiste Daroussin 
808*61d06d6bSBaptiste Daroussin 		putchar('\n');
809*61d06d6bSBaptiste Daroussin 	}
810*61d06d6bSBaptiste Daroussin 	free(p);
811*61d06d6bSBaptiste Daroussin 
812*61d06d6bSBaptiste Daroussin 	puts("</pre>\n"
813*61d06d6bSBaptiste Daroussin 	     "</div>");
814*61d06d6bSBaptiste Daroussin 
815*61d06d6bSBaptiste Daroussin 	fclose(f);
816*61d06d6bSBaptiste Daroussin }
817*61d06d6bSBaptiste Daroussin 
818*61d06d6bSBaptiste Daroussin static void
819*61d06d6bSBaptiste Daroussin resp_format(const struct req *req, const char *file)
820*61d06d6bSBaptiste Daroussin {
821*61d06d6bSBaptiste Daroussin 	struct manoutput conf;
822*61d06d6bSBaptiste Daroussin 	struct mparse	*mp;
823*61d06d6bSBaptiste Daroussin 	struct roff_man	*man;
824*61d06d6bSBaptiste Daroussin 	void		*vp;
825*61d06d6bSBaptiste Daroussin 	int		 fd;
826*61d06d6bSBaptiste Daroussin 	int		 usepath;
827*61d06d6bSBaptiste Daroussin 
828*61d06d6bSBaptiste Daroussin 	if (-1 == (fd = open(file, O_RDONLY, 0))) {
829*61d06d6bSBaptiste Daroussin 		puts("<p>You specified an invalid manual file.</p>");
830*61d06d6bSBaptiste Daroussin 		return;
831*61d06d6bSBaptiste Daroussin 	}
832*61d06d6bSBaptiste Daroussin 
833*61d06d6bSBaptiste Daroussin 	mchars_alloc();
834*61d06d6bSBaptiste Daroussin 	mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1,
835*61d06d6bSBaptiste Daroussin 	    MANDOCERR_MAX, NULL, MANDOC_OS_OTHER, req->q.manpath);
836*61d06d6bSBaptiste Daroussin 	mparse_readfd(mp, fd, file);
837*61d06d6bSBaptiste Daroussin 	close(fd);
838*61d06d6bSBaptiste Daroussin 
839*61d06d6bSBaptiste Daroussin 	memset(&conf, 0, sizeof(conf));
840*61d06d6bSBaptiste Daroussin 	conf.fragment = 1;
841*61d06d6bSBaptiste Daroussin 	conf.style = mandoc_strdup(CSS_DIR "/mandoc.css");
842*61d06d6bSBaptiste Daroussin 	usepath = strcmp(req->q.manpath, req->p[0]);
843*61d06d6bSBaptiste Daroussin 	mandoc_asprintf(&conf.man, "/%s%s%s%s%%N.%%S",
844*61d06d6bSBaptiste Daroussin 	    scriptname, *scriptname == '\0' ? "" : "/",
845*61d06d6bSBaptiste Daroussin 	    usepath ? req->q.manpath : "", usepath ? "/" : "");
846*61d06d6bSBaptiste Daroussin 
847*61d06d6bSBaptiste Daroussin 	mparse_result(mp, &man, NULL);
848*61d06d6bSBaptiste Daroussin 	if (man == NULL) {
849*61d06d6bSBaptiste Daroussin 		warnx("fatal mandoc error: %s/%s", req->q.manpath, file);
850*61d06d6bSBaptiste Daroussin 		pg_error_internal();
851*61d06d6bSBaptiste Daroussin 		mparse_free(mp);
852*61d06d6bSBaptiste Daroussin 		mchars_free();
853*61d06d6bSBaptiste Daroussin 		return;
854*61d06d6bSBaptiste Daroussin 	}
855*61d06d6bSBaptiste Daroussin 
856*61d06d6bSBaptiste Daroussin 	vp = html_alloc(&conf);
857*61d06d6bSBaptiste Daroussin 
858*61d06d6bSBaptiste Daroussin 	if (man->macroset == MACROSET_MDOC) {
859*61d06d6bSBaptiste Daroussin 		mdoc_validate(man);
860*61d06d6bSBaptiste Daroussin 		html_mdoc(vp, man);
861*61d06d6bSBaptiste Daroussin 	} else {
862*61d06d6bSBaptiste Daroussin 		man_validate(man);
863*61d06d6bSBaptiste Daroussin 		html_man(vp, man);
864*61d06d6bSBaptiste Daroussin 	}
865*61d06d6bSBaptiste Daroussin 
866*61d06d6bSBaptiste Daroussin 	html_free(vp);
867*61d06d6bSBaptiste Daroussin 	mparse_free(mp);
868*61d06d6bSBaptiste Daroussin 	mchars_free();
869*61d06d6bSBaptiste Daroussin 	free(conf.man);
870*61d06d6bSBaptiste Daroussin 	free(conf.style);
871*61d06d6bSBaptiste Daroussin }
872*61d06d6bSBaptiste Daroussin 
873*61d06d6bSBaptiste Daroussin static void
874*61d06d6bSBaptiste Daroussin resp_show(const struct req *req, const char *file)
875*61d06d6bSBaptiste Daroussin {
876*61d06d6bSBaptiste Daroussin 
877*61d06d6bSBaptiste Daroussin 	if ('.' == file[0] && '/' == file[1])
878*61d06d6bSBaptiste Daroussin 		file += 2;
879*61d06d6bSBaptiste Daroussin 
880*61d06d6bSBaptiste Daroussin 	if ('c' == *file)
881*61d06d6bSBaptiste Daroussin 		resp_catman(req, file);
882*61d06d6bSBaptiste Daroussin 	else
883*61d06d6bSBaptiste Daroussin 		resp_format(req, file);
884*61d06d6bSBaptiste Daroussin }
885*61d06d6bSBaptiste Daroussin 
886*61d06d6bSBaptiste Daroussin static void
887*61d06d6bSBaptiste Daroussin pg_show(struct req *req, const char *fullpath)
888*61d06d6bSBaptiste Daroussin {
889*61d06d6bSBaptiste Daroussin 	char		*manpath;
890*61d06d6bSBaptiste Daroussin 	const char	*file;
891*61d06d6bSBaptiste Daroussin 
892*61d06d6bSBaptiste Daroussin 	if ((file = strchr(fullpath, '/')) == NULL) {
893*61d06d6bSBaptiste Daroussin 		pg_error_badrequest(
894*61d06d6bSBaptiste Daroussin 		    "You did not specify a page to show.");
895*61d06d6bSBaptiste Daroussin 		return;
896*61d06d6bSBaptiste Daroussin 	}
897*61d06d6bSBaptiste Daroussin 	manpath = mandoc_strndup(fullpath, file - fullpath);
898*61d06d6bSBaptiste Daroussin 	file++;
899*61d06d6bSBaptiste Daroussin 
900*61d06d6bSBaptiste Daroussin 	if ( ! validate_manpath(req, manpath)) {
901*61d06d6bSBaptiste Daroussin 		pg_error_badrequest(
902*61d06d6bSBaptiste Daroussin 		    "You specified an invalid manpath.");
903*61d06d6bSBaptiste Daroussin 		free(manpath);
904*61d06d6bSBaptiste Daroussin 		return;
905*61d06d6bSBaptiste Daroussin 	}
906*61d06d6bSBaptiste Daroussin 
907*61d06d6bSBaptiste Daroussin 	/*
908*61d06d6bSBaptiste Daroussin 	 * Begin by chdir()ing into the manpath.
909*61d06d6bSBaptiste Daroussin 	 * This way we can pick up the database files, which are
910*61d06d6bSBaptiste Daroussin 	 * relative to the manpath root.
911*61d06d6bSBaptiste Daroussin 	 */
912*61d06d6bSBaptiste Daroussin 
913*61d06d6bSBaptiste Daroussin 	if (chdir(manpath) == -1) {
914*61d06d6bSBaptiste Daroussin 		warn("chdir %s", manpath);
915*61d06d6bSBaptiste Daroussin 		pg_error_internal();
916*61d06d6bSBaptiste Daroussin 		free(manpath);
917*61d06d6bSBaptiste Daroussin 		return;
918*61d06d6bSBaptiste Daroussin 	}
919*61d06d6bSBaptiste Daroussin 	free(manpath);
920*61d06d6bSBaptiste Daroussin 
921*61d06d6bSBaptiste Daroussin 	if ( ! validate_filename(file)) {
922*61d06d6bSBaptiste Daroussin 		pg_error_badrequest(
923*61d06d6bSBaptiste Daroussin 		    "You specified an invalid manual file.");
924*61d06d6bSBaptiste Daroussin 		return;
925*61d06d6bSBaptiste Daroussin 	}
926*61d06d6bSBaptiste Daroussin 
927*61d06d6bSBaptiste Daroussin 	resp_begin_html(200, NULL, file);
928*61d06d6bSBaptiste Daroussin 	resp_searchform(req, FOCUS_NONE);
929*61d06d6bSBaptiste Daroussin 	resp_show(req, file);
930*61d06d6bSBaptiste Daroussin 	resp_end_html();
931*61d06d6bSBaptiste Daroussin }
932*61d06d6bSBaptiste Daroussin 
933*61d06d6bSBaptiste Daroussin static void
934*61d06d6bSBaptiste Daroussin pg_search(const struct req *req)
935*61d06d6bSBaptiste Daroussin {
936*61d06d6bSBaptiste Daroussin 	struct mansearch	  search;
937*61d06d6bSBaptiste Daroussin 	struct manpaths		  paths;
938*61d06d6bSBaptiste Daroussin 	struct manpage		 *res;
939*61d06d6bSBaptiste Daroussin 	char			**argv;
940*61d06d6bSBaptiste Daroussin 	char			 *query, *rp, *wp;
941*61d06d6bSBaptiste Daroussin 	size_t			  ressz;
942*61d06d6bSBaptiste Daroussin 	int			  argc;
943*61d06d6bSBaptiste Daroussin 
944*61d06d6bSBaptiste Daroussin 	/*
945*61d06d6bSBaptiste Daroussin 	 * Begin by chdir()ing into the root of the manpath.
946*61d06d6bSBaptiste Daroussin 	 * This way we can pick up the database files, which are
947*61d06d6bSBaptiste Daroussin 	 * relative to the manpath root.
948*61d06d6bSBaptiste Daroussin 	 */
949*61d06d6bSBaptiste Daroussin 
950*61d06d6bSBaptiste Daroussin 	if (chdir(req->q.manpath) == -1) {
951*61d06d6bSBaptiste Daroussin 		warn("chdir %s", req->q.manpath);
952*61d06d6bSBaptiste Daroussin 		pg_error_internal();
953*61d06d6bSBaptiste Daroussin 		return;
954*61d06d6bSBaptiste Daroussin 	}
955*61d06d6bSBaptiste Daroussin 
956*61d06d6bSBaptiste Daroussin 	search.arch = req->q.arch;
957*61d06d6bSBaptiste Daroussin 	search.sec = req->q.sec;
958*61d06d6bSBaptiste Daroussin 	search.outkey = "Nd";
959*61d06d6bSBaptiste Daroussin 	search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR;
960*61d06d6bSBaptiste Daroussin 	search.firstmatch = 1;
961*61d06d6bSBaptiste Daroussin 
962*61d06d6bSBaptiste Daroussin 	paths.sz = 1;
963*61d06d6bSBaptiste Daroussin 	paths.paths = mandoc_malloc(sizeof(char *));
964*61d06d6bSBaptiste Daroussin 	paths.paths[0] = mandoc_strdup(".");
965*61d06d6bSBaptiste Daroussin 
966*61d06d6bSBaptiste Daroussin 	/*
967*61d06d6bSBaptiste Daroussin 	 * Break apart at spaces with backslash-escaping.
968*61d06d6bSBaptiste Daroussin 	 */
969*61d06d6bSBaptiste Daroussin 
970*61d06d6bSBaptiste Daroussin 	argc = 0;
971*61d06d6bSBaptiste Daroussin 	argv = NULL;
972*61d06d6bSBaptiste Daroussin 	rp = query = mandoc_strdup(req->q.query);
973*61d06d6bSBaptiste Daroussin 	for (;;) {
974*61d06d6bSBaptiste Daroussin 		while (isspace((unsigned char)*rp))
975*61d06d6bSBaptiste Daroussin 			rp++;
976*61d06d6bSBaptiste Daroussin 		if (*rp == '\0')
977*61d06d6bSBaptiste Daroussin 			break;
978*61d06d6bSBaptiste Daroussin 		argv = mandoc_reallocarray(argv, argc + 1, sizeof(char *));
979*61d06d6bSBaptiste Daroussin 		argv[argc++] = wp = rp;
980*61d06d6bSBaptiste Daroussin 		for (;;) {
981*61d06d6bSBaptiste Daroussin 			if (isspace((unsigned char)*rp)) {
982*61d06d6bSBaptiste Daroussin 				*wp = '\0';
983*61d06d6bSBaptiste Daroussin 				rp++;
984*61d06d6bSBaptiste Daroussin 				break;
985*61d06d6bSBaptiste Daroussin 			}
986*61d06d6bSBaptiste Daroussin 			if (rp[0] == '\\' && rp[1] != '\0')
987*61d06d6bSBaptiste Daroussin 				rp++;
988*61d06d6bSBaptiste Daroussin 			if (wp != rp)
989*61d06d6bSBaptiste Daroussin 				*wp = *rp;
990*61d06d6bSBaptiste Daroussin 			if (*rp == '\0')
991*61d06d6bSBaptiste Daroussin 				break;
992*61d06d6bSBaptiste Daroussin 			wp++;
993*61d06d6bSBaptiste Daroussin 			rp++;
994*61d06d6bSBaptiste Daroussin 		}
995*61d06d6bSBaptiste Daroussin 	}
996*61d06d6bSBaptiste Daroussin 
997*61d06d6bSBaptiste Daroussin 	res = NULL;
998*61d06d6bSBaptiste Daroussin 	ressz = 0;
999*61d06d6bSBaptiste Daroussin 	if (req->isquery && req->q.equal && argc == 1)
1000*61d06d6bSBaptiste Daroussin 		pg_redirect(req, argv[0]);
1001*61d06d6bSBaptiste Daroussin 	else if (mansearch(&search, &paths, argc, argv, &res, &ressz) == 0)
1002*61d06d6bSBaptiste Daroussin 		pg_noresult(req, "You entered an invalid query.");
1003*61d06d6bSBaptiste Daroussin 	else if (ressz == 0)
1004*61d06d6bSBaptiste Daroussin 		pg_noresult(req, "No results found.");
1005*61d06d6bSBaptiste Daroussin 	else
1006*61d06d6bSBaptiste Daroussin 		pg_searchres(req, res, ressz);
1007*61d06d6bSBaptiste Daroussin 
1008*61d06d6bSBaptiste Daroussin 	free(query);
1009*61d06d6bSBaptiste Daroussin 	mansearch_free(res, ressz);
1010*61d06d6bSBaptiste Daroussin 	free(paths.paths[0]);
1011*61d06d6bSBaptiste Daroussin 	free(paths.paths);
1012*61d06d6bSBaptiste Daroussin }
1013*61d06d6bSBaptiste Daroussin 
1014*61d06d6bSBaptiste Daroussin int
1015*61d06d6bSBaptiste Daroussin main(void)
1016*61d06d6bSBaptiste Daroussin {
1017*61d06d6bSBaptiste Daroussin 	struct req	 req;
1018*61d06d6bSBaptiste Daroussin 	struct itimerval itimer;
1019*61d06d6bSBaptiste Daroussin 	const char	*path;
1020*61d06d6bSBaptiste Daroussin 	const char	*querystring;
1021*61d06d6bSBaptiste Daroussin 	int		 i;
1022*61d06d6bSBaptiste Daroussin 
1023*61d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
1024*61d06d6bSBaptiste Daroussin 	/*
1025*61d06d6bSBaptiste Daroussin 	 * The "rpath" pledge could be revoked after mparse_readfd()
1026*61d06d6bSBaptiste Daroussin 	 * if the file desciptor to "/footer.html" would be opened
1027*61d06d6bSBaptiste Daroussin 	 * up front, but it's probably not worth the complication
1028*61d06d6bSBaptiste Daroussin 	 * of the code it would cause: it would require scattering
1029*61d06d6bSBaptiste Daroussin 	 * pledge() calls in multiple low-level resp_*() functions.
1030*61d06d6bSBaptiste Daroussin 	 */
1031*61d06d6bSBaptiste Daroussin 
1032*61d06d6bSBaptiste Daroussin 	if (pledge("stdio rpath", NULL) == -1) {
1033*61d06d6bSBaptiste Daroussin 		warn("pledge");
1034*61d06d6bSBaptiste Daroussin 		pg_error_internal();
1035*61d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
1036*61d06d6bSBaptiste Daroussin 	}
1037*61d06d6bSBaptiste Daroussin #endif
1038*61d06d6bSBaptiste Daroussin 
1039*61d06d6bSBaptiste Daroussin 	/* Poor man's ReDoS mitigation. */
1040*61d06d6bSBaptiste Daroussin 
1041*61d06d6bSBaptiste Daroussin 	itimer.it_value.tv_sec = 2;
1042*61d06d6bSBaptiste Daroussin 	itimer.it_value.tv_usec = 0;
1043*61d06d6bSBaptiste Daroussin 	itimer.it_interval.tv_sec = 2;
1044*61d06d6bSBaptiste Daroussin 	itimer.it_interval.tv_usec = 0;
1045*61d06d6bSBaptiste Daroussin 	if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) {
1046*61d06d6bSBaptiste Daroussin 		warn("setitimer");
1047*61d06d6bSBaptiste Daroussin 		pg_error_internal();
1048*61d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
1049*61d06d6bSBaptiste Daroussin 	}
1050*61d06d6bSBaptiste Daroussin 
1051*61d06d6bSBaptiste Daroussin 	/*
1052*61d06d6bSBaptiste Daroussin 	 * First we change directory into the MAN_DIR so that
1053*61d06d6bSBaptiste Daroussin 	 * subsequent scanning for manpath directories is rooted
1054*61d06d6bSBaptiste Daroussin 	 * relative to the same position.
1055*61d06d6bSBaptiste Daroussin 	 */
1056*61d06d6bSBaptiste Daroussin 
1057*61d06d6bSBaptiste Daroussin 	if (chdir(MAN_DIR) == -1) {
1058*61d06d6bSBaptiste Daroussin 		warn("MAN_DIR: %s", MAN_DIR);
1059*61d06d6bSBaptiste Daroussin 		pg_error_internal();
1060*61d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
1061*61d06d6bSBaptiste Daroussin 	}
1062*61d06d6bSBaptiste Daroussin 
1063*61d06d6bSBaptiste Daroussin 	memset(&req, 0, sizeof(struct req));
1064*61d06d6bSBaptiste Daroussin 	req.q.equal = 1;
1065*61d06d6bSBaptiste Daroussin 	parse_manpath_conf(&req);
1066*61d06d6bSBaptiste Daroussin 
1067*61d06d6bSBaptiste Daroussin 	/* Parse the path info and the query string. */
1068*61d06d6bSBaptiste Daroussin 
1069*61d06d6bSBaptiste Daroussin 	if ((path = getenv("PATH_INFO")) == NULL)
1070*61d06d6bSBaptiste Daroussin 		path = "";
1071*61d06d6bSBaptiste Daroussin 	else if (*path == '/')
1072*61d06d6bSBaptiste Daroussin 		path++;
1073*61d06d6bSBaptiste Daroussin 
1074*61d06d6bSBaptiste Daroussin 	if (*path != '\0') {
1075*61d06d6bSBaptiste Daroussin 		parse_path_info(&req, path);
1076*61d06d6bSBaptiste Daroussin 		if (req.q.manpath == NULL || req.q.sec == NULL ||
1077*61d06d6bSBaptiste Daroussin 		    *req.q.query == '\0' || access(path, F_OK) == -1)
1078*61d06d6bSBaptiste Daroussin 			path = "";
1079*61d06d6bSBaptiste Daroussin 	} else if ((querystring = getenv("QUERY_STRING")) != NULL)
1080*61d06d6bSBaptiste Daroussin 		parse_query_string(&req, querystring);
1081*61d06d6bSBaptiste Daroussin 
1082*61d06d6bSBaptiste Daroussin 	/* Validate parsed data and add defaults. */
1083*61d06d6bSBaptiste Daroussin 
1084*61d06d6bSBaptiste Daroussin 	if (req.q.manpath == NULL)
1085*61d06d6bSBaptiste Daroussin 		req.q.manpath = mandoc_strdup(req.p[0]);
1086*61d06d6bSBaptiste Daroussin 	else if ( ! validate_manpath(&req, req.q.manpath)) {
1087*61d06d6bSBaptiste Daroussin 		pg_error_badrequest(
1088*61d06d6bSBaptiste Daroussin 		    "You specified an invalid manpath.");
1089*61d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
1090*61d06d6bSBaptiste Daroussin 	}
1091*61d06d6bSBaptiste Daroussin 
1092*61d06d6bSBaptiste Daroussin 	if ( ! (NULL == req.q.arch || validate_urifrag(req.q.arch))) {
1093*61d06d6bSBaptiste Daroussin 		pg_error_badrequest(
1094*61d06d6bSBaptiste Daroussin 		    "You specified an invalid architecture.");
1095*61d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
1096*61d06d6bSBaptiste Daroussin 	}
1097*61d06d6bSBaptiste Daroussin 
1098*61d06d6bSBaptiste Daroussin 	/* Dispatch to the three different pages. */
1099*61d06d6bSBaptiste Daroussin 
1100*61d06d6bSBaptiste Daroussin 	if ('\0' != *path)
1101*61d06d6bSBaptiste Daroussin 		pg_show(&req, path);
1102*61d06d6bSBaptiste Daroussin 	else if (NULL != req.q.query)
1103*61d06d6bSBaptiste Daroussin 		pg_search(&req);
1104*61d06d6bSBaptiste Daroussin 	else
1105*61d06d6bSBaptiste Daroussin 		pg_index(&req);
1106*61d06d6bSBaptiste Daroussin 
1107*61d06d6bSBaptiste Daroussin 	free(req.q.manpath);
1108*61d06d6bSBaptiste Daroussin 	free(req.q.arch);
1109*61d06d6bSBaptiste Daroussin 	free(req.q.sec);
1110*61d06d6bSBaptiste Daroussin 	free(req.q.query);
1111*61d06d6bSBaptiste Daroussin 	for (i = 0; i < (int)req.psz; i++)
1112*61d06d6bSBaptiste Daroussin 		free(req.p[i]);
1113*61d06d6bSBaptiste Daroussin 	free(req.p);
1114*61d06d6bSBaptiste Daroussin 	return EXIT_SUCCESS;
1115*61d06d6bSBaptiste Daroussin }
1116*61d06d6bSBaptiste Daroussin 
1117*61d06d6bSBaptiste Daroussin /*
1118*61d06d6bSBaptiste Daroussin  * If PATH_INFO is not a file name, translate it to a query.
1119*61d06d6bSBaptiste Daroussin  */
1120*61d06d6bSBaptiste Daroussin static void
1121*61d06d6bSBaptiste Daroussin parse_path_info(struct req *req, const char *path)
1122*61d06d6bSBaptiste Daroussin {
1123*61d06d6bSBaptiste Daroussin 	char	*dir[4];
1124*61d06d6bSBaptiste Daroussin 	int	 i;
1125*61d06d6bSBaptiste Daroussin 
1126*61d06d6bSBaptiste Daroussin 	req->isquery = 0;
1127*61d06d6bSBaptiste Daroussin 	req->q.equal = 1;
1128*61d06d6bSBaptiste Daroussin 	req->q.manpath = mandoc_strdup(path);
1129*61d06d6bSBaptiste Daroussin 	req->q.arch = NULL;
1130*61d06d6bSBaptiste Daroussin 
1131*61d06d6bSBaptiste Daroussin 	/* Mandatory manual page name. */
1132*61d06d6bSBaptiste Daroussin 	if ((req->q.query = strrchr(req->q.manpath, '/')) == NULL) {
1133*61d06d6bSBaptiste Daroussin 		req->q.query = req->q.manpath;
1134*61d06d6bSBaptiste Daroussin 		req->q.manpath = NULL;
1135*61d06d6bSBaptiste Daroussin 	} else
1136*61d06d6bSBaptiste Daroussin 		*req->q.query++ = '\0';
1137*61d06d6bSBaptiste Daroussin 
1138*61d06d6bSBaptiste Daroussin 	/* Optional trailing section. */
1139*61d06d6bSBaptiste Daroussin 	if ((req->q.sec = strrchr(req->q.query, '.')) != NULL) {
1140*61d06d6bSBaptiste Daroussin 		if(isdigit((unsigned char)req->q.sec[1])) {
1141*61d06d6bSBaptiste Daroussin 			*req->q.sec++ = '\0';
1142*61d06d6bSBaptiste Daroussin 			req->q.sec = mandoc_strdup(req->q.sec);
1143*61d06d6bSBaptiste Daroussin 		} else
1144*61d06d6bSBaptiste Daroussin 			req->q.sec = NULL;
1145*61d06d6bSBaptiste Daroussin 	}
1146*61d06d6bSBaptiste Daroussin 
1147*61d06d6bSBaptiste Daroussin 	/* Handle the case of name[.section] only. */
1148*61d06d6bSBaptiste Daroussin 	if (req->q.manpath == NULL)
1149*61d06d6bSBaptiste Daroussin 		return;
1150*61d06d6bSBaptiste Daroussin 	req->q.query = mandoc_strdup(req->q.query);
1151*61d06d6bSBaptiste Daroussin 
1152*61d06d6bSBaptiste Daroussin 	/* Split directory components. */
1153*61d06d6bSBaptiste Daroussin 	dir[i = 0] = req->q.manpath;
1154*61d06d6bSBaptiste Daroussin 	while ((dir[i + 1] = strchr(dir[i], '/')) != NULL) {
1155*61d06d6bSBaptiste Daroussin 		if (++i == 3) {
1156*61d06d6bSBaptiste Daroussin 			pg_error_badrequest(
1157*61d06d6bSBaptiste Daroussin 			    "You specified too many directory components.");
1158*61d06d6bSBaptiste Daroussin 			exit(EXIT_FAILURE);
1159*61d06d6bSBaptiste Daroussin 		}
1160*61d06d6bSBaptiste Daroussin 		*dir[i]++ = '\0';
1161*61d06d6bSBaptiste Daroussin 	}
1162*61d06d6bSBaptiste Daroussin 
1163*61d06d6bSBaptiste Daroussin 	/* Optional manpath. */
1164*61d06d6bSBaptiste Daroussin 	if ((i = validate_manpath(req, req->q.manpath)) == 0)
1165*61d06d6bSBaptiste Daroussin 		req->q.manpath = NULL;
1166*61d06d6bSBaptiste Daroussin 	else if (dir[1] == NULL)
1167*61d06d6bSBaptiste Daroussin 		return;
1168*61d06d6bSBaptiste Daroussin 
1169*61d06d6bSBaptiste Daroussin 	/* Optional section. */
1170*61d06d6bSBaptiste Daroussin 	if (strncmp(dir[i], "man", 3) == 0) {
1171*61d06d6bSBaptiste Daroussin 		free(req->q.sec);
1172*61d06d6bSBaptiste Daroussin 		req->q.sec = mandoc_strdup(dir[i++] + 3);
1173*61d06d6bSBaptiste Daroussin 	}
1174*61d06d6bSBaptiste Daroussin 	if (dir[i] == NULL) {
1175*61d06d6bSBaptiste Daroussin 		if (req->q.manpath == NULL)
1176*61d06d6bSBaptiste Daroussin 			free(dir[0]);
1177*61d06d6bSBaptiste Daroussin 		return;
1178*61d06d6bSBaptiste Daroussin 	}
1179*61d06d6bSBaptiste Daroussin 	if (dir[i + 1] != NULL) {
1180*61d06d6bSBaptiste Daroussin 		pg_error_badrequest(
1181*61d06d6bSBaptiste Daroussin 		    "You specified an invalid directory component.");
1182*61d06d6bSBaptiste Daroussin 		exit(EXIT_FAILURE);
1183*61d06d6bSBaptiste Daroussin 	}
1184*61d06d6bSBaptiste Daroussin 
1185*61d06d6bSBaptiste Daroussin 	/* Optional architecture. */
1186*61d06d6bSBaptiste Daroussin 	if (i) {
1187*61d06d6bSBaptiste Daroussin 		req->q.arch = mandoc_strdup(dir[i]);
1188*61d06d6bSBaptiste Daroussin 		if (req->q.manpath == NULL)
1189*61d06d6bSBaptiste Daroussin 			free(dir[0]);
1190*61d06d6bSBaptiste Daroussin 	} else
1191*61d06d6bSBaptiste Daroussin 		req->q.arch = dir[0];
1192*61d06d6bSBaptiste Daroussin }
1193*61d06d6bSBaptiste Daroussin 
1194*61d06d6bSBaptiste Daroussin /*
1195*61d06d6bSBaptiste Daroussin  * Scan for indexable paths.
1196*61d06d6bSBaptiste Daroussin  */
1197*61d06d6bSBaptiste Daroussin static void
1198*61d06d6bSBaptiste Daroussin parse_manpath_conf(struct req *req)
1199*61d06d6bSBaptiste Daroussin {
1200*61d06d6bSBaptiste Daroussin 	FILE	*fp;
1201*61d06d6bSBaptiste Daroussin 	char	*dp;
1202*61d06d6bSBaptiste Daroussin 	size_t	 dpsz;
1203*61d06d6bSBaptiste Daroussin 	ssize_t	 len;
1204*61d06d6bSBaptiste Daroussin 
1205*61d06d6bSBaptiste Daroussin 	if ((fp = fopen("manpath.conf", "r")) == NULL) {
1206*61d06d6bSBaptiste Daroussin 		warn("%s/manpath.conf", MAN_DIR);
1207*61d06d6bSBaptiste Daroussin 		pg_error_internal();
1208*61d06d6bSBaptiste Daroussin 		exit(EXIT_FAILURE);
1209*61d06d6bSBaptiste Daroussin 	}
1210*61d06d6bSBaptiste Daroussin 
1211*61d06d6bSBaptiste Daroussin 	dp = NULL;
1212*61d06d6bSBaptiste Daroussin 	dpsz = 0;
1213*61d06d6bSBaptiste Daroussin 
1214*61d06d6bSBaptiste Daroussin 	while ((len = getline(&dp, &dpsz, fp)) != -1) {
1215*61d06d6bSBaptiste Daroussin 		if (dp[len - 1] == '\n')
1216*61d06d6bSBaptiste Daroussin 			dp[--len] = '\0';
1217*61d06d6bSBaptiste Daroussin 		req->p = mandoc_realloc(req->p,
1218*61d06d6bSBaptiste Daroussin 		    (req->psz + 1) * sizeof(char *));
1219*61d06d6bSBaptiste Daroussin 		if ( ! validate_urifrag(dp)) {
1220*61d06d6bSBaptiste Daroussin 			warnx("%s/manpath.conf contains "
1221*61d06d6bSBaptiste Daroussin 			    "unsafe path \"%s\"", MAN_DIR, dp);
1222*61d06d6bSBaptiste Daroussin 			pg_error_internal();
1223*61d06d6bSBaptiste Daroussin 			exit(EXIT_FAILURE);
1224*61d06d6bSBaptiste Daroussin 		}
1225*61d06d6bSBaptiste Daroussin 		if (strchr(dp, '/') != NULL) {
1226*61d06d6bSBaptiste Daroussin 			warnx("%s/manpath.conf contains "
1227*61d06d6bSBaptiste Daroussin 			    "path with slash \"%s\"", MAN_DIR, dp);
1228*61d06d6bSBaptiste Daroussin 			pg_error_internal();
1229*61d06d6bSBaptiste Daroussin 			exit(EXIT_FAILURE);
1230*61d06d6bSBaptiste Daroussin 		}
1231*61d06d6bSBaptiste Daroussin 		req->p[req->psz++] = dp;
1232*61d06d6bSBaptiste Daroussin 		dp = NULL;
1233*61d06d6bSBaptiste Daroussin 		dpsz = 0;
1234*61d06d6bSBaptiste Daroussin 	}
1235*61d06d6bSBaptiste Daroussin 	free(dp);
1236*61d06d6bSBaptiste Daroussin 
1237*61d06d6bSBaptiste Daroussin 	if (req->p == NULL) {
1238*61d06d6bSBaptiste Daroussin 		warnx("%s/manpath.conf is empty", MAN_DIR);
1239*61d06d6bSBaptiste Daroussin 		pg_error_internal();
1240*61d06d6bSBaptiste Daroussin 		exit(EXIT_FAILURE);
1241*61d06d6bSBaptiste Daroussin 	}
1242*61d06d6bSBaptiste Daroussin }
1243