xref: /freebsd/contrib/mandoc/cgi.c (revision 7295610f5da64ab1818458ce007d9eb924496330)
1*7295610fSBaptiste Daroussin /*	$Id: cgi.c,v 1.166 2019/03/06 12:32:41 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4*7295610fSBaptiste Daroussin  * Copyright (c) 2014, 2015, 2016, 2017, 2018 Ingo Schwarze <schwarze@usta.de>
561d06d6bSBaptiste Daroussin  *
661d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
761d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
861d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
961d06d6bSBaptiste Daroussin  *
1061d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1161d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1261d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1361d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1561d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1661d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1761d06d6bSBaptiste Daroussin  */
1861d06d6bSBaptiste Daroussin #include "config.h"
1961d06d6bSBaptiste Daroussin 
2061d06d6bSBaptiste Daroussin #include <sys/types.h>
2161d06d6bSBaptiste Daroussin #include <sys/time.h>
2261d06d6bSBaptiste Daroussin 
2361d06d6bSBaptiste Daroussin #include <ctype.h>
2461d06d6bSBaptiste Daroussin #if HAVE_ERR
2561d06d6bSBaptiste Daroussin #include <err.h>
2661d06d6bSBaptiste Daroussin #endif
2761d06d6bSBaptiste Daroussin #include <errno.h>
2861d06d6bSBaptiste Daroussin #include <fcntl.h>
2961d06d6bSBaptiste Daroussin #include <limits.h>
3061d06d6bSBaptiste Daroussin #include <stdint.h>
3161d06d6bSBaptiste Daroussin #include <stdio.h>
3261d06d6bSBaptiste Daroussin #include <stdlib.h>
3361d06d6bSBaptiste Daroussin #include <string.h>
3461d06d6bSBaptiste Daroussin #include <unistd.h>
3561d06d6bSBaptiste Daroussin 
3661d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
3761d06d6bSBaptiste Daroussin #include "mandoc.h"
3861d06d6bSBaptiste Daroussin #include "roff.h"
3961d06d6bSBaptiste Daroussin #include "mdoc.h"
4061d06d6bSBaptiste Daroussin #include "man.h"
41*7295610fSBaptiste Daroussin #include "mandoc_parse.h"
4261d06d6bSBaptiste Daroussin #include "main.h"
4361d06d6bSBaptiste Daroussin #include "manconf.h"
4461d06d6bSBaptiste Daroussin #include "mansearch.h"
4561d06d6bSBaptiste Daroussin #include "cgi.h"
4661d06d6bSBaptiste Daroussin 
4761d06d6bSBaptiste Daroussin /*
4861d06d6bSBaptiste Daroussin  * A query as passed to the search function.
4961d06d6bSBaptiste Daroussin  */
5061d06d6bSBaptiste Daroussin struct	query {
5161d06d6bSBaptiste Daroussin 	char		*manpath; /* desired manual directory */
5261d06d6bSBaptiste Daroussin 	char		*arch; /* architecture */
5361d06d6bSBaptiste Daroussin 	char		*sec; /* manual section */
5461d06d6bSBaptiste Daroussin 	char		*query; /* unparsed query expression */
5561d06d6bSBaptiste Daroussin 	int		 equal; /* match whole names, not substrings */
5661d06d6bSBaptiste Daroussin };
5761d06d6bSBaptiste Daroussin 
5861d06d6bSBaptiste Daroussin struct	req {
5961d06d6bSBaptiste Daroussin 	struct query	  q;
6061d06d6bSBaptiste Daroussin 	char		**p; /* array of available manpaths */
6161d06d6bSBaptiste Daroussin 	size_t		  psz; /* number of available manpaths */
6261d06d6bSBaptiste Daroussin 	int		  isquery; /* QUERY_STRING used, not PATH_INFO */
6361d06d6bSBaptiste Daroussin };
6461d06d6bSBaptiste Daroussin 
6561d06d6bSBaptiste Daroussin enum	focus {
6661d06d6bSBaptiste Daroussin 	FOCUS_NONE = 0,
6761d06d6bSBaptiste Daroussin 	FOCUS_QUERY
6861d06d6bSBaptiste Daroussin };
6961d06d6bSBaptiste Daroussin 
7061d06d6bSBaptiste Daroussin static	void		 html_print(const char *);
7161d06d6bSBaptiste Daroussin static	void		 html_putchar(char);
7261d06d6bSBaptiste Daroussin static	int		 http_decode(char *);
73*7295610fSBaptiste Daroussin static	void		 http_encode(const char *p);
7461d06d6bSBaptiste Daroussin static	void		 parse_manpath_conf(struct req *);
7561d06d6bSBaptiste Daroussin static	void		 parse_path_info(struct req *req, const char *path);
7661d06d6bSBaptiste Daroussin static	void		 parse_query_string(struct req *, const char *);
7761d06d6bSBaptiste Daroussin static	void		 pg_error_badrequest(const char *);
7861d06d6bSBaptiste Daroussin static	void		 pg_error_internal(void);
7961d06d6bSBaptiste Daroussin static	void		 pg_index(const struct req *);
8061d06d6bSBaptiste Daroussin static	void		 pg_noresult(const struct req *, const char *);
8161d06d6bSBaptiste Daroussin static	void		 pg_redirect(const struct req *, const char *);
8261d06d6bSBaptiste Daroussin static	void		 pg_search(const struct req *);
8361d06d6bSBaptiste Daroussin static	void		 pg_searchres(const struct req *,
8461d06d6bSBaptiste Daroussin 				struct manpage *, size_t);
8561d06d6bSBaptiste Daroussin static	void		 pg_show(struct req *, const char *);
8661d06d6bSBaptiste Daroussin static	void		 resp_begin_html(int, const char *, const char *);
8761d06d6bSBaptiste Daroussin static	void		 resp_begin_http(int, const char *);
8861d06d6bSBaptiste Daroussin static	void		 resp_catman(const struct req *, const char *);
8961d06d6bSBaptiste Daroussin static	void		 resp_copy(const char *);
9061d06d6bSBaptiste Daroussin static	void		 resp_end_html(void);
9161d06d6bSBaptiste Daroussin static	void		 resp_format(const struct req *, const char *);
9261d06d6bSBaptiste Daroussin static	void		 resp_searchform(const struct req *, enum focus);
9361d06d6bSBaptiste Daroussin static	void		 resp_show(const struct req *, const char *);
9461d06d6bSBaptiste Daroussin static	void		 set_query_attr(char **, char **);
95*7295610fSBaptiste Daroussin static	int		 validate_arch(const char *);
9661d06d6bSBaptiste Daroussin static	int		 validate_filename(const char *);
9761d06d6bSBaptiste Daroussin static	int		 validate_manpath(const struct req *, const char *);
9861d06d6bSBaptiste Daroussin static	int		 validate_urifrag(const char *);
9961d06d6bSBaptiste Daroussin 
10061d06d6bSBaptiste Daroussin static	const char	 *scriptname = SCRIPT_NAME;
10161d06d6bSBaptiste Daroussin 
10261d06d6bSBaptiste Daroussin static	const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
10361d06d6bSBaptiste Daroussin static	const char *const sec_numbers[] = {
10461d06d6bSBaptiste Daroussin     "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
10561d06d6bSBaptiste Daroussin };
10661d06d6bSBaptiste Daroussin static	const char *const sec_names[] = {
10761d06d6bSBaptiste Daroussin     "All Sections",
10861d06d6bSBaptiste Daroussin     "1 - General Commands",
10961d06d6bSBaptiste Daroussin     "2 - System Calls",
11061d06d6bSBaptiste Daroussin     "3 - Library Functions",
11161d06d6bSBaptiste Daroussin     "3p - Perl Library",
11261d06d6bSBaptiste Daroussin     "4 - Device Drivers",
11361d06d6bSBaptiste Daroussin     "5 - File Formats",
11461d06d6bSBaptiste Daroussin     "6 - Games",
11561d06d6bSBaptiste Daroussin     "7 - Miscellaneous Information",
11661d06d6bSBaptiste Daroussin     "8 - System Manager\'s Manual",
11761d06d6bSBaptiste Daroussin     "9 - Kernel Developer\'s Manual"
11861d06d6bSBaptiste Daroussin };
11961d06d6bSBaptiste Daroussin static	const int sec_MAX = sizeof(sec_names) / sizeof(char *);
12061d06d6bSBaptiste Daroussin 
12161d06d6bSBaptiste Daroussin static	const char *const arch_names[] = {
12261d06d6bSBaptiste Daroussin     "amd64",       "alpha",       "armv7",	"arm64",
12361d06d6bSBaptiste Daroussin     "hppa",        "i386",        "landisk",
12461d06d6bSBaptiste Daroussin     "loongson",    "luna88k",     "macppc",      "mips64",
12561d06d6bSBaptiste Daroussin     "octeon",      "sgi",         "socppc",      "sparc64",
12661d06d6bSBaptiste Daroussin     "amiga",       "arc",         "armish",      "arm32",
12761d06d6bSBaptiste Daroussin     "atari",       "aviion",      "beagle",      "cats",
12861d06d6bSBaptiste Daroussin     "hppa64",      "hp300",
12961d06d6bSBaptiste Daroussin     "ia64",        "mac68k",      "mvme68k",     "mvme88k",
13061d06d6bSBaptiste Daroussin     "mvmeppc",     "palm",        "pc532",       "pegasos",
13161d06d6bSBaptiste Daroussin     "pmax",        "powerpc",     "solbourne",   "sparc",
13261d06d6bSBaptiste Daroussin     "sun3",        "vax",         "wgrisc",      "x68k",
13361d06d6bSBaptiste Daroussin     "zaurus"
13461d06d6bSBaptiste Daroussin };
13561d06d6bSBaptiste Daroussin static	const int arch_MAX = sizeof(arch_names) / sizeof(char *);
13661d06d6bSBaptiste Daroussin 
13761d06d6bSBaptiste Daroussin /*
13861d06d6bSBaptiste Daroussin  * Print a character, escaping HTML along the way.
13961d06d6bSBaptiste Daroussin  * This will pass non-ASCII straight to output: be warned!
14061d06d6bSBaptiste Daroussin  */
14161d06d6bSBaptiste Daroussin static void
14261d06d6bSBaptiste Daroussin html_putchar(char c)
14361d06d6bSBaptiste Daroussin {
14461d06d6bSBaptiste Daroussin 
14561d06d6bSBaptiste Daroussin 	switch (c) {
14661d06d6bSBaptiste Daroussin 	case '"':
14761d06d6bSBaptiste Daroussin 		printf("&quot;");
14861d06d6bSBaptiste Daroussin 		break;
14961d06d6bSBaptiste Daroussin 	case '&':
15061d06d6bSBaptiste Daroussin 		printf("&amp;");
15161d06d6bSBaptiste Daroussin 		break;
15261d06d6bSBaptiste Daroussin 	case '>':
15361d06d6bSBaptiste Daroussin 		printf("&gt;");
15461d06d6bSBaptiste Daroussin 		break;
15561d06d6bSBaptiste Daroussin 	case '<':
15661d06d6bSBaptiste Daroussin 		printf("&lt;");
15761d06d6bSBaptiste Daroussin 		break;
15861d06d6bSBaptiste Daroussin 	default:
15961d06d6bSBaptiste Daroussin 		putchar((unsigned char)c);
16061d06d6bSBaptiste Daroussin 		break;
16161d06d6bSBaptiste Daroussin 	}
16261d06d6bSBaptiste Daroussin }
16361d06d6bSBaptiste Daroussin 
16461d06d6bSBaptiste Daroussin /*
16561d06d6bSBaptiste Daroussin  * Call through to html_putchar().
16661d06d6bSBaptiste Daroussin  * Accepts NULL strings.
16761d06d6bSBaptiste Daroussin  */
16861d06d6bSBaptiste Daroussin static void
16961d06d6bSBaptiste Daroussin html_print(const char *p)
17061d06d6bSBaptiste Daroussin {
17161d06d6bSBaptiste Daroussin 
17261d06d6bSBaptiste Daroussin 	if (NULL == p)
17361d06d6bSBaptiste Daroussin 		return;
17461d06d6bSBaptiste Daroussin 	while ('\0' != *p)
17561d06d6bSBaptiste Daroussin 		html_putchar(*p++);
17661d06d6bSBaptiste Daroussin }
17761d06d6bSBaptiste Daroussin 
17861d06d6bSBaptiste Daroussin /*
17961d06d6bSBaptiste Daroussin  * Transfer the responsibility for the allocated string *val
18061d06d6bSBaptiste Daroussin  * to the query structure.
18161d06d6bSBaptiste Daroussin  */
18261d06d6bSBaptiste Daroussin static void
18361d06d6bSBaptiste Daroussin set_query_attr(char **attr, char **val)
18461d06d6bSBaptiste Daroussin {
18561d06d6bSBaptiste Daroussin 
18661d06d6bSBaptiste Daroussin 	free(*attr);
18761d06d6bSBaptiste Daroussin 	if (**val == '\0') {
18861d06d6bSBaptiste Daroussin 		*attr = NULL;
18961d06d6bSBaptiste Daroussin 		free(*val);
19061d06d6bSBaptiste Daroussin 	} else
19161d06d6bSBaptiste Daroussin 		*attr = *val;
19261d06d6bSBaptiste Daroussin 	*val = NULL;
19361d06d6bSBaptiste Daroussin }
19461d06d6bSBaptiste Daroussin 
19561d06d6bSBaptiste Daroussin /*
19661d06d6bSBaptiste Daroussin  * Parse the QUERY_STRING for key-value pairs
19761d06d6bSBaptiste Daroussin  * and store the values into the query structure.
19861d06d6bSBaptiste Daroussin  */
19961d06d6bSBaptiste Daroussin static void
20061d06d6bSBaptiste Daroussin parse_query_string(struct req *req, const char *qs)
20161d06d6bSBaptiste Daroussin {
20261d06d6bSBaptiste Daroussin 	char		*key, *val;
20361d06d6bSBaptiste Daroussin 	size_t		 keysz, valsz;
20461d06d6bSBaptiste Daroussin 
20561d06d6bSBaptiste Daroussin 	req->isquery	= 1;
20661d06d6bSBaptiste Daroussin 	req->q.manpath	= NULL;
20761d06d6bSBaptiste Daroussin 	req->q.arch	= NULL;
20861d06d6bSBaptiste Daroussin 	req->q.sec	= NULL;
20961d06d6bSBaptiste Daroussin 	req->q.query	= NULL;
21061d06d6bSBaptiste Daroussin 	req->q.equal	= 1;
21161d06d6bSBaptiste Daroussin 
21261d06d6bSBaptiste Daroussin 	key = val = NULL;
21361d06d6bSBaptiste Daroussin 	while (*qs != '\0') {
21461d06d6bSBaptiste Daroussin 
21561d06d6bSBaptiste Daroussin 		/* Parse one key. */
21661d06d6bSBaptiste Daroussin 
21761d06d6bSBaptiste Daroussin 		keysz = strcspn(qs, "=;&");
21861d06d6bSBaptiste Daroussin 		key = mandoc_strndup(qs, keysz);
21961d06d6bSBaptiste Daroussin 		qs += keysz;
22061d06d6bSBaptiste Daroussin 		if (*qs != '=')
22161d06d6bSBaptiste Daroussin 			goto next;
22261d06d6bSBaptiste Daroussin 
22361d06d6bSBaptiste Daroussin 		/* Parse one value. */
22461d06d6bSBaptiste Daroussin 
22561d06d6bSBaptiste Daroussin 		valsz = strcspn(++qs, ";&");
22661d06d6bSBaptiste Daroussin 		val = mandoc_strndup(qs, valsz);
22761d06d6bSBaptiste Daroussin 		qs += valsz;
22861d06d6bSBaptiste Daroussin 
22961d06d6bSBaptiste Daroussin 		/* Decode and catch encoding errors. */
23061d06d6bSBaptiste Daroussin 
23161d06d6bSBaptiste Daroussin 		if ( ! (http_decode(key) && http_decode(val)))
23261d06d6bSBaptiste Daroussin 			goto next;
23361d06d6bSBaptiste Daroussin 
23461d06d6bSBaptiste Daroussin 		/* Handle key-value pairs. */
23561d06d6bSBaptiste Daroussin 
23661d06d6bSBaptiste Daroussin 		if ( ! strcmp(key, "query"))
23761d06d6bSBaptiste Daroussin 			set_query_attr(&req->q.query, &val);
23861d06d6bSBaptiste Daroussin 
23961d06d6bSBaptiste Daroussin 		else if ( ! strcmp(key, "apropos"))
24061d06d6bSBaptiste Daroussin 			req->q.equal = !strcmp(val, "0");
24161d06d6bSBaptiste Daroussin 
24261d06d6bSBaptiste Daroussin 		else if ( ! strcmp(key, "manpath")) {
24361d06d6bSBaptiste Daroussin #ifdef COMPAT_OLDURI
24461d06d6bSBaptiste Daroussin 			if ( ! strncmp(val, "OpenBSD ", 8)) {
24561d06d6bSBaptiste Daroussin 				val[7] = '-';
24661d06d6bSBaptiste Daroussin 				if ('C' == val[8])
24761d06d6bSBaptiste Daroussin 					val[8] = 'c';
24861d06d6bSBaptiste Daroussin 			}
24961d06d6bSBaptiste Daroussin #endif
25061d06d6bSBaptiste Daroussin 			set_query_attr(&req->q.manpath, &val);
25161d06d6bSBaptiste Daroussin 		}
25261d06d6bSBaptiste Daroussin 
25361d06d6bSBaptiste Daroussin 		else if ( ! (strcmp(key, "sec")
25461d06d6bSBaptiste Daroussin #ifdef COMPAT_OLDURI
25561d06d6bSBaptiste Daroussin 		    && strcmp(key, "sektion")
25661d06d6bSBaptiste Daroussin #endif
25761d06d6bSBaptiste Daroussin 		    )) {
25861d06d6bSBaptiste Daroussin 			if ( ! strcmp(val, "0"))
25961d06d6bSBaptiste Daroussin 				*val = '\0';
26061d06d6bSBaptiste Daroussin 			set_query_attr(&req->q.sec, &val);
26161d06d6bSBaptiste Daroussin 		}
26261d06d6bSBaptiste Daroussin 
26361d06d6bSBaptiste Daroussin 		else if ( ! strcmp(key, "arch")) {
26461d06d6bSBaptiste Daroussin 			if ( ! strcmp(val, "default"))
26561d06d6bSBaptiste Daroussin 				*val = '\0';
26661d06d6bSBaptiste Daroussin 			set_query_attr(&req->q.arch, &val);
26761d06d6bSBaptiste Daroussin 		}
26861d06d6bSBaptiste Daroussin 
26961d06d6bSBaptiste Daroussin 		/*
27061d06d6bSBaptiste Daroussin 		 * The key must be freed in any case.
27161d06d6bSBaptiste Daroussin 		 * The val may have been handed over to the query
27261d06d6bSBaptiste Daroussin 		 * structure, in which case it is now NULL.
27361d06d6bSBaptiste Daroussin 		 */
27461d06d6bSBaptiste Daroussin next:
27561d06d6bSBaptiste Daroussin 		free(key);
27661d06d6bSBaptiste Daroussin 		key = NULL;
27761d06d6bSBaptiste Daroussin 		free(val);
27861d06d6bSBaptiste Daroussin 		val = NULL;
27961d06d6bSBaptiste Daroussin 
28061d06d6bSBaptiste Daroussin 		if (*qs != '\0')
28161d06d6bSBaptiste Daroussin 			qs++;
28261d06d6bSBaptiste Daroussin 	}
28361d06d6bSBaptiste Daroussin }
28461d06d6bSBaptiste Daroussin 
28561d06d6bSBaptiste Daroussin /*
28661d06d6bSBaptiste Daroussin  * HTTP-decode a string.  The standard explanation is that this turns
28761d06d6bSBaptiste Daroussin  * "%4e+foo" into "n foo" in the regular way.  This is done in-place
28861d06d6bSBaptiste Daroussin  * over the allocated string.
28961d06d6bSBaptiste Daroussin  */
29061d06d6bSBaptiste Daroussin static int
29161d06d6bSBaptiste Daroussin http_decode(char *p)
29261d06d6bSBaptiste Daroussin {
29361d06d6bSBaptiste Daroussin 	char             hex[3];
29461d06d6bSBaptiste Daroussin 	char		*q;
29561d06d6bSBaptiste Daroussin 	int              c;
29661d06d6bSBaptiste Daroussin 
29761d06d6bSBaptiste Daroussin 	hex[2] = '\0';
29861d06d6bSBaptiste Daroussin 
29961d06d6bSBaptiste Daroussin 	q = p;
30061d06d6bSBaptiste Daroussin 	for ( ; '\0' != *p; p++, q++) {
30161d06d6bSBaptiste Daroussin 		if ('%' == *p) {
30261d06d6bSBaptiste Daroussin 			if ('\0' == (hex[0] = *(p + 1)))
30361d06d6bSBaptiste Daroussin 				return 0;
30461d06d6bSBaptiste Daroussin 			if ('\0' == (hex[1] = *(p + 2)))
30561d06d6bSBaptiste Daroussin 				return 0;
30661d06d6bSBaptiste Daroussin 			if (1 != sscanf(hex, "%x", &c))
30761d06d6bSBaptiste Daroussin 				return 0;
30861d06d6bSBaptiste Daroussin 			if ('\0' == c)
30961d06d6bSBaptiste Daroussin 				return 0;
31061d06d6bSBaptiste Daroussin 
31161d06d6bSBaptiste Daroussin 			*q = (char)c;
31261d06d6bSBaptiste Daroussin 			p += 2;
31361d06d6bSBaptiste Daroussin 		} else
31461d06d6bSBaptiste Daroussin 			*q = '+' == *p ? ' ' : *p;
31561d06d6bSBaptiste Daroussin 	}
31661d06d6bSBaptiste Daroussin 
31761d06d6bSBaptiste Daroussin 	*q = '\0';
31861d06d6bSBaptiste Daroussin 	return 1;
31961d06d6bSBaptiste Daroussin }
32061d06d6bSBaptiste Daroussin 
32161d06d6bSBaptiste Daroussin static void
322*7295610fSBaptiste Daroussin http_encode(const char *p)
323*7295610fSBaptiste Daroussin {
324*7295610fSBaptiste Daroussin 	for (; *p != '\0'; p++) {
325*7295610fSBaptiste Daroussin 		if (isalnum((unsigned char)*p) == 0 &&
326*7295610fSBaptiste Daroussin 		    strchr("-._~", *p) == NULL)
327*7295610fSBaptiste Daroussin 			printf("%%%2.2X", (unsigned char)*p);
328*7295610fSBaptiste Daroussin 		else
329*7295610fSBaptiste Daroussin 			putchar(*p);
330*7295610fSBaptiste Daroussin 	}
331*7295610fSBaptiste Daroussin }
332*7295610fSBaptiste Daroussin 
333*7295610fSBaptiste Daroussin static void
33461d06d6bSBaptiste Daroussin resp_begin_http(int code, const char *msg)
33561d06d6bSBaptiste Daroussin {
33661d06d6bSBaptiste Daroussin 
33761d06d6bSBaptiste Daroussin 	if (200 != code)
33861d06d6bSBaptiste Daroussin 		printf("Status: %d %s\r\n", code, msg);
33961d06d6bSBaptiste Daroussin 
34061d06d6bSBaptiste Daroussin 	printf("Content-Type: text/html; charset=utf-8\r\n"
34161d06d6bSBaptiste Daroussin 	     "Cache-Control: no-cache\r\n"
34261d06d6bSBaptiste Daroussin 	     "Pragma: no-cache\r\n"
34361d06d6bSBaptiste Daroussin 	     "\r\n");
34461d06d6bSBaptiste Daroussin 
34561d06d6bSBaptiste Daroussin 	fflush(stdout);
34661d06d6bSBaptiste Daroussin }
34761d06d6bSBaptiste Daroussin 
34861d06d6bSBaptiste Daroussin static void
34961d06d6bSBaptiste Daroussin resp_copy(const char *filename)
35061d06d6bSBaptiste Daroussin {
35161d06d6bSBaptiste Daroussin 	char	 buf[4096];
35261d06d6bSBaptiste Daroussin 	ssize_t	 sz;
35361d06d6bSBaptiste Daroussin 	int	 fd;
35461d06d6bSBaptiste Daroussin 
35561d06d6bSBaptiste Daroussin 	if ((fd = open(filename, O_RDONLY)) != -1) {
35661d06d6bSBaptiste Daroussin 		fflush(stdout);
35761d06d6bSBaptiste Daroussin 		while ((sz = read(fd, buf, sizeof(buf))) > 0)
35861d06d6bSBaptiste Daroussin 			write(STDOUT_FILENO, buf, sz);
35961d06d6bSBaptiste Daroussin 		close(fd);
36061d06d6bSBaptiste Daroussin 	}
36161d06d6bSBaptiste Daroussin }
36261d06d6bSBaptiste Daroussin 
36361d06d6bSBaptiste Daroussin static void
36461d06d6bSBaptiste Daroussin resp_begin_html(int code, const char *msg, const char *file)
36561d06d6bSBaptiste Daroussin {
36661d06d6bSBaptiste Daroussin 	char	*cp;
36761d06d6bSBaptiste Daroussin 
36861d06d6bSBaptiste Daroussin 	resp_begin_http(code, msg);
36961d06d6bSBaptiste Daroussin 
37061d06d6bSBaptiste Daroussin 	printf("<!DOCTYPE html>\n"
37161d06d6bSBaptiste Daroussin 	       "<html>\n"
37261d06d6bSBaptiste Daroussin 	       "<head>\n"
37361d06d6bSBaptiste Daroussin 	       "  <meta charset=\"UTF-8\"/>\n"
37461d06d6bSBaptiste Daroussin 	       "  <meta name=\"viewport\""
37561d06d6bSBaptiste Daroussin 		      " content=\"width=device-width, initial-scale=1.0\">\n"
37661d06d6bSBaptiste Daroussin 	       "  <link rel=\"stylesheet\" href=\"%s/mandoc.css\""
37761d06d6bSBaptiste Daroussin 	       " type=\"text/css\" media=\"all\">\n"
37861d06d6bSBaptiste Daroussin 	       "  <title>",
37961d06d6bSBaptiste Daroussin 	       CSS_DIR);
38061d06d6bSBaptiste Daroussin 	if (file != NULL) {
38161d06d6bSBaptiste Daroussin 		if ((cp = strrchr(file, '/')) != NULL)
38261d06d6bSBaptiste Daroussin 			file = cp + 1;
38361d06d6bSBaptiste Daroussin 		if ((cp = strrchr(file, '.')) != NULL) {
38461d06d6bSBaptiste Daroussin 			printf("%.*s(%s) - ", (int)(cp - file), file, cp + 1);
38561d06d6bSBaptiste Daroussin 		} else
38661d06d6bSBaptiste Daroussin 			printf("%s - ", file);
38761d06d6bSBaptiste Daroussin 	}
38861d06d6bSBaptiste Daroussin 	printf("%s</title>\n"
38961d06d6bSBaptiste Daroussin 	       "</head>\n"
39061d06d6bSBaptiste Daroussin 	       "<body>\n",
39161d06d6bSBaptiste Daroussin 	       CUSTOMIZE_TITLE);
39261d06d6bSBaptiste Daroussin 
39361d06d6bSBaptiste Daroussin 	resp_copy(MAN_DIR "/header.html");
39461d06d6bSBaptiste Daroussin }
39561d06d6bSBaptiste Daroussin 
39661d06d6bSBaptiste Daroussin static void
39761d06d6bSBaptiste Daroussin resp_end_html(void)
39861d06d6bSBaptiste Daroussin {
39961d06d6bSBaptiste Daroussin 
40061d06d6bSBaptiste Daroussin 	resp_copy(MAN_DIR "/footer.html");
40161d06d6bSBaptiste Daroussin 
40261d06d6bSBaptiste Daroussin 	puts("</body>\n"
40361d06d6bSBaptiste Daroussin 	     "</html>");
40461d06d6bSBaptiste Daroussin }
40561d06d6bSBaptiste Daroussin 
40661d06d6bSBaptiste Daroussin static void
40761d06d6bSBaptiste Daroussin resp_searchform(const struct req *req, enum focus focus)
40861d06d6bSBaptiste Daroussin {
40961d06d6bSBaptiste Daroussin 	int		 i;
41061d06d6bSBaptiste Daroussin 
41161d06d6bSBaptiste Daroussin 	printf("<form action=\"/%s\" method=\"get\">\n"
41261d06d6bSBaptiste Daroussin 	       "  <fieldset>\n"
41361d06d6bSBaptiste Daroussin 	       "    <legend>Manual Page Search Parameters</legend>\n",
41461d06d6bSBaptiste Daroussin 	       scriptname);
41561d06d6bSBaptiste Daroussin 
41661d06d6bSBaptiste Daroussin 	/* Write query input box. */
41761d06d6bSBaptiste Daroussin 
41861d06d6bSBaptiste Daroussin 	printf("    <input type=\"search\" name=\"query\" value=\"");
41961d06d6bSBaptiste Daroussin 	if (req->q.query != NULL)
42061d06d6bSBaptiste Daroussin 		html_print(req->q.query);
42161d06d6bSBaptiste Daroussin 	printf( "\" size=\"40\"");
42261d06d6bSBaptiste Daroussin 	if (focus == FOCUS_QUERY)
42361d06d6bSBaptiste Daroussin 		printf(" autofocus");
42461d06d6bSBaptiste Daroussin 	puts(">");
42561d06d6bSBaptiste Daroussin 
42661d06d6bSBaptiste Daroussin 	/* Write submission buttons. */
42761d06d6bSBaptiste Daroussin 
42861d06d6bSBaptiste Daroussin 	printf(	"    <button type=\"submit\" name=\"apropos\" value=\"0\">"
42961d06d6bSBaptiste Daroussin 		"man</button>\n"
43061d06d6bSBaptiste Daroussin 		"    <button type=\"submit\" name=\"apropos\" value=\"1\">"
43161d06d6bSBaptiste Daroussin 		"apropos</button>\n"
43261d06d6bSBaptiste Daroussin 		"    <br/>\n");
43361d06d6bSBaptiste Daroussin 
43461d06d6bSBaptiste Daroussin 	/* Write section selector. */
43561d06d6bSBaptiste Daroussin 
43661d06d6bSBaptiste Daroussin 	puts("    <select name=\"sec\">");
43761d06d6bSBaptiste Daroussin 	for (i = 0; i < sec_MAX; i++) {
43861d06d6bSBaptiste Daroussin 		printf("      <option value=\"%s\"", sec_numbers[i]);
43961d06d6bSBaptiste Daroussin 		if (NULL != req->q.sec &&
44061d06d6bSBaptiste Daroussin 		    0 == strcmp(sec_numbers[i], req->q.sec))
44161d06d6bSBaptiste Daroussin 			printf(" selected=\"selected\"");
44261d06d6bSBaptiste Daroussin 		printf(">%s</option>\n", sec_names[i]);
44361d06d6bSBaptiste Daroussin 	}
44461d06d6bSBaptiste Daroussin 	puts("    </select>");
44561d06d6bSBaptiste Daroussin 
44661d06d6bSBaptiste Daroussin 	/* Write architecture selector. */
44761d06d6bSBaptiste Daroussin 
44861d06d6bSBaptiste Daroussin 	printf(	"    <select name=\"arch\">\n"
44961d06d6bSBaptiste Daroussin 		"      <option value=\"default\"");
45061d06d6bSBaptiste Daroussin 	if (NULL == req->q.arch)
45161d06d6bSBaptiste Daroussin 		printf(" selected=\"selected\"");
45261d06d6bSBaptiste Daroussin 	puts(">All Architectures</option>");
45361d06d6bSBaptiste Daroussin 	for (i = 0; i < arch_MAX; i++) {
45461d06d6bSBaptiste Daroussin 		printf("      <option");
45561d06d6bSBaptiste Daroussin 		if (NULL != req->q.arch &&
45661d06d6bSBaptiste Daroussin 		    0 == strcmp(arch_names[i], req->q.arch))
45761d06d6bSBaptiste Daroussin 			printf(" selected=\"selected\"");
45861d06d6bSBaptiste Daroussin 		printf(">%s</option>\n", arch_names[i]);
45961d06d6bSBaptiste Daroussin 	}
46061d06d6bSBaptiste Daroussin 	puts("    </select>");
46161d06d6bSBaptiste Daroussin 
46261d06d6bSBaptiste Daroussin 	/* Write manpath selector. */
46361d06d6bSBaptiste Daroussin 
46461d06d6bSBaptiste Daroussin 	if (req->psz > 1) {
46561d06d6bSBaptiste Daroussin 		puts("    <select name=\"manpath\">");
46661d06d6bSBaptiste Daroussin 		for (i = 0; i < (int)req->psz; i++) {
46761d06d6bSBaptiste Daroussin 			printf("      <option");
46861d06d6bSBaptiste Daroussin 			if (strcmp(req->q.manpath, req->p[i]) == 0)
46961d06d6bSBaptiste Daroussin 				printf(" selected=\"selected\"");
47061d06d6bSBaptiste Daroussin 			printf(">");
47161d06d6bSBaptiste Daroussin 			html_print(req->p[i]);
47261d06d6bSBaptiste Daroussin 			puts("</option>");
47361d06d6bSBaptiste Daroussin 		}
47461d06d6bSBaptiste Daroussin 		puts("    </select>");
47561d06d6bSBaptiste Daroussin 	}
47661d06d6bSBaptiste Daroussin 
47761d06d6bSBaptiste Daroussin 	puts("  </fieldset>\n"
47861d06d6bSBaptiste Daroussin 	     "</form>");
47961d06d6bSBaptiste Daroussin }
48061d06d6bSBaptiste Daroussin 
48161d06d6bSBaptiste Daroussin static int
48261d06d6bSBaptiste Daroussin validate_urifrag(const char *frag)
48361d06d6bSBaptiste Daroussin {
48461d06d6bSBaptiste Daroussin 
48561d06d6bSBaptiste Daroussin 	while ('\0' != *frag) {
48661d06d6bSBaptiste Daroussin 		if ( ! (isalnum((unsigned char)*frag) ||
48761d06d6bSBaptiste Daroussin 		    '-' == *frag || '.' == *frag ||
48861d06d6bSBaptiste Daroussin 		    '/' == *frag || '_' == *frag))
48961d06d6bSBaptiste Daroussin 			return 0;
49061d06d6bSBaptiste Daroussin 		frag++;
49161d06d6bSBaptiste Daroussin 	}
49261d06d6bSBaptiste Daroussin 	return 1;
49361d06d6bSBaptiste Daroussin }
49461d06d6bSBaptiste Daroussin 
49561d06d6bSBaptiste Daroussin static int
49661d06d6bSBaptiste Daroussin validate_manpath(const struct req *req, const char* manpath)
49761d06d6bSBaptiste Daroussin {
49861d06d6bSBaptiste Daroussin 	size_t	 i;
49961d06d6bSBaptiste Daroussin 
50061d06d6bSBaptiste Daroussin 	for (i = 0; i < req->psz; i++)
50161d06d6bSBaptiste Daroussin 		if ( ! strcmp(manpath, req->p[i]))
50261d06d6bSBaptiste Daroussin 			return 1;
50361d06d6bSBaptiste Daroussin 
50461d06d6bSBaptiste Daroussin 	return 0;
50561d06d6bSBaptiste Daroussin }
50661d06d6bSBaptiste Daroussin 
50761d06d6bSBaptiste Daroussin static int
508*7295610fSBaptiste Daroussin validate_arch(const char *arch)
509*7295610fSBaptiste Daroussin {
510*7295610fSBaptiste Daroussin 	int	 i;
511*7295610fSBaptiste Daroussin 
512*7295610fSBaptiste Daroussin 	for (i = 0; i < arch_MAX; i++)
513*7295610fSBaptiste Daroussin 		if (strcmp(arch, arch_names[i]) == 0)
514*7295610fSBaptiste Daroussin 			return 1;
515*7295610fSBaptiste Daroussin 
516*7295610fSBaptiste Daroussin 	return 0;
517*7295610fSBaptiste Daroussin }
518*7295610fSBaptiste Daroussin 
519*7295610fSBaptiste Daroussin static int
52061d06d6bSBaptiste Daroussin validate_filename(const char *file)
52161d06d6bSBaptiste Daroussin {
52261d06d6bSBaptiste Daroussin 
52361d06d6bSBaptiste Daroussin 	if ('.' == file[0] && '/' == file[1])
52461d06d6bSBaptiste Daroussin 		file += 2;
52561d06d6bSBaptiste Daroussin 
52661d06d6bSBaptiste Daroussin 	return ! (strstr(file, "../") || strstr(file, "/..") ||
52761d06d6bSBaptiste Daroussin 	    (strncmp(file, "man", 3) && strncmp(file, "cat", 3)));
52861d06d6bSBaptiste Daroussin }
52961d06d6bSBaptiste Daroussin 
53061d06d6bSBaptiste Daroussin static void
53161d06d6bSBaptiste Daroussin pg_index(const struct req *req)
53261d06d6bSBaptiste Daroussin {
53361d06d6bSBaptiste Daroussin 
53461d06d6bSBaptiste Daroussin 	resp_begin_html(200, NULL, NULL);
53561d06d6bSBaptiste Daroussin 	resp_searchform(req, FOCUS_QUERY);
53661d06d6bSBaptiste Daroussin 	printf("<p>\n"
53761d06d6bSBaptiste Daroussin 	       "This web interface is documented in the\n"
53861d06d6bSBaptiste Daroussin 	       "<a class=\"Xr\" href=\"/%s%sman.cgi.8\">man.cgi(8)</a>\n"
53961d06d6bSBaptiste Daroussin 	       "manual, and the\n"
54061d06d6bSBaptiste Daroussin 	       "<a class=\"Xr\" href=\"/%s%sapropos.1\">apropos(1)</a>\n"
54161d06d6bSBaptiste Daroussin 	       "manual explains the query syntax.\n"
54261d06d6bSBaptiste Daroussin 	       "</p>\n",
54361d06d6bSBaptiste Daroussin 	       scriptname, *scriptname == '\0' ? "" : "/",
54461d06d6bSBaptiste Daroussin 	       scriptname, *scriptname == '\0' ? "" : "/");
54561d06d6bSBaptiste Daroussin 	resp_end_html();
54661d06d6bSBaptiste Daroussin }
54761d06d6bSBaptiste Daroussin 
54861d06d6bSBaptiste Daroussin static void
54961d06d6bSBaptiste Daroussin pg_noresult(const struct req *req, const char *msg)
55061d06d6bSBaptiste Daroussin {
55161d06d6bSBaptiste Daroussin 	resp_begin_html(200, NULL, NULL);
55261d06d6bSBaptiste Daroussin 	resp_searchform(req, FOCUS_QUERY);
55361d06d6bSBaptiste Daroussin 	puts("<p>");
55461d06d6bSBaptiste Daroussin 	puts(msg);
55561d06d6bSBaptiste Daroussin 	puts("</p>");
55661d06d6bSBaptiste Daroussin 	resp_end_html();
55761d06d6bSBaptiste Daroussin }
55861d06d6bSBaptiste Daroussin 
55961d06d6bSBaptiste Daroussin static void
56061d06d6bSBaptiste Daroussin pg_error_badrequest(const char *msg)
56161d06d6bSBaptiste Daroussin {
56261d06d6bSBaptiste Daroussin 
56361d06d6bSBaptiste Daroussin 	resp_begin_html(400, "Bad Request", NULL);
56461d06d6bSBaptiste Daroussin 	puts("<h1>Bad Request</h1>\n"
56561d06d6bSBaptiste Daroussin 	     "<p>\n");
56661d06d6bSBaptiste Daroussin 	puts(msg);
56761d06d6bSBaptiste Daroussin 	printf("Try again from the\n"
56861d06d6bSBaptiste Daroussin 	       "<a href=\"/%s\">main page</a>.\n"
56961d06d6bSBaptiste Daroussin 	       "</p>", scriptname);
57061d06d6bSBaptiste Daroussin 	resp_end_html();
57161d06d6bSBaptiste Daroussin }
57261d06d6bSBaptiste Daroussin 
57361d06d6bSBaptiste Daroussin static void
57461d06d6bSBaptiste Daroussin pg_error_internal(void)
57561d06d6bSBaptiste Daroussin {
57661d06d6bSBaptiste Daroussin 	resp_begin_html(500, "Internal Server Error", NULL);
57761d06d6bSBaptiste Daroussin 	puts("<p>Internal Server Error</p>");
57861d06d6bSBaptiste Daroussin 	resp_end_html();
57961d06d6bSBaptiste Daroussin }
58061d06d6bSBaptiste Daroussin 
58161d06d6bSBaptiste Daroussin static void
58261d06d6bSBaptiste Daroussin pg_redirect(const struct req *req, const char *name)
58361d06d6bSBaptiste Daroussin {
58461d06d6bSBaptiste Daroussin 	printf("Status: 303 See Other\r\n"
58561d06d6bSBaptiste Daroussin 	    "Location: /");
58661d06d6bSBaptiste Daroussin 	if (*scriptname != '\0')
58761d06d6bSBaptiste Daroussin 		printf("%s/", scriptname);
58861d06d6bSBaptiste Daroussin 	if (strcmp(req->q.manpath, req->p[0]))
58961d06d6bSBaptiste Daroussin 		printf("%s/", req->q.manpath);
59061d06d6bSBaptiste Daroussin 	if (req->q.arch != NULL)
59161d06d6bSBaptiste Daroussin 		printf("%s/", req->q.arch);
592*7295610fSBaptiste Daroussin 	http_encode(name);
593*7295610fSBaptiste Daroussin 	if (req->q.sec != NULL) {
594*7295610fSBaptiste Daroussin 		putchar('.');
595*7295610fSBaptiste Daroussin 		http_encode(req->q.sec);
596*7295610fSBaptiste Daroussin 	}
59761d06d6bSBaptiste Daroussin 	printf("\r\nContent-Type: text/html; charset=utf-8\r\n\r\n");
59861d06d6bSBaptiste Daroussin }
59961d06d6bSBaptiste Daroussin 
60061d06d6bSBaptiste Daroussin static void
60161d06d6bSBaptiste Daroussin pg_searchres(const struct req *req, struct manpage *r, size_t sz)
60261d06d6bSBaptiste Daroussin {
60361d06d6bSBaptiste Daroussin 	char		*arch, *archend;
60461d06d6bSBaptiste Daroussin 	const char	*sec;
60561d06d6bSBaptiste Daroussin 	size_t		 i, iuse;
60661d06d6bSBaptiste Daroussin 	int		 archprio, archpriouse;
60761d06d6bSBaptiste Daroussin 	int		 prio, priouse;
60861d06d6bSBaptiste Daroussin 
60961d06d6bSBaptiste Daroussin 	for (i = 0; i < sz; i++) {
61061d06d6bSBaptiste Daroussin 		if (validate_filename(r[i].file))
61161d06d6bSBaptiste Daroussin 			continue;
61261d06d6bSBaptiste Daroussin 		warnx("invalid filename %s in %s database",
61361d06d6bSBaptiste Daroussin 		    r[i].file, req->q.manpath);
61461d06d6bSBaptiste Daroussin 		pg_error_internal();
61561d06d6bSBaptiste Daroussin 		return;
61661d06d6bSBaptiste Daroussin 	}
61761d06d6bSBaptiste Daroussin 
61861d06d6bSBaptiste Daroussin 	if (req->isquery && sz == 1) {
61961d06d6bSBaptiste Daroussin 		/*
62061d06d6bSBaptiste Daroussin 		 * If we have just one result, then jump there now
62161d06d6bSBaptiste Daroussin 		 * without any delay.
62261d06d6bSBaptiste Daroussin 		 */
62361d06d6bSBaptiste Daroussin 		printf("Status: 303 See Other\r\n"
62461d06d6bSBaptiste Daroussin 		    "Location: /");
62561d06d6bSBaptiste Daroussin 		if (*scriptname != '\0')
62661d06d6bSBaptiste Daroussin 			printf("%s/", scriptname);
62761d06d6bSBaptiste Daroussin 		if (strcmp(req->q.manpath, req->p[0]))
62861d06d6bSBaptiste Daroussin 			printf("%s/", req->q.manpath);
62961d06d6bSBaptiste Daroussin 		printf("%s\r\n"
63061d06d6bSBaptiste Daroussin 		    "Content-Type: text/html; charset=utf-8\r\n\r\n",
63161d06d6bSBaptiste Daroussin 		    r[0].file);
63261d06d6bSBaptiste Daroussin 		return;
63361d06d6bSBaptiste Daroussin 	}
63461d06d6bSBaptiste Daroussin 
63561d06d6bSBaptiste Daroussin 	/*
63661d06d6bSBaptiste Daroussin 	 * In man(1) mode, show one of the pages
63761d06d6bSBaptiste Daroussin 	 * even if more than one is found.
63861d06d6bSBaptiste Daroussin 	 */
63961d06d6bSBaptiste Daroussin 
64061d06d6bSBaptiste Daroussin 	iuse = 0;
64161d06d6bSBaptiste Daroussin 	if (req->q.equal || sz == 1) {
64261d06d6bSBaptiste Daroussin 		priouse = 20;
64361d06d6bSBaptiste Daroussin 		archpriouse = 3;
64461d06d6bSBaptiste Daroussin 		for (i = 0; i < sz; i++) {
64561d06d6bSBaptiste Daroussin 			sec = r[i].file;
64661d06d6bSBaptiste Daroussin 			sec += strcspn(sec, "123456789");
64761d06d6bSBaptiste Daroussin 			if (sec[0] == '\0')
64861d06d6bSBaptiste Daroussin 				continue;
64961d06d6bSBaptiste Daroussin 			prio = sec_prios[sec[0] - '1'];
65061d06d6bSBaptiste Daroussin 			if (sec[1] != '/')
65161d06d6bSBaptiste Daroussin 				prio += 10;
65261d06d6bSBaptiste Daroussin 			if (req->q.arch == NULL) {
65361d06d6bSBaptiste Daroussin 				archprio =
65461d06d6bSBaptiste Daroussin 				    ((arch = strchr(sec + 1, '/'))
65561d06d6bSBaptiste Daroussin 					== NULL) ? 3 :
65661d06d6bSBaptiste Daroussin 				    ((archend = strchr(arch + 1, '/'))
65761d06d6bSBaptiste Daroussin 					== NULL) ? 0 :
65861d06d6bSBaptiste Daroussin 				    strncmp(arch, "amd64/",
65961d06d6bSBaptiste Daroussin 					archend - arch) ? 2 : 1;
66061d06d6bSBaptiste Daroussin 				if (archprio < archpriouse) {
66161d06d6bSBaptiste Daroussin 					archpriouse = archprio;
66261d06d6bSBaptiste Daroussin 					priouse = prio;
66361d06d6bSBaptiste Daroussin 					iuse = i;
66461d06d6bSBaptiste Daroussin 					continue;
66561d06d6bSBaptiste Daroussin 				}
66661d06d6bSBaptiste Daroussin 				if (archprio > archpriouse)
66761d06d6bSBaptiste Daroussin 					continue;
66861d06d6bSBaptiste Daroussin 			}
66961d06d6bSBaptiste Daroussin 			if (prio >= priouse)
67061d06d6bSBaptiste Daroussin 				continue;
67161d06d6bSBaptiste Daroussin 			priouse = prio;
67261d06d6bSBaptiste Daroussin 			iuse = i;
67361d06d6bSBaptiste Daroussin 		}
67461d06d6bSBaptiste Daroussin 		resp_begin_html(200, NULL, r[iuse].file);
67561d06d6bSBaptiste Daroussin 	} else
67661d06d6bSBaptiste Daroussin 		resp_begin_html(200, NULL, NULL);
67761d06d6bSBaptiste Daroussin 
67861d06d6bSBaptiste Daroussin 	resp_searchform(req,
67961d06d6bSBaptiste Daroussin 	    req->q.equal || sz == 1 ? FOCUS_NONE : FOCUS_QUERY);
68061d06d6bSBaptiste Daroussin 
68161d06d6bSBaptiste Daroussin 	if (sz > 1) {
68261d06d6bSBaptiste Daroussin 		puts("<table class=\"results\">");
68361d06d6bSBaptiste Daroussin 		for (i = 0; i < sz; i++) {
68461d06d6bSBaptiste Daroussin 			printf("  <tr>\n"
68561d06d6bSBaptiste Daroussin 			       "    <td>"
68661d06d6bSBaptiste Daroussin 			       "<a class=\"Xr\" href=\"/");
68761d06d6bSBaptiste Daroussin 			if (*scriptname != '\0')
68861d06d6bSBaptiste Daroussin 				printf("%s/", scriptname);
68961d06d6bSBaptiste Daroussin 			if (strcmp(req->q.manpath, req->p[0]))
69061d06d6bSBaptiste Daroussin 				printf("%s/", req->q.manpath);
69161d06d6bSBaptiste Daroussin 			printf("%s\">", r[i].file);
69261d06d6bSBaptiste Daroussin 			html_print(r[i].names);
69361d06d6bSBaptiste Daroussin 			printf("</a></td>\n"
69461d06d6bSBaptiste Daroussin 			       "    <td><span class=\"Nd\">");
69561d06d6bSBaptiste Daroussin 			html_print(r[i].output);
69661d06d6bSBaptiste Daroussin 			puts("</span></td>\n"
69761d06d6bSBaptiste Daroussin 			     "  </tr>");
69861d06d6bSBaptiste Daroussin 		}
69961d06d6bSBaptiste Daroussin 		puts("</table>");
70061d06d6bSBaptiste Daroussin 	}
70161d06d6bSBaptiste Daroussin 
70261d06d6bSBaptiste Daroussin 	if (req->q.equal || sz == 1) {
70361d06d6bSBaptiste Daroussin 		puts("<hr>");
70461d06d6bSBaptiste Daroussin 		resp_show(req, r[iuse].file);
70561d06d6bSBaptiste Daroussin 	}
70661d06d6bSBaptiste Daroussin 
70761d06d6bSBaptiste Daroussin 	resp_end_html();
70861d06d6bSBaptiste Daroussin }
70961d06d6bSBaptiste Daroussin 
71061d06d6bSBaptiste Daroussin static void
71161d06d6bSBaptiste Daroussin resp_catman(const struct req *req, const char *file)
71261d06d6bSBaptiste Daroussin {
71361d06d6bSBaptiste Daroussin 	FILE		*f;
71461d06d6bSBaptiste Daroussin 	char		*p;
71561d06d6bSBaptiste Daroussin 	size_t		 sz;
71661d06d6bSBaptiste Daroussin 	ssize_t		 len;
71761d06d6bSBaptiste Daroussin 	int		 i;
71861d06d6bSBaptiste Daroussin 	int		 italic, bold;
71961d06d6bSBaptiste Daroussin 
72061d06d6bSBaptiste Daroussin 	if ((f = fopen(file, "r")) == NULL) {
72161d06d6bSBaptiste Daroussin 		puts("<p>You specified an invalid manual file.</p>");
72261d06d6bSBaptiste Daroussin 		return;
72361d06d6bSBaptiste Daroussin 	}
72461d06d6bSBaptiste Daroussin 
72561d06d6bSBaptiste Daroussin 	puts("<div class=\"catman\">\n"
72661d06d6bSBaptiste Daroussin 	     "<pre>");
72761d06d6bSBaptiste Daroussin 
72861d06d6bSBaptiste Daroussin 	p = NULL;
72961d06d6bSBaptiste Daroussin 	sz = 0;
73061d06d6bSBaptiste Daroussin 
73161d06d6bSBaptiste Daroussin 	while ((len = getline(&p, &sz, f)) != -1) {
73261d06d6bSBaptiste Daroussin 		bold = italic = 0;
73361d06d6bSBaptiste Daroussin 		for (i = 0; i < len - 1; i++) {
73461d06d6bSBaptiste Daroussin 			/*
73561d06d6bSBaptiste Daroussin 			 * This means that the catpage is out of state.
73661d06d6bSBaptiste Daroussin 			 * Ignore it and keep going (although the
73761d06d6bSBaptiste Daroussin 			 * catpage is bogus).
73861d06d6bSBaptiste Daroussin 			 */
73961d06d6bSBaptiste Daroussin 
74061d06d6bSBaptiste Daroussin 			if ('\b' == p[i] || '\n' == p[i])
74161d06d6bSBaptiste Daroussin 				continue;
74261d06d6bSBaptiste Daroussin 
74361d06d6bSBaptiste Daroussin 			/*
74461d06d6bSBaptiste Daroussin 			 * Print a regular character.
74561d06d6bSBaptiste Daroussin 			 * Close out any bold/italic scopes.
74661d06d6bSBaptiste Daroussin 			 * If we're in back-space mode, make sure we'll
74761d06d6bSBaptiste Daroussin 			 * have something to enter when we backspace.
74861d06d6bSBaptiste Daroussin 			 */
74961d06d6bSBaptiste Daroussin 
75061d06d6bSBaptiste Daroussin 			if ('\b' != p[i + 1]) {
75161d06d6bSBaptiste Daroussin 				if (italic)
75261d06d6bSBaptiste Daroussin 					printf("</i>");
75361d06d6bSBaptiste Daroussin 				if (bold)
75461d06d6bSBaptiste Daroussin 					printf("</b>");
75561d06d6bSBaptiste Daroussin 				italic = bold = 0;
75661d06d6bSBaptiste Daroussin 				html_putchar(p[i]);
75761d06d6bSBaptiste Daroussin 				continue;
75861d06d6bSBaptiste Daroussin 			} else if (i + 2 >= len)
75961d06d6bSBaptiste Daroussin 				continue;
76061d06d6bSBaptiste Daroussin 
76161d06d6bSBaptiste Daroussin 			/* Italic mode. */
76261d06d6bSBaptiste Daroussin 
76361d06d6bSBaptiste Daroussin 			if ('_' == p[i]) {
76461d06d6bSBaptiste Daroussin 				if (bold)
76561d06d6bSBaptiste Daroussin 					printf("</b>");
76661d06d6bSBaptiste Daroussin 				if ( ! italic)
76761d06d6bSBaptiste Daroussin 					printf("<i>");
76861d06d6bSBaptiste Daroussin 				bold = 0;
76961d06d6bSBaptiste Daroussin 				italic = 1;
77061d06d6bSBaptiste Daroussin 				i += 2;
77161d06d6bSBaptiste Daroussin 				html_putchar(p[i]);
77261d06d6bSBaptiste Daroussin 				continue;
77361d06d6bSBaptiste Daroussin 			}
77461d06d6bSBaptiste Daroussin 
77561d06d6bSBaptiste Daroussin 			/*
77661d06d6bSBaptiste Daroussin 			 * Handle funny behaviour troff-isms.
77761d06d6bSBaptiste Daroussin 			 * These grok'd from the original man2html.c.
77861d06d6bSBaptiste Daroussin 			 */
77961d06d6bSBaptiste Daroussin 
78061d06d6bSBaptiste Daroussin 			if (('+' == p[i] && 'o' == p[i + 2]) ||
78161d06d6bSBaptiste Daroussin 					('o' == p[i] && '+' == p[i + 2]) ||
78261d06d6bSBaptiste Daroussin 					('|' == p[i] && '=' == p[i + 2]) ||
78361d06d6bSBaptiste Daroussin 					('=' == p[i] && '|' == p[i + 2]) ||
78461d06d6bSBaptiste Daroussin 					('*' == p[i] && '=' == p[i + 2]) ||
78561d06d6bSBaptiste Daroussin 					('=' == p[i] && '*' == p[i + 2]) ||
78661d06d6bSBaptiste Daroussin 					('*' == p[i] && '|' == p[i + 2]) ||
78761d06d6bSBaptiste Daroussin 					('|' == p[i] && '*' == p[i + 2]))  {
78861d06d6bSBaptiste Daroussin 				if (italic)
78961d06d6bSBaptiste Daroussin 					printf("</i>");
79061d06d6bSBaptiste Daroussin 				if (bold)
79161d06d6bSBaptiste Daroussin 					printf("</b>");
79261d06d6bSBaptiste Daroussin 				italic = bold = 0;
79361d06d6bSBaptiste Daroussin 				putchar('*');
79461d06d6bSBaptiste Daroussin 				i += 2;
79561d06d6bSBaptiste Daroussin 				continue;
79661d06d6bSBaptiste Daroussin 			} else if (('|' == p[i] && '-' == p[i + 2]) ||
79761d06d6bSBaptiste Daroussin 					('-' == p[i] && '|' == p[i + 1]) ||
79861d06d6bSBaptiste Daroussin 					('+' == p[i] && '-' == p[i + 1]) ||
79961d06d6bSBaptiste Daroussin 					('-' == p[i] && '+' == p[i + 1]) ||
80061d06d6bSBaptiste Daroussin 					('+' == p[i] && '|' == p[i + 1]) ||
80161d06d6bSBaptiste Daroussin 					('|' == p[i] && '+' == p[i + 1]))  {
80261d06d6bSBaptiste Daroussin 				if (italic)
80361d06d6bSBaptiste Daroussin 					printf("</i>");
80461d06d6bSBaptiste Daroussin 				if (bold)
80561d06d6bSBaptiste Daroussin 					printf("</b>");
80661d06d6bSBaptiste Daroussin 				italic = bold = 0;
80761d06d6bSBaptiste Daroussin 				putchar('+');
80861d06d6bSBaptiste Daroussin 				i += 2;
80961d06d6bSBaptiste Daroussin 				continue;
81061d06d6bSBaptiste Daroussin 			}
81161d06d6bSBaptiste Daroussin 
81261d06d6bSBaptiste Daroussin 			/* Bold mode. */
81361d06d6bSBaptiste Daroussin 
81461d06d6bSBaptiste Daroussin 			if (italic)
81561d06d6bSBaptiste Daroussin 				printf("</i>");
81661d06d6bSBaptiste Daroussin 			if ( ! bold)
81761d06d6bSBaptiste Daroussin 				printf("<b>");
81861d06d6bSBaptiste Daroussin 			bold = 1;
81961d06d6bSBaptiste Daroussin 			italic = 0;
82061d06d6bSBaptiste Daroussin 			i += 2;
82161d06d6bSBaptiste Daroussin 			html_putchar(p[i]);
82261d06d6bSBaptiste Daroussin 		}
82361d06d6bSBaptiste Daroussin 
82461d06d6bSBaptiste Daroussin 		/*
82561d06d6bSBaptiste Daroussin 		 * Clean up the last character.
82661d06d6bSBaptiste Daroussin 		 * We can get to a newline; don't print that.
82761d06d6bSBaptiste Daroussin 		 */
82861d06d6bSBaptiste Daroussin 
82961d06d6bSBaptiste Daroussin 		if (italic)
83061d06d6bSBaptiste Daroussin 			printf("</i>");
83161d06d6bSBaptiste Daroussin 		if (bold)
83261d06d6bSBaptiste Daroussin 			printf("</b>");
83361d06d6bSBaptiste Daroussin 
83461d06d6bSBaptiste Daroussin 		if (i == len - 1 && p[i] != '\n')
83561d06d6bSBaptiste Daroussin 			html_putchar(p[i]);
83661d06d6bSBaptiste Daroussin 
83761d06d6bSBaptiste Daroussin 		putchar('\n');
83861d06d6bSBaptiste Daroussin 	}
83961d06d6bSBaptiste Daroussin 	free(p);
84061d06d6bSBaptiste Daroussin 
84161d06d6bSBaptiste Daroussin 	puts("</pre>\n"
84261d06d6bSBaptiste Daroussin 	     "</div>");
84361d06d6bSBaptiste Daroussin 
84461d06d6bSBaptiste Daroussin 	fclose(f);
84561d06d6bSBaptiste Daroussin }
84661d06d6bSBaptiste Daroussin 
84761d06d6bSBaptiste Daroussin static void
84861d06d6bSBaptiste Daroussin resp_format(const struct req *req, const char *file)
84961d06d6bSBaptiste Daroussin {
85061d06d6bSBaptiste Daroussin 	struct manoutput conf;
85161d06d6bSBaptiste Daroussin 	struct mparse	*mp;
852*7295610fSBaptiste Daroussin 	struct roff_meta *meta;
85361d06d6bSBaptiste Daroussin 	void		*vp;
85461d06d6bSBaptiste Daroussin 	int		 fd;
85561d06d6bSBaptiste Daroussin 	int		 usepath;
85661d06d6bSBaptiste Daroussin 
85761d06d6bSBaptiste Daroussin 	if (-1 == (fd = open(file, O_RDONLY, 0))) {
85861d06d6bSBaptiste Daroussin 		puts("<p>You specified an invalid manual file.</p>");
85961d06d6bSBaptiste Daroussin 		return;
86061d06d6bSBaptiste Daroussin 	}
86161d06d6bSBaptiste Daroussin 
86261d06d6bSBaptiste Daroussin 	mchars_alloc();
863*7295610fSBaptiste Daroussin 	mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
864*7295610fSBaptiste Daroussin 	    MPARSE_VALIDATE, MANDOC_OS_OTHER, req->q.manpath);
86561d06d6bSBaptiste Daroussin 	mparse_readfd(mp, fd, file);
86661d06d6bSBaptiste Daroussin 	close(fd);
867*7295610fSBaptiste Daroussin 	meta = mparse_result(mp);
86861d06d6bSBaptiste Daroussin 
86961d06d6bSBaptiste Daroussin 	memset(&conf, 0, sizeof(conf));
87061d06d6bSBaptiste Daroussin 	conf.fragment = 1;
87161d06d6bSBaptiste Daroussin 	conf.style = mandoc_strdup(CSS_DIR "/mandoc.css");
872*7295610fSBaptiste Daroussin 	conf.toc = 1;
87361d06d6bSBaptiste Daroussin 	usepath = strcmp(req->q.manpath, req->p[0]);
87461d06d6bSBaptiste Daroussin 	mandoc_asprintf(&conf.man, "/%s%s%s%s%%N.%%S",
87561d06d6bSBaptiste Daroussin 	    scriptname, *scriptname == '\0' ? "" : "/",
87661d06d6bSBaptiste Daroussin 	    usepath ? req->q.manpath : "", usepath ? "/" : "");
87761d06d6bSBaptiste Daroussin 
87861d06d6bSBaptiste Daroussin 	vp = html_alloc(&conf);
879*7295610fSBaptiste Daroussin 	if (meta->macroset == MACROSET_MDOC)
880*7295610fSBaptiste Daroussin 		html_mdoc(vp, meta);
881*7295610fSBaptiste Daroussin 	else
882*7295610fSBaptiste Daroussin 		html_man(vp, meta);
88361d06d6bSBaptiste Daroussin 
88461d06d6bSBaptiste Daroussin 	html_free(vp);
88561d06d6bSBaptiste Daroussin 	mparse_free(mp);
88661d06d6bSBaptiste Daroussin 	mchars_free();
88761d06d6bSBaptiste Daroussin 	free(conf.man);
88861d06d6bSBaptiste Daroussin 	free(conf.style);
88961d06d6bSBaptiste Daroussin }
89061d06d6bSBaptiste Daroussin 
89161d06d6bSBaptiste Daroussin static void
89261d06d6bSBaptiste Daroussin resp_show(const struct req *req, const char *file)
89361d06d6bSBaptiste Daroussin {
89461d06d6bSBaptiste Daroussin 
89561d06d6bSBaptiste Daroussin 	if ('.' == file[0] && '/' == file[1])
89661d06d6bSBaptiste Daroussin 		file += 2;
89761d06d6bSBaptiste Daroussin 
89861d06d6bSBaptiste Daroussin 	if ('c' == *file)
89961d06d6bSBaptiste Daroussin 		resp_catman(req, file);
90061d06d6bSBaptiste Daroussin 	else
90161d06d6bSBaptiste Daroussin 		resp_format(req, file);
90261d06d6bSBaptiste Daroussin }
90361d06d6bSBaptiste Daroussin 
90461d06d6bSBaptiste Daroussin static void
90561d06d6bSBaptiste Daroussin pg_show(struct req *req, const char *fullpath)
90661d06d6bSBaptiste Daroussin {
90761d06d6bSBaptiste Daroussin 	char		*manpath;
90861d06d6bSBaptiste Daroussin 	const char	*file;
90961d06d6bSBaptiste Daroussin 
91061d06d6bSBaptiste Daroussin 	if ((file = strchr(fullpath, '/')) == NULL) {
91161d06d6bSBaptiste Daroussin 		pg_error_badrequest(
91261d06d6bSBaptiste Daroussin 		    "You did not specify a page to show.");
91361d06d6bSBaptiste Daroussin 		return;
91461d06d6bSBaptiste Daroussin 	}
91561d06d6bSBaptiste Daroussin 	manpath = mandoc_strndup(fullpath, file - fullpath);
91661d06d6bSBaptiste Daroussin 	file++;
91761d06d6bSBaptiste Daroussin 
91861d06d6bSBaptiste Daroussin 	if ( ! validate_manpath(req, manpath)) {
91961d06d6bSBaptiste Daroussin 		pg_error_badrequest(
92061d06d6bSBaptiste Daroussin 		    "You specified an invalid manpath.");
92161d06d6bSBaptiste Daroussin 		free(manpath);
92261d06d6bSBaptiste Daroussin 		return;
92361d06d6bSBaptiste Daroussin 	}
92461d06d6bSBaptiste Daroussin 
92561d06d6bSBaptiste Daroussin 	/*
92661d06d6bSBaptiste Daroussin 	 * Begin by chdir()ing into the manpath.
92761d06d6bSBaptiste Daroussin 	 * This way we can pick up the database files, which are
92861d06d6bSBaptiste Daroussin 	 * relative to the manpath root.
92961d06d6bSBaptiste Daroussin 	 */
93061d06d6bSBaptiste Daroussin 
93161d06d6bSBaptiste Daroussin 	if (chdir(manpath) == -1) {
93261d06d6bSBaptiste Daroussin 		warn("chdir %s", manpath);
93361d06d6bSBaptiste Daroussin 		pg_error_internal();
93461d06d6bSBaptiste Daroussin 		free(manpath);
93561d06d6bSBaptiste Daroussin 		return;
93661d06d6bSBaptiste Daroussin 	}
93761d06d6bSBaptiste Daroussin 	free(manpath);
93861d06d6bSBaptiste Daroussin 
93961d06d6bSBaptiste Daroussin 	if ( ! validate_filename(file)) {
94061d06d6bSBaptiste Daroussin 		pg_error_badrequest(
94161d06d6bSBaptiste Daroussin 		    "You specified an invalid manual file.");
94261d06d6bSBaptiste Daroussin 		return;
94361d06d6bSBaptiste Daroussin 	}
94461d06d6bSBaptiste Daroussin 
94561d06d6bSBaptiste Daroussin 	resp_begin_html(200, NULL, file);
94661d06d6bSBaptiste Daroussin 	resp_searchform(req, FOCUS_NONE);
94761d06d6bSBaptiste Daroussin 	resp_show(req, file);
94861d06d6bSBaptiste Daroussin 	resp_end_html();
94961d06d6bSBaptiste Daroussin }
95061d06d6bSBaptiste Daroussin 
95161d06d6bSBaptiste Daroussin static void
95261d06d6bSBaptiste Daroussin pg_search(const struct req *req)
95361d06d6bSBaptiste Daroussin {
95461d06d6bSBaptiste Daroussin 	struct mansearch	  search;
95561d06d6bSBaptiste Daroussin 	struct manpaths		  paths;
95661d06d6bSBaptiste Daroussin 	struct manpage		 *res;
95761d06d6bSBaptiste Daroussin 	char			**argv;
95861d06d6bSBaptiste Daroussin 	char			 *query, *rp, *wp;
95961d06d6bSBaptiste Daroussin 	size_t			  ressz;
96061d06d6bSBaptiste Daroussin 	int			  argc;
96161d06d6bSBaptiste Daroussin 
96261d06d6bSBaptiste Daroussin 	/*
96361d06d6bSBaptiste Daroussin 	 * Begin by chdir()ing into the root of the manpath.
96461d06d6bSBaptiste Daroussin 	 * This way we can pick up the database files, which are
96561d06d6bSBaptiste Daroussin 	 * relative to the manpath root.
96661d06d6bSBaptiste Daroussin 	 */
96761d06d6bSBaptiste Daroussin 
96861d06d6bSBaptiste Daroussin 	if (chdir(req->q.manpath) == -1) {
96961d06d6bSBaptiste Daroussin 		warn("chdir %s", req->q.manpath);
97061d06d6bSBaptiste Daroussin 		pg_error_internal();
97161d06d6bSBaptiste Daroussin 		return;
97261d06d6bSBaptiste Daroussin 	}
97361d06d6bSBaptiste Daroussin 
97461d06d6bSBaptiste Daroussin 	search.arch = req->q.arch;
97561d06d6bSBaptiste Daroussin 	search.sec = req->q.sec;
97661d06d6bSBaptiste Daroussin 	search.outkey = "Nd";
97761d06d6bSBaptiste Daroussin 	search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR;
97861d06d6bSBaptiste Daroussin 	search.firstmatch = 1;
97961d06d6bSBaptiste Daroussin 
98061d06d6bSBaptiste Daroussin 	paths.sz = 1;
98161d06d6bSBaptiste Daroussin 	paths.paths = mandoc_malloc(sizeof(char *));
98261d06d6bSBaptiste Daroussin 	paths.paths[0] = mandoc_strdup(".");
98361d06d6bSBaptiste Daroussin 
98461d06d6bSBaptiste Daroussin 	/*
98561d06d6bSBaptiste Daroussin 	 * Break apart at spaces with backslash-escaping.
98661d06d6bSBaptiste Daroussin 	 */
98761d06d6bSBaptiste Daroussin 
98861d06d6bSBaptiste Daroussin 	argc = 0;
98961d06d6bSBaptiste Daroussin 	argv = NULL;
99061d06d6bSBaptiste Daroussin 	rp = query = mandoc_strdup(req->q.query);
99161d06d6bSBaptiste Daroussin 	for (;;) {
99261d06d6bSBaptiste Daroussin 		while (isspace((unsigned char)*rp))
99361d06d6bSBaptiste Daroussin 			rp++;
99461d06d6bSBaptiste Daroussin 		if (*rp == '\0')
99561d06d6bSBaptiste Daroussin 			break;
99661d06d6bSBaptiste Daroussin 		argv = mandoc_reallocarray(argv, argc + 1, sizeof(char *));
99761d06d6bSBaptiste Daroussin 		argv[argc++] = wp = rp;
99861d06d6bSBaptiste Daroussin 		for (;;) {
99961d06d6bSBaptiste Daroussin 			if (isspace((unsigned char)*rp)) {
100061d06d6bSBaptiste Daroussin 				*wp = '\0';
100161d06d6bSBaptiste Daroussin 				rp++;
100261d06d6bSBaptiste Daroussin 				break;
100361d06d6bSBaptiste Daroussin 			}
100461d06d6bSBaptiste Daroussin 			if (rp[0] == '\\' && rp[1] != '\0')
100561d06d6bSBaptiste Daroussin 				rp++;
100661d06d6bSBaptiste Daroussin 			if (wp != rp)
100761d06d6bSBaptiste Daroussin 				*wp = *rp;
100861d06d6bSBaptiste Daroussin 			if (*rp == '\0')
100961d06d6bSBaptiste Daroussin 				break;
101061d06d6bSBaptiste Daroussin 			wp++;
101161d06d6bSBaptiste Daroussin 			rp++;
101261d06d6bSBaptiste Daroussin 		}
101361d06d6bSBaptiste Daroussin 	}
101461d06d6bSBaptiste Daroussin 
101561d06d6bSBaptiste Daroussin 	res = NULL;
101661d06d6bSBaptiste Daroussin 	ressz = 0;
101761d06d6bSBaptiste Daroussin 	if (req->isquery && req->q.equal && argc == 1)
101861d06d6bSBaptiste Daroussin 		pg_redirect(req, argv[0]);
101961d06d6bSBaptiste Daroussin 	else if (mansearch(&search, &paths, argc, argv, &res, &ressz) == 0)
102061d06d6bSBaptiste Daroussin 		pg_noresult(req, "You entered an invalid query.");
102161d06d6bSBaptiste Daroussin 	else if (ressz == 0)
102261d06d6bSBaptiste Daroussin 		pg_noresult(req, "No results found.");
102361d06d6bSBaptiste Daroussin 	else
102461d06d6bSBaptiste Daroussin 		pg_searchres(req, res, ressz);
102561d06d6bSBaptiste Daroussin 
102661d06d6bSBaptiste Daroussin 	free(query);
102761d06d6bSBaptiste Daroussin 	mansearch_free(res, ressz);
102861d06d6bSBaptiste Daroussin 	free(paths.paths[0]);
102961d06d6bSBaptiste Daroussin 	free(paths.paths);
103061d06d6bSBaptiste Daroussin }
103161d06d6bSBaptiste Daroussin 
103261d06d6bSBaptiste Daroussin int
103361d06d6bSBaptiste Daroussin main(void)
103461d06d6bSBaptiste Daroussin {
103561d06d6bSBaptiste Daroussin 	struct req	 req;
103661d06d6bSBaptiste Daroussin 	struct itimerval itimer;
103761d06d6bSBaptiste Daroussin 	const char	*path;
103861d06d6bSBaptiste Daroussin 	const char	*querystring;
103961d06d6bSBaptiste Daroussin 	int		 i;
104061d06d6bSBaptiste Daroussin 
104161d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
104261d06d6bSBaptiste Daroussin 	/*
104361d06d6bSBaptiste Daroussin 	 * The "rpath" pledge could be revoked after mparse_readfd()
104461d06d6bSBaptiste Daroussin 	 * if the file desciptor to "/footer.html" would be opened
104561d06d6bSBaptiste Daroussin 	 * up front, but it's probably not worth the complication
104661d06d6bSBaptiste Daroussin 	 * of the code it would cause: it would require scattering
104761d06d6bSBaptiste Daroussin 	 * pledge() calls in multiple low-level resp_*() functions.
104861d06d6bSBaptiste Daroussin 	 */
104961d06d6bSBaptiste Daroussin 
105061d06d6bSBaptiste Daroussin 	if (pledge("stdio rpath", NULL) == -1) {
105161d06d6bSBaptiste Daroussin 		warn("pledge");
105261d06d6bSBaptiste Daroussin 		pg_error_internal();
105361d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
105461d06d6bSBaptiste Daroussin 	}
105561d06d6bSBaptiste Daroussin #endif
105661d06d6bSBaptiste Daroussin 
105761d06d6bSBaptiste Daroussin 	/* Poor man's ReDoS mitigation. */
105861d06d6bSBaptiste Daroussin 
105961d06d6bSBaptiste Daroussin 	itimer.it_value.tv_sec = 2;
106061d06d6bSBaptiste Daroussin 	itimer.it_value.tv_usec = 0;
106161d06d6bSBaptiste Daroussin 	itimer.it_interval.tv_sec = 2;
106261d06d6bSBaptiste Daroussin 	itimer.it_interval.tv_usec = 0;
106361d06d6bSBaptiste Daroussin 	if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) {
106461d06d6bSBaptiste Daroussin 		warn("setitimer");
106561d06d6bSBaptiste Daroussin 		pg_error_internal();
106661d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
106761d06d6bSBaptiste Daroussin 	}
106861d06d6bSBaptiste Daroussin 
106961d06d6bSBaptiste Daroussin 	/*
107061d06d6bSBaptiste Daroussin 	 * First we change directory into the MAN_DIR so that
107161d06d6bSBaptiste Daroussin 	 * subsequent scanning for manpath directories is rooted
107261d06d6bSBaptiste Daroussin 	 * relative to the same position.
107361d06d6bSBaptiste Daroussin 	 */
107461d06d6bSBaptiste Daroussin 
107561d06d6bSBaptiste Daroussin 	if (chdir(MAN_DIR) == -1) {
107661d06d6bSBaptiste Daroussin 		warn("MAN_DIR: %s", MAN_DIR);
107761d06d6bSBaptiste Daroussin 		pg_error_internal();
107861d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
107961d06d6bSBaptiste Daroussin 	}
108061d06d6bSBaptiste Daroussin 
108161d06d6bSBaptiste Daroussin 	memset(&req, 0, sizeof(struct req));
108261d06d6bSBaptiste Daroussin 	req.q.equal = 1;
108361d06d6bSBaptiste Daroussin 	parse_manpath_conf(&req);
108461d06d6bSBaptiste Daroussin 
108561d06d6bSBaptiste Daroussin 	/* Parse the path info and the query string. */
108661d06d6bSBaptiste Daroussin 
108761d06d6bSBaptiste Daroussin 	if ((path = getenv("PATH_INFO")) == NULL)
108861d06d6bSBaptiste Daroussin 		path = "";
108961d06d6bSBaptiste Daroussin 	else if (*path == '/')
109061d06d6bSBaptiste Daroussin 		path++;
109161d06d6bSBaptiste Daroussin 
109261d06d6bSBaptiste Daroussin 	if (*path != '\0') {
109361d06d6bSBaptiste Daroussin 		parse_path_info(&req, path);
109461d06d6bSBaptiste Daroussin 		if (req.q.manpath == NULL || req.q.sec == NULL ||
109561d06d6bSBaptiste Daroussin 		    *req.q.query == '\0' || access(path, F_OK) == -1)
109661d06d6bSBaptiste Daroussin 			path = "";
109761d06d6bSBaptiste Daroussin 	} else if ((querystring = getenv("QUERY_STRING")) != NULL)
109861d06d6bSBaptiste Daroussin 		parse_query_string(&req, querystring);
109961d06d6bSBaptiste Daroussin 
110061d06d6bSBaptiste Daroussin 	/* Validate parsed data and add defaults. */
110161d06d6bSBaptiste Daroussin 
110261d06d6bSBaptiste Daroussin 	if (req.q.manpath == NULL)
110361d06d6bSBaptiste Daroussin 		req.q.manpath = mandoc_strdup(req.p[0]);
110461d06d6bSBaptiste Daroussin 	else if ( ! validate_manpath(&req, req.q.manpath)) {
110561d06d6bSBaptiste Daroussin 		pg_error_badrequest(
110661d06d6bSBaptiste Daroussin 		    "You specified an invalid manpath.");
110761d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
110861d06d6bSBaptiste Daroussin 	}
110961d06d6bSBaptiste Daroussin 
1110*7295610fSBaptiste Daroussin 	if (req.q.arch != NULL && validate_arch(req.q.arch) == 0) {
111161d06d6bSBaptiste Daroussin 		pg_error_badrequest(
111261d06d6bSBaptiste Daroussin 		    "You specified an invalid architecture.");
111361d06d6bSBaptiste Daroussin 		return EXIT_FAILURE;
111461d06d6bSBaptiste Daroussin 	}
111561d06d6bSBaptiste Daroussin 
111661d06d6bSBaptiste Daroussin 	/* Dispatch to the three different pages. */
111761d06d6bSBaptiste Daroussin 
111861d06d6bSBaptiste Daroussin 	if ('\0' != *path)
111961d06d6bSBaptiste Daroussin 		pg_show(&req, path);
112061d06d6bSBaptiste Daroussin 	else if (NULL != req.q.query)
112161d06d6bSBaptiste Daroussin 		pg_search(&req);
112261d06d6bSBaptiste Daroussin 	else
112361d06d6bSBaptiste Daroussin 		pg_index(&req);
112461d06d6bSBaptiste Daroussin 
112561d06d6bSBaptiste Daroussin 	free(req.q.manpath);
112661d06d6bSBaptiste Daroussin 	free(req.q.arch);
112761d06d6bSBaptiste Daroussin 	free(req.q.sec);
112861d06d6bSBaptiste Daroussin 	free(req.q.query);
112961d06d6bSBaptiste Daroussin 	for (i = 0; i < (int)req.psz; i++)
113061d06d6bSBaptiste Daroussin 		free(req.p[i]);
113161d06d6bSBaptiste Daroussin 	free(req.p);
113261d06d6bSBaptiste Daroussin 	return EXIT_SUCCESS;
113361d06d6bSBaptiste Daroussin }
113461d06d6bSBaptiste Daroussin 
113561d06d6bSBaptiste Daroussin /*
1136*7295610fSBaptiste Daroussin  * Translate PATH_INFO to a query.
113761d06d6bSBaptiste Daroussin  */
113861d06d6bSBaptiste Daroussin static void
113961d06d6bSBaptiste Daroussin parse_path_info(struct req *req, const char *path)
114061d06d6bSBaptiste Daroussin {
1141*7295610fSBaptiste Daroussin 	const char	*name, *sec, *end;
114261d06d6bSBaptiste Daroussin 
114361d06d6bSBaptiste Daroussin 	req->isquery = 0;
114461d06d6bSBaptiste Daroussin 	req->q.equal = 1;
1145*7295610fSBaptiste Daroussin 	req->q.manpath = NULL;
114661d06d6bSBaptiste Daroussin 	req->q.arch = NULL;
114761d06d6bSBaptiste Daroussin 
114861d06d6bSBaptiste Daroussin 	/* Mandatory manual page name. */
1149*7295610fSBaptiste Daroussin 	if ((name = strrchr(path, '/')) == NULL)
1150*7295610fSBaptiste Daroussin 		name = path;
1151*7295610fSBaptiste Daroussin 	else
1152*7295610fSBaptiste Daroussin 		name++;
115361d06d6bSBaptiste Daroussin 
115461d06d6bSBaptiste Daroussin 	/* Optional trailing section. */
1155*7295610fSBaptiste Daroussin 	sec = strrchr(name, '.');
1156*7295610fSBaptiste Daroussin 	if (sec != NULL && isdigit((unsigned char)*++sec)) {
1157*7295610fSBaptiste Daroussin 		req->q.query = mandoc_strndup(name, sec - name - 1);
1158*7295610fSBaptiste Daroussin 		req->q.sec = mandoc_strdup(sec);
1159*7295610fSBaptiste Daroussin 	} else {
1160*7295610fSBaptiste Daroussin 		req->q.query = mandoc_strdup(name);
116161d06d6bSBaptiste Daroussin 		req->q.sec = NULL;
116261d06d6bSBaptiste Daroussin 	}
116361d06d6bSBaptiste Daroussin 
116461d06d6bSBaptiste Daroussin 	/* Handle the case of name[.section] only. */
1165*7295610fSBaptiste Daroussin 	if (name == path)
116661d06d6bSBaptiste Daroussin 		return;
116761d06d6bSBaptiste Daroussin 
1168*7295610fSBaptiste Daroussin 	/* Optional manpath. */
1169*7295610fSBaptiste Daroussin 	end = strchr(path, '/');
1170*7295610fSBaptiste Daroussin 	req->q.manpath = mandoc_strndup(path, end - path);
1171*7295610fSBaptiste Daroussin 	if (validate_manpath(req, req->q.manpath)) {
1172*7295610fSBaptiste Daroussin 		path = end + 1;
1173*7295610fSBaptiste Daroussin 		if (name == path)
1174*7295610fSBaptiste Daroussin 			return;
1175*7295610fSBaptiste Daroussin 	} else {
1176*7295610fSBaptiste Daroussin 		free(req->q.manpath);
1177*7295610fSBaptiste Daroussin 		req->q.manpath = NULL;
1178*7295610fSBaptiste Daroussin 	}
1179*7295610fSBaptiste Daroussin 
1180*7295610fSBaptiste Daroussin 	/* Optional section. */
1181*7295610fSBaptiste Daroussin 	if (strncmp(path, "man", 3) == 0 || strncmp(path, "cat", 3) == 0) {
1182*7295610fSBaptiste Daroussin 		path += 3;
1183*7295610fSBaptiste Daroussin 		end = strchr(path, '/');
1184*7295610fSBaptiste Daroussin 		free(req->q.sec);
1185*7295610fSBaptiste Daroussin 		req->q.sec = mandoc_strndup(path, end - path);
1186*7295610fSBaptiste Daroussin 		path = end + 1;
1187*7295610fSBaptiste Daroussin 		if (name == path)
1188*7295610fSBaptiste Daroussin 			return;
1189*7295610fSBaptiste Daroussin 	}
1190*7295610fSBaptiste Daroussin 
1191*7295610fSBaptiste Daroussin 	/* Optional architecture. */
1192*7295610fSBaptiste Daroussin 	end = strchr(path, '/');
1193*7295610fSBaptiste Daroussin 	if (end + 1 != name) {
119461d06d6bSBaptiste Daroussin 		pg_error_badrequest(
119561d06d6bSBaptiste Daroussin 		    "You specified too many directory components.");
119661d06d6bSBaptiste Daroussin 		exit(EXIT_FAILURE);
119761d06d6bSBaptiste Daroussin 	}
1198*7295610fSBaptiste Daroussin 	req->q.arch = mandoc_strndup(path, end - path);
1199*7295610fSBaptiste Daroussin 	if (validate_arch(req->q.arch) == 0) {
120061d06d6bSBaptiste Daroussin 		pg_error_badrequest(
120161d06d6bSBaptiste Daroussin 		    "You specified an invalid directory component.");
120261d06d6bSBaptiste Daroussin 		exit(EXIT_FAILURE);
120361d06d6bSBaptiste Daroussin 	}
120461d06d6bSBaptiste Daroussin }
120561d06d6bSBaptiste Daroussin 
120661d06d6bSBaptiste Daroussin /*
120761d06d6bSBaptiste Daroussin  * Scan for indexable paths.
120861d06d6bSBaptiste Daroussin  */
120961d06d6bSBaptiste Daroussin static void
121061d06d6bSBaptiste Daroussin parse_manpath_conf(struct req *req)
121161d06d6bSBaptiste Daroussin {
121261d06d6bSBaptiste Daroussin 	FILE	*fp;
121361d06d6bSBaptiste Daroussin 	char	*dp;
121461d06d6bSBaptiste Daroussin 	size_t	 dpsz;
121561d06d6bSBaptiste Daroussin 	ssize_t	 len;
121661d06d6bSBaptiste Daroussin 
121761d06d6bSBaptiste Daroussin 	if ((fp = fopen("manpath.conf", "r")) == NULL) {
121861d06d6bSBaptiste Daroussin 		warn("%s/manpath.conf", MAN_DIR);
121961d06d6bSBaptiste Daroussin 		pg_error_internal();
122061d06d6bSBaptiste Daroussin 		exit(EXIT_FAILURE);
122161d06d6bSBaptiste Daroussin 	}
122261d06d6bSBaptiste Daroussin 
122361d06d6bSBaptiste Daroussin 	dp = NULL;
122461d06d6bSBaptiste Daroussin 	dpsz = 0;
122561d06d6bSBaptiste Daroussin 
122661d06d6bSBaptiste Daroussin 	while ((len = getline(&dp, &dpsz, fp)) != -1) {
122761d06d6bSBaptiste Daroussin 		if (dp[len - 1] == '\n')
122861d06d6bSBaptiste Daroussin 			dp[--len] = '\0';
122961d06d6bSBaptiste Daroussin 		req->p = mandoc_realloc(req->p,
123061d06d6bSBaptiste Daroussin 		    (req->psz + 1) * sizeof(char *));
123161d06d6bSBaptiste Daroussin 		if ( ! validate_urifrag(dp)) {
123261d06d6bSBaptiste Daroussin 			warnx("%s/manpath.conf contains "
123361d06d6bSBaptiste Daroussin 			    "unsafe path \"%s\"", MAN_DIR, dp);
123461d06d6bSBaptiste Daroussin 			pg_error_internal();
123561d06d6bSBaptiste Daroussin 			exit(EXIT_FAILURE);
123661d06d6bSBaptiste Daroussin 		}
123761d06d6bSBaptiste Daroussin 		if (strchr(dp, '/') != NULL) {
123861d06d6bSBaptiste Daroussin 			warnx("%s/manpath.conf contains "
123961d06d6bSBaptiste Daroussin 			    "path with slash \"%s\"", MAN_DIR, dp);
124061d06d6bSBaptiste Daroussin 			pg_error_internal();
124161d06d6bSBaptiste Daroussin 			exit(EXIT_FAILURE);
124261d06d6bSBaptiste Daroussin 		}
124361d06d6bSBaptiste Daroussin 		req->p[req->psz++] = dp;
124461d06d6bSBaptiste Daroussin 		dp = NULL;
124561d06d6bSBaptiste Daroussin 		dpsz = 0;
124661d06d6bSBaptiste Daroussin 	}
124761d06d6bSBaptiste Daroussin 	free(dp);
124861d06d6bSBaptiste Daroussin 
124961d06d6bSBaptiste Daroussin 	if (req->p == NULL) {
125061d06d6bSBaptiste Daroussin 		warnx("%s/manpath.conf is empty", MAN_DIR);
125161d06d6bSBaptiste Daroussin 		pg_error_internal();
125261d06d6bSBaptiste Daroussin 		exit(EXIT_FAILURE);
125361d06d6bSBaptiste Daroussin 	}
125461d06d6bSBaptiste Daroussin }
1255