1*6d38604fSBaptiste Daroussin /* $Id: cgi.c,v 1.175 2021/08/19 15:23:36 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*6d38604fSBaptiste Daroussin * Copyright (c) 2014-2019, 2021 Ingo Schwarze <schwarze@usta.de>
461d06d6bSBaptiste Daroussin * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
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.
17*6d38604fSBaptiste Daroussin *
18*6d38604fSBaptiste Daroussin * Implementation of the man.cgi(8) program.
1961d06d6bSBaptiste Daroussin */
2061d06d6bSBaptiste Daroussin #include "config.h"
2161d06d6bSBaptiste Daroussin
2261d06d6bSBaptiste Daroussin #include <sys/types.h>
2361d06d6bSBaptiste Daroussin #include <sys/time.h>
2461d06d6bSBaptiste Daroussin
2561d06d6bSBaptiste Daroussin #include <ctype.h>
2661d06d6bSBaptiste Daroussin #if HAVE_ERR
2761d06d6bSBaptiste Daroussin #include <err.h>
2861d06d6bSBaptiste Daroussin #endif
2961d06d6bSBaptiste Daroussin #include <errno.h>
3061d06d6bSBaptiste Daroussin #include <fcntl.h>
3161d06d6bSBaptiste Daroussin #include <limits.h>
3261d06d6bSBaptiste Daroussin #include <stdint.h>
3361d06d6bSBaptiste Daroussin #include <stdio.h>
3461d06d6bSBaptiste Daroussin #include <stdlib.h>
3561d06d6bSBaptiste Daroussin #include <string.h>
3661d06d6bSBaptiste Daroussin #include <unistd.h>
3761d06d6bSBaptiste Daroussin
3861d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
3961d06d6bSBaptiste Daroussin #include "mandoc.h"
4061d06d6bSBaptiste Daroussin #include "roff.h"
4161d06d6bSBaptiste Daroussin #include "mdoc.h"
4261d06d6bSBaptiste Daroussin #include "man.h"
437295610fSBaptiste Daroussin #include "mandoc_parse.h"
4461d06d6bSBaptiste Daroussin #include "main.h"
4561d06d6bSBaptiste Daroussin #include "manconf.h"
4661d06d6bSBaptiste Daroussin #include "mansearch.h"
4761d06d6bSBaptiste Daroussin #include "cgi.h"
4861d06d6bSBaptiste Daroussin
4961d06d6bSBaptiste Daroussin /*
5061d06d6bSBaptiste Daroussin * A query as passed to the search function.
5161d06d6bSBaptiste Daroussin */
5261d06d6bSBaptiste Daroussin struct query {
5361d06d6bSBaptiste Daroussin char *manpath; /* desired manual directory */
5461d06d6bSBaptiste Daroussin char *arch; /* architecture */
5561d06d6bSBaptiste Daroussin char *sec; /* manual section */
5661d06d6bSBaptiste Daroussin char *query; /* unparsed query expression */
5761d06d6bSBaptiste Daroussin int equal; /* match whole names, not substrings */
5861d06d6bSBaptiste Daroussin };
5961d06d6bSBaptiste Daroussin
6061d06d6bSBaptiste Daroussin struct req {
6161d06d6bSBaptiste Daroussin struct query q;
6261d06d6bSBaptiste Daroussin char **p; /* array of available manpaths */
6361d06d6bSBaptiste Daroussin size_t psz; /* number of available manpaths */
6461d06d6bSBaptiste Daroussin int isquery; /* QUERY_STRING used, not PATH_INFO */
6561d06d6bSBaptiste Daroussin };
6661d06d6bSBaptiste Daroussin
6761d06d6bSBaptiste Daroussin enum focus {
6861d06d6bSBaptiste Daroussin FOCUS_NONE = 0,
6961d06d6bSBaptiste Daroussin FOCUS_QUERY
7061d06d6bSBaptiste Daroussin };
7161d06d6bSBaptiste Daroussin
7261d06d6bSBaptiste Daroussin static void html_print(const char *);
7361d06d6bSBaptiste Daroussin static void html_putchar(char);
7461d06d6bSBaptiste Daroussin static int http_decode(char *);
75*6d38604fSBaptiste Daroussin static void http_encode(const char *);
7661d06d6bSBaptiste Daroussin static void parse_manpath_conf(struct req *);
77*6d38604fSBaptiste Daroussin static void parse_path_info(struct req *, const char *);
7861d06d6bSBaptiste Daroussin static void parse_query_string(struct req *, const char *);
7961d06d6bSBaptiste Daroussin static void pg_error_badrequest(const char *);
8061d06d6bSBaptiste Daroussin static void pg_error_internal(void);
8161d06d6bSBaptiste Daroussin static void pg_index(const struct req *);
82*6d38604fSBaptiste Daroussin static void pg_noresult(const struct req *, int, const char *,
83*6d38604fSBaptiste Daroussin const char *);
8461d06d6bSBaptiste Daroussin static void pg_redirect(const struct req *, const char *);
8561d06d6bSBaptiste Daroussin static void pg_search(const struct req *);
8661d06d6bSBaptiste Daroussin static void pg_searchres(const struct req *,
8761d06d6bSBaptiste Daroussin struct manpage *, size_t);
8861d06d6bSBaptiste Daroussin static void pg_show(struct req *, const char *);
8961d06d6bSBaptiste Daroussin static void resp_begin_html(int, const char *, const char *);
9061d06d6bSBaptiste Daroussin static void resp_begin_http(int, const char *);
9161d06d6bSBaptiste Daroussin static void resp_catman(const struct req *, const char *);
9261d06d6bSBaptiste Daroussin static void resp_copy(const char *);
9361d06d6bSBaptiste Daroussin static void resp_end_html(void);
9461d06d6bSBaptiste Daroussin static void resp_format(const struct req *, const char *);
9561d06d6bSBaptiste Daroussin static void resp_searchform(const struct req *, enum focus);
9661d06d6bSBaptiste Daroussin static void resp_show(const struct req *, const char *);
9761d06d6bSBaptiste Daroussin static void set_query_attr(char **, char **);
987295610fSBaptiste Daroussin static int validate_arch(const char *);
9961d06d6bSBaptiste Daroussin static int validate_filename(const char *);
10061d06d6bSBaptiste Daroussin static int validate_manpath(const struct req *, const char *);
10161d06d6bSBaptiste Daroussin static int validate_urifrag(const char *);
10261d06d6bSBaptiste Daroussin
10361d06d6bSBaptiste Daroussin static const char *scriptname = SCRIPT_NAME;
10461d06d6bSBaptiste Daroussin
10561d06d6bSBaptiste Daroussin static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
10661d06d6bSBaptiste Daroussin static const char *const sec_numbers[] = {
10761d06d6bSBaptiste Daroussin "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
10861d06d6bSBaptiste Daroussin };
10961d06d6bSBaptiste Daroussin static const char *const sec_names[] = {
11061d06d6bSBaptiste Daroussin "All Sections",
11161d06d6bSBaptiste Daroussin "1 - General Commands",
11261d06d6bSBaptiste Daroussin "2 - System Calls",
11361d06d6bSBaptiste Daroussin "3 - Library Functions",
11461d06d6bSBaptiste Daroussin "3p - Perl Library",
11561d06d6bSBaptiste Daroussin "4 - Device Drivers",
11661d06d6bSBaptiste Daroussin "5 - File Formats",
11761d06d6bSBaptiste Daroussin "6 - Games",
11861d06d6bSBaptiste Daroussin "7 - Miscellaneous Information",
11961d06d6bSBaptiste Daroussin "8 - System Manager\'s Manual",
12061d06d6bSBaptiste Daroussin "9 - Kernel Developer\'s Manual"
12161d06d6bSBaptiste Daroussin };
12261d06d6bSBaptiste Daroussin static const int sec_MAX = sizeof(sec_names) / sizeof(char *);
12361d06d6bSBaptiste Daroussin
12461d06d6bSBaptiste Daroussin static const char *const arch_names[] = {
12561d06d6bSBaptiste Daroussin "amd64", "alpha", "armv7", "arm64",
126*6d38604fSBaptiste Daroussin "hppa", "i386", "landisk", "loongson",
127*6d38604fSBaptiste Daroussin "luna88k", "macppc", "mips64", "octeon",
128*6d38604fSBaptiste Daroussin "powerpc64", "riscv64", "sparc64",
129*6d38604fSBaptiste Daroussin
13061d06d6bSBaptiste Daroussin "amiga", "arc", "armish", "arm32",
13161d06d6bSBaptiste Daroussin "atari", "aviion", "beagle", "cats",
13261d06d6bSBaptiste Daroussin "hppa64", "hp300",
13361d06d6bSBaptiste Daroussin "ia64", "mac68k", "mvme68k", "mvme88k",
13461d06d6bSBaptiste Daroussin "mvmeppc", "palm", "pc532", "pegasos",
135*6d38604fSBaptiste Daroussin "pmax", "powerpc", "sgi", "socppc",
136*6d38604fSBaptiste Daroussin "solbourne", "sparc",
13761d06d6bSBaptiste Daroussin "sun3", "vax", "wgrisc", "x68k",
13861d06d6bSBaptiste Daroussin "zaurus"
13961d06d6bSBaptiste Daroussin };
14061d06d6bSBaptiste Daroussin static const int arch_MAX = sizeof(arch_names) / sizeof(char *);
14161d06d6bSBaptiste Daroussin
14261d06d6bSBaptiste Daroussin /*
14361d06d6bSBaptiste Daroussin * Print a character, escaping HTML along the way.
14461d06d6bSBaptiste Daroussin * This will pass non-ASCII straight to output: be warned!
14561d06d6bSBaptiste Daroussin */
14661d06d6bSBaptiste Daroussin static void
html_putchar(char c)14761d06d6bSBaptiste Daroussin html_putchar(char c)
14861d06d6bSBaptiste Daroussin {
14961d06d6bSBaptiste Daroussin
15061d06d6bSBaptiste Daroussin switch (c) {
15161d06d6bSBaptiste Daroussin case '"':
15261d06d6bSBaptiste Daroussin printf(""");
15361d06d6bSBaptiste Daroussin break;
15461d06d6bSBaptiste Daroussin case '&':
15561d06d6bSBaptiste Daroussin printf("&");
15661d06d6bSBaptiste Daroussin break;
15761d06d6bSBaptiste Daroussin case '>':
15861d06d6bSBaptiste Daroussin printf(">");
15961d06d6bSBaptiste Daroussin break;
16061d06d6bSBaptiste Daroussin case '<':
16161d06d6bSBaptiste Daroussin printf("<");
16261d06d6bSBaptiste Daroussin break;
16361d06d6bSBaptiste Daroussin default:
16461d06d6bSBaptiste Daroussin putchar((unsigned char)c);
16561d06d6bSBaptiste Daroussin break;
16661d06d6bSBaptiste Daroussin }
16761d06d6bSBaptiste Daroussin }
16861d06d6bSBaptiste Daroussin
16961d06d6bSBaptiste Daroussin /*
17061d06d6bSBaptiste Daroussin * Call through to html_putchar().
17161d06d6bSBaptiste Daroussin * Accepts NULL strings.
17261d06d6bSBaptiste Daroussin */
17361d06d6bSBaptiste Daroussin static void
html_print(const char * p)17461d06d6bSBaptiste Daroussin html_print(const char *p)
17561d06d6bSBaptiste Daroussin {
17661d06d6bSBaptiste Daroussin
17761d06d6bSBaptiste Daroussin if (NULL == p)
17861d06d6bSBaptiste Daroussin return;
17961d06d6bSBaptiste Daroussin while ('\0' != *p)
18061d06d6bSBaptiste Daroussin html_putchar(*p++);
18161d06d6bSBaptiste Daroussin }
18261d06d6bSBaptiste Daroussin
18361d06d6bSBaptiste Daroussin /*
18461d06d6bSBaptiste Daroussin * Transfer the responsibility for the allocated string *val
18561d06d6bSBaptiste Daroussin * to the query structure.
18661d06d6bSBaptiste Daroussin */
18761d06d6bSBaptiste Daroussin static void
set_query_attr(char ** attr,char ** val)18861d06d6bSBaptiste Daroussin set_query_attr(char **attr, char **val)
18961d06d6bSBaptiste Daroussin {
19061d06d6bSBaptiste Daroussin
19161d06d6bSBaptiste Daroussin free(*attr);
19261d06d6bSBaptiste Daroussin if (**val == '\0') {
19361d06d6bSBaptiste Daroussin *attr = NULL;
19461d06d6bSBaptiste Daroussin free(*val);
19561d06d6bSBaptiste Daroussin } else
19661d06d6bSBaptiste Daroussin *attr = *val;
19761d06d6bSBaptiste Daroussin *val = NULL;
19861d06d6bSBaptiste Daroussin }
19961d06d6bSBaptiste Daroussin
20061d06d6bSBaptiste Daroussin /*
20161d06d6bSBaptiste Daroussin * Parse the QUERY_STRING for key-value pairs
20261d06d6bSBaptiste Daroussin * and store the values into the query structure.
20361d06d6bSBaptiste Daroussin */
20461d06d6bSBaptiste Daroussin static void
parse_query_string(struct req * req,const char * qs)20561d06d6bSBaptiste Daroussin parse_query_string(struct req *req, const char *qs)
20661d06d6bSBaptiste Daroussin {
20761d06d6bSBaptiste Daroussin char *key, *val;
20861d06d6bSBaptiste Daroussin size_t keysz, valsz;
20961d06d6bSBaptiste Daroussin
21061d06d6bSBaptiste Daroussin req->isquery = 1;
21161d06d6bSBaptiste Daroussin req->q.manpath = NULL;
21261d06d6bSBaptiste Daroussin req->q.arch = NULL;
21361d06d6bSBaptiste Daroussin req->q.sec = NULL;
21461d06d6bSBaptiste Daroussin req->q.query = NULL;
21561d06d6bSBaptiste Daroussin req->q.equal = 1;
21661d06d6bSBaptiste Daroussin
21761d06d6bSBaptiste Daroussin key = val = NULL;
21861d06d6bSBaptiste Daroussin while (*qs != '\0') {
21961d06d6bSBaptiste Daroussin
22061d06d6bSBaptiste Daroussin /* Parse one key. */
22161d06d6bSBaptiste Daroussin
22261d06d6bSBaptiste Daroussin keysz = strcspn(qs, "=;&");
22361d06d6bSBaptiste Daroussin key = mandoc_strndup(qs, keysz);
22461d06d6bSBaptiste Daroussin qs += keysz;
22561d06d6bSBaptiste Daroussin if (*qs != '=')
22661d06d6bSBaptiste Daroussin goto next;
22761d06d6bSBaptiste Daroussin
22861d06d6bSBaptiste Daroussin /* Parse one value. */
22961d06d6bSBaptiste Daroussin
23061d06d6bSBaptiste Daroussin valsz = strcspn(++qs, ";&");
23161d06d6bSBaptiste Daroussin val = mandoc_strndup(qs, valsz);
23261d06d6bSBaptiste Daroussin qs += valsz;
23361d06d6bSBaptiste Daroussin
23461d06d6bSBaptiste Daroussin /* Decode and catch encoding errors. */
23561d06d6bSBaptiste Daroussin
23661d06d6bSBaptiste Daroussin if ( ! (http_decode(key) && http_decode(val)))
23761d06d6bSBaptiste Daroussin goto next;
23861d06d6bSBaptiste Daroussin
23961d06d6bSBaptiste Daroussin /* Handle key-value pairs. */
24061d06d6bSBaptiste Daroussin
24161d06d6bSBaptiste Daroussin if ( ! strcmp(key, "query"))
24261d06d6bSBaptiste Daroussin set_query_attr(&req->q.query, &val);
24361d06d6bSBaptiste Daroussin
24461d06d6bSBaptiste Daroussin else if ( ! strcmp(key, "apropos"))
24561d06d6bSBaptiste Daroussin req->q.equal = !strcmp(val, "0");
24661d06d6bSBaptiste Daroussin
24761d06d6bSBaptiste Daroussin else if ( ! strcmp(key, "manpath")) {
24861d06d6bSBaptiste Daroussin #ifdef COMPAT_OLDURI
24961d06d6bSBaptiste Daroussin if ( ! strncmp(val, "OpenBSD ", 8)) {
25061d06d6bSBaptiste Daroussin val[7] = '-';
25161d06d6bSBaptiste Daroussin if ('C' == val[8])
25261d06d6bSBaptiste Daroussin val[8] = 'c';
25361d06d6bSBaptiste Daroussin }
25461d06d6bSBaptiste Daroussin #endif
25561d06d6bSBaptiste Daroussin set_query_attr(&req->q.manpath, &val);
25661d06d6bSBaptiste Daroussin }
25761d06d6bSBaptiste Daroussin
25861d06d6bSBaptiste Daroussin else if ( ! (strcmp(key, "sec")
25961d06d6bSBaptiste Daroussin #ifdef COMPAT_OLDURI
26061d06d6bSBaptiste Daroussin && strcmp(key, "sektion")
26161d06d6bSBaptiste Daroussin #endif
26261d06d6bSBaptiste Daroussin )) {
26361d06d6bSBaptiste Daroussin if ( ! strcmp(val, "0"))
26461d06d6bSBaptiste Daroussin *val = '\0';
26561d06d6bSBaptiste Daroussin set_query_attr(&req->q.sec, &val);
26661d06d6bSBaptiste Daroussin }
26761d06d6bSBaptiste Daroussin
26861d06d6bSBaptiste Daroussin else if ( ! strcmp(key, "arch")) {
26961d06d6bSBaptiste Daroussin if ( ! strcmp(val, "default"))
27061d06d6bSBaptiste Daroussin *val = '\0';
27161d06d6bSBaptiste Daroussin set_query_attr(&req->q.arch, &val);
27261d06d6bSBaptiste Daroussin }
27361d06d6bSBaptiste Daroussin
27461d06d6bSBaptiste Daroussin /*
27561d06d6bSBaptiste Daroussin * The key must be freed in any case.
27661d06d6bSBaptiste Daroussin * The val may have been handed over to the query
27761d06d6bSBaptiste Daroussin * structure, in which case it is now NULL.
27861d06d6bSBaptiste Daroussin */
27961d06d6bSBaptiste Daroussin next:
28061d06d6bSBaptiste Daroussin free(key);
28161d06d6bSBaptiste Daroussin key = NULL;
28261d06d6bSBaptiste Daroussin free(val);
28361d06d6bSBaptiste Daroussin val = NULL;
28461d06d6bSBaptiste Daroussin
28561d06d6bSBaptiste Daroussin if (*qs != '\0')
28661d06d6bSBaptiste Daroussin qs++;
28761d06d6bSBaptiste Daroussin }
28861d06d6bSBaptiste Daroussin }
28961d06d6bSBaptiste Daroussin
29061d06d6bSBaptiste Daroussin /*
29161d06d6bSBaptiste Daroussin * HTTP-decode a string. The standard explanation is that this turns
29261d06d6bSBaptiste Daroussin * "%4e+foo" into "n foo" in the regular way. This is done in-place
29361d06d6bSBaptiste Daroussin * over the allocated string.
29461d06d6bSBaptiste Daroussin */
29561d06d6bSBaptiste Daroussin static int
http_decode(char * p)29661d06d6bSBaptiste Daroussin http_decode(char *p)
29761d06d6bSBaptiste Daroussin {
29861d06d6bSBaptiste Daroussin char hex[3];
29961d06d6bSBaptiste Daroussin char *q;
30061d06d6bSBaptiste Daroussin int c;
30161d06d6bSBaptiste Daroussin
30261d06d6bSBaptiste Daroussin hex[2] = '\0';
30361d06d6bSBaptiste Daroussin
30461d06d6bSBaptiste Daroussin q = p;
30561d06d6bSBaptiste Daroussin for ( ; '\0' != *p; p++, q++) {
30661d06d6bSBaptiste Daroussin if ('%' == *p) {
30761d06d6bSBaptiste Daroussin if ('\0' == (hex[0] = *(p + 1)))
30861d06d6bSBaptiste Daroussin return 0;
30961d06d6bSBaptiste Daroussin if ('\0' == (hex[1] = *(p + 2)))
31061d06d6bSBaptiste Daroussin return 0;
31161d06d6bSBaptiste Daroussin if (1 != sscanf(hex, "%x", &c))
31261d06d6bSBaptiste Daroussin return 0;
31361d06d6bSBaptiste Daroussin if ('\0' == c)
31461d06d6bSBaptiste Daroussin return 0;
31561d06d6bSBaptiste Daroussin
31661d06d6bSBaptiste Daroussin *q = (char)c;
31761d06d6bSBaptiste Daroussin p += 2;
31861d06d6bSBaptiste Daroussin } else
31961d06d6bSBaptiste Daroussin *q = '+' == *p ? ' ' : *p;
32061d06d6bSBaptiste Daroussin }
32161d06d6bSBaptiste Daroussin
32261d06d6bSBaptiste Daroussin *q = '\0';
32361d06d6bSBaptiste Daroussin return 1;
32461d06d6bSBaptiste Daroussin }
32561d06d6bSBaptiste Daroussin
32661d06d6bSBaptiste Daroussin static void
http_encode(const char * p)3277295610fSBaptiste Daroussin http_encode(const char *p)
3287295610fSBaptiste Daroussin {
3297295610fSBaptiste Daroussin for (; *p != '\0'; p++) {
3307295610fSBaptiste Daroussin if (isalnum((unsigned char)*p) == 0 &&
3317295610fSBaptiste Daroussin strchr("-._~", *p) == NULL)
3327295610fSBaptiste Daroussin printf("%%%2.2X", (unsigned char)*p);
3337295610fSBaptiste Daroussin else
3347295610fSBaptiste Daroussin putchar(*p);
3357295610fSBaptiste Daroussin }
3367295610fSBaptiste Daroussin }
3377295610fSBaptiste Daroussin
3387295610fSBaptiste Daroussin static void
resp_begin_http(int code,const char * msg)33961d06d6bSBaptiste Daroussin resp_begin_http(int code, const char *msg)
34061d06d6bSBaptiste Daroussin {
34161d06d6bSBaptiste Daroussin
34261d06d6bSBaptiste Daroussin if (200 != code)
34361d06d6bSBaptiste Daroussin printf("Status: %d %s\r\n", code, msg);
34461d06d6bSBaptiste Daroussin
34561d06d6bSBaptiste Daroussin printf("Content-Type: text/html; charset=utf-8\r\n"
34661d06d6bSBaptiste Daroussin "Cache-Control: no-cache\r\n"
347*6d38604fSBaptiste Daroussin "Content-Security-Policy: default-src 'none'; "
348*6d38604fSBaptiste Daroussin "style-src 'self' 'unsafe-inline'\r\n"
34961d06d6bSBaptiste Daroussin "Pragma: no-cache\r\n"
35061d06d6bSBaptiste Daroussin "\r\n");
35161d06d6bSBaptiste Daroussin
35261d06d6bSBaptiste Daroussin fflush(stdout);
35361d06d6bSBaptiste Daroussin }
35461d06d6bSBaptiste Daroussin
35561d06d6bSBaptiste Daroussin static void
resp_copy(const char * filename)35661d06d6bSBaptiste Daroussin resp_copy(const char *filename)
35761d06d6bSBaptiste Daroussin {
35861d06d6bSBaptiste Daroussin char buf[4096];
35961d06d6bSBaptiste Daroussin ssize_t sz;
36061d06d6bSBaptiste Daroussin int fd;
36161d06d6bSBaptiste Daroussin
36261d06d6bSBaptiste Daroussin if ((fd = open(filename, O_RDONLY)) != -1) {
36361d06d6bSBaptiste Daroussin fflush(stdout);
36461d06d6bSBaptiste Daroussin while ((sz = read(fd, buf, sizeof(buf))) > 0)
36561d06d6bSBaptiste Daroussin write(STDOUT_FILENO, buf, sz);
36661d06d6bSBaptiste Daroussin close(fd);
36761d06d6bSBaptiste Daroussin }
36861d06d6bSBaptiste Daroussin }
36961d06d6bSBaptiste Daroussin
37061d06d6bSBaptiste Daroussin static void
resp_begin_html(int code,const char * msg,const char * file)37161d06d6bSBaptiste Daroussin resp_begin_html(int code, const char *msg, const char *file)
37261d06d6bSBaptiste Daroussin {
373*6d38604fSBaptiste Daroussin const char *name, *sec, *cp;
374*6d38604fSBaptiste Daroussin int namesz, secsz;
37561d06d6bSBaptiste Daroussin
37661d06d6bSBaptiste Daroussin resp_begin_http(code, msg);
37761d06d6bSBaptiste Daroussin
37861d06d6bSBaptiste Daroussin printf("<!DOCTYPE html>\n"
37961d06d6bSBaptiste Daroussin "<html>\n"
38061d06d6bSBaptiste Daroussin "<head>\n"
38161d06d6bSBaptiste Daroussin " <meta charset=\"UTF-8\"/>\n"
38261d06d6bSBaptiste Daroussin " <meta name=\"viewport\""
38361d06d6bSBaptiste Daroussin " content=\"width=device-width, initial-scale=1.0\">\n"
38461d06d6bSBaptiste Daroussin " <link rel=\"stylesheet\" href=\"%s/mandoc.css\""
38561d06d6bSBaptiste Daroussin " type=\"text/css\" media=\"all\">\n"
38661d06d6bSBaptiste Daroussin " <title>",
38761d06d6bSBaptiste Daroussin CSS_DIR);
38861d06d6bSBaptiste Daroussin if (file != NULL) {
389*6d38604fSBaptiste Daroussin cp = strrchr(file, '/');
390*6d38604fSBaptiste Daroussin name = cp == NULL ? file : cp + 1;
391*6d38604fSBaptiste Daroussin cp = strrchr(name, '.');
392*6d38604fSBaptiste Daroussin namesz = cp == NULL ? strlen(name) : cp - name;
393*6d38604fSBaptiste Daroussin sec = NULL;
394*6d38604fSBaptiste Daroussin if (cp != NULL && cp[1] != '0') {
395*6d38604fSBaptiste Daroussin sec = cp + 1;
396*6d38604fSBaptiste Daroussin secsz = strlen(sec);
397*6d38604fSBaptiste Daroussin } else if (name - file > 1) {
398*6d38604fSBaptiste Daroussin for (cp = name - 2; cp >= file; cp--) {
399*6d38604fSBaptiste Daroussin if (*cp < '1' || *cp > '9')
400*6d38604fSBaptiste Daroussin continue;
401*6d38604fSBaptiste Daroussin sec = cp;
402*6d38604fSBaptiste Daroussin secsz = name - cp - 1;
403*6d38604fSBaptiste Daroussin break;
404*6d38604fSBaptiste Daroussin }
405*6d38604fSBaptiste Daroussin }
406*6d38604fSBaptiste Daroussin printf("%.*s", namesz, name);
407*6d38604fSBaptiste Daroussin if (sec != NULL)
408*6d38604fSBaptiste Daroussin printf("(%.*s)", secsz, sec);
409*6d38604fSBaptiste Daroussin fputs(" - ", stdout);
41061d06d6bSBaptiste Daroussin }
41161d06d6bSBaptiste Daroussin printf("%s</title>\n"
41261d06d6bSBaptiste Daroussin "</head>\n"
41361d06d6bSBaptiste Daroussin "<body>\n",
41461d06d6bSBaptiste Daroussin CUSTOMIZE_TITLE);
41561d06d6bSBaptiste Daroussin
41661d06d6bSBaptiste Daroussin resp_copy(MAN_DIR "/header.html");
41761d06d6bSBaptiste Daroussin }
41861d06d6bSBaptiste Daroussin
41961d06d6bSBaptiste Daroussin static void
resp_end_html(void)42061d06d6bSBaptiste Daroussin resp_end_html(void)
42161d06d6bSBaptiste Daroussin {
42261d06d6bSBaptiste Daroussin
42361d06d6bSBaptiste Daroussin resp_copy(MAN_DIR "/footer.html");
42461d06d6bSBaptiste Daroussin
42561d06d6bSBaptiste Daroussin puts("</body>\n"
42661d06d6bSBaptiste Daroussin "</html>");
42761d06d6bSBaptiste Daroussin }
42861d06d6bSBaptiste Daroussin
42961d06d6bSBaptiste Daroussin static void
resp_searchform(const struct req * req,enum focus focus)43061d06d6bSBaptiste Daroussin resp_searchform(const struct req *req, enum focus focus)
43161d06d6bSBaptiste Daroussin {
43261d06d6bSBaptiste Daroussin int i;
43361d06d6bSBaptiste Daroussin
434*6d38604fSBaptiste Daroussin printf("<form action=\"/%s\" method=\"get\" "
435*6d38604fSBaptiste Daroussin "autocomplete=\"off\" autocapitalize=\"none\">\n"
43661d06d6bSBaptiste Daroussin " <fieldset>\n"
43761d06d6bSBaptiste Daroussin " <legend>Manual Page Search Parameters</legend>\n",
43861d06d6bSBaptiste Daroussin scriptname);
43961d06d6bSBaptiste Daroussin
44061d06d6bSBaptiste Daroussin /* Write query input box. */
44161d06d6bSBaptiste Daroussin
44261d06d6bSBaptiste Daroussin printf(" <input type=\"search\" name=\"query\" value=\"");
44361d06d6bSBaptiste Daroussin if (req->q.query != NULL)
44461d06d6bSBaptiste Daroussin html_print(req->q.query);
44561d06d6bSBaptiste Daroussin printf( "\" size=\"40\"");
44661d06d6bSBaptiste Daroussin if (focus == FOCUS_QUERY)
44761d06d6bSBaptiste Daroussin printf(" autofocus");
44861d06d6bSBaptiste Daroussin puts(">");
44961d06d6bSBaptiste Daroussin
45061d06d6bSBaptiste Daroussin /* Write submission buttons. */
45161d06d6bSBaptiste Daroussin
45261d06d6bSBaptiste Daroussin printf( " <button type=\"submit\" name=\"apropos\" value=\"0\">"
45361d06d6bSBaptiste Daroussin "man</button>\n"
45461d06d6bSBaptiste Daroussin " <button type=\"submit\" name=\"apropos\" value=\"1\">"
45561d06d6bSBaptiste Daroussin "apropos</button>\n"
45661d06d6bSBaptiste Daroussin " <br/>\n");
45761d06d6bSBaptiste Daroussin
45861d06d6bSBaptiste Daroussin /* Write section selector. */
45961d06d6bSBaptiste Daroussin
46061d06d6bSBaptiste Daroussin puts(" <select name=\"sec\">");
46161d06d6bSBaptiste Daroussin for (i = 0; i < sec_MAX; i++) {
46261d06d6bSBaptiste Daroussin printf(" <option value=\"%s\"", sec_numbers[i]);
46361d06d6bSBaptiste Daroussin if (NULL != req->q.sec &&
46461d06d6bSBaptiste Daroussin 0 == strcmp(sec_numbers[i], req->q.sec))
46561d06d6bSBaptiste Daroussin printf(" selected=\"selected\"");
46661d06d6bSBaptiste Daroussin printf(">%s</option>\n", sec_names[i]);
46761d06d6bSBaptiste Daroussin }
46861d06d6bSBaptiste Daroussin puts(" </select>");
46961d06d6bSBaptiste Daroussin
47061d06d6bSBaptiste Daroussin /* Write architecture selector. */
47161d06d6bSBaptiste Daroussin
47261d06d6bSBaptiste Daroussin printf( " <select name=\"arch\">\n"
47361d06d6bSBaptiste Daroussin " <option value=\"default\"");
47461d06d6bSBaptiste Daroussin if (NULL == req->q.arch)
47561d06d6bSBaptiste Daroussin printf(" selected=\"selected\"");
47661d06d6bSBaptiste Daroussin puts(">All Architectures</option>");
47761d06d6bSBaptiste Daroussin for (i = 0; i < arch_MAX; i++) {
47861d06d6bSBaptiste Daroussin printf(" <option");
47961d06d6bSBaptiste Daroussin if (NULL != req->q.arch &&
48061d06d6bSBaptiste Daroussin 0 == strcmp(arch_names[i], req->q.arch))
48161d06d6bSBaptiste Daroussin printf(" selected=\"selected\"");
48261d06d6bSBaptiste Daroussin printf(">%s</option>\n", arch_names[i]);
48361d06d6bSBaptiste Daroussin }
48461d06d6bSBaptiste Daroussin puts(" </select>");
48561d06d6bSBaptiste Daroussin
48661d06d6bSBaptiste Daroussin /* Write manpath selector. */
48761d06d6bSBaptiste Daroussin
48861d06d6bSBaptiste Daroussin if (req->psz > 1) {
48961d06d6bSBaptiste Daroussin puts(" <select name=\"manpath\">");
49061d06d6bSBaptiste Daroussin for (i = 0; i < (int)req->psz; i++) {
49161d06d6bSBaptiste Daroussin printf(" <option");
49261d06d6bSBaptiste Daroussin if (strcmp(req->q.manpath, req->p[i]) == 0)
49361d06d6bSBaptiste Daroussin printf(" selected=\"selected\"");
49461d06d6bSBaptiste Daroussin printf(">");
49561d06d6bSBaptiste Daroussin html_print(req->p[i]);
49661d06d6bSBaptiste Daroussin puts("</option>");
49761d06d6bSBaptiste Daroussin }
49861d06d6bSBaptiste Daroussin puts(" </select>");
49961d06d6bSBaptiste Daroussin }
50061d06d6bSBaptiste Daroussin
50161d06d6bSBaptiste Daroussin puts(" </fieldset>\n"
50261d06d6bSBaptiste Daroussin "</form>");
50361d06d6bSBaptiste Daroussin }
50461d06d6bSBaptiste Daroussin
50561d06d6bSBaptiste Daroussin static int
validate_urifrag(const char * frag)50661d06d6bSBaptiste Daroussin validate_urifrag(const char *frag)
50761d06d6bSBaptiste Daroussin {
50861d06d6bSBaptiste Daroussin
50961d06d6bSBaptiste Daroussin while ('\0' != *frag) {
51061d06d6bSBaptiste Daroussin if ( ! (isalnum((unsigned char)*frag) ||
51161d06d6bSBaptiste Daroussin '-' == *frag || '.' == *frag ||
51261d06d6bSBaptiste Daroussin '/' == *frag || '_' == *frag))
51361d06d6bSBaptiste Daroussin return 0;
51461d06d6bSBaptiste Daroussin frag++;
51561d06d6bSBaptiste Daroussin }
51661d06d6bSBaptiste Daroussin return 1;
51761d06d6bSBaptiste Daroussin }
51861d06d6bSBaptiste Daroussin
51961d06d6bSBaptiste Daroussin static int
validate_manpath(const struct req * req,const char * manpath)52061d06d6bSBaptiste Daroussin validate_manpath(const struct req *req, const char* manpath)
52161d06d6bSBaptiste Daroussin {
52261d06d6bSBaptiste Daroussin size_t i;
52361d06d6bSBaptiste Daroussin
52461d06d6bSBaptiste Daroussin for (i = 0; i < req->psz; i++)
52561d06d6bSBaptiste Daroussin if ( ! strcmp(manpath, req->p[i]))
52661d06d6bSBaptiste Daroussin return 1;
52761d06d6bSBaptiste Daroussin
52861d06d6bSBaptiste Daroussin return 0;
52961d06d6bSBaptiste Daroussin }
53061d06d6bSBaptiste Daroussin
53161d06d6bSBaptiste Daroussin static int
validate_arch(const char * arch)5327295610fSBaptiste Daroussin validate_arch(const char *arch)
5337295610fSBaptiste Daroussin {
5347295610fSBaptiste Daroussin int i;
5357295610fSBaptiste Daroussin
5367295610fSBaptiste Daroussin for (i = 0; i < arch_MAX; i++)
5377295610fSBaptiste Daroussin if (strcmp(arch, arch_names[i]) == 0)
5387295610fSBaptiste Daroussin return 1;
5397295610fSBaptiste Daroussin
5407295610fSBaptiste Daroussin return 0;
5417295610fSBaptiste Daroussin }
5427295610fSBaptiste Daroussin
5437295610fSBaptiste Daroussin static int
validate_filename(const char * file)54461d06d6bSBaptiste Daroussin validate_filename(const char *file)
54561d06d6bSBaptiste Daroussin {
54661d06d6bSBaptiste Daroussin
54761d06d6bSBaptiste Daroussin if ('.' == file[0] && '/' == file[1])
54861d06d6bSBaptiste Daroussin file += 2;
54961d06d6bSBaptiste Daroussin
55061d06d6bSBaptiste Daroussin return ! (strstr(file, "../") || strstr(file, "/..") ||
55161d06d6bSBaptiste Daroussin (strncmp(file, "man", 3) && strncmp(file, "cat", 3)));
55261d06d6bSBaptiste Daroussin }
55361d06d6bSBaptiste Daroussin
55461d06d6bSBaptiste Daroussin static void
pg_index(const struct req * req)55561d06d6bSBaptiste Daroussin pg_index(const struct req *req)
55661d06d6bSBaptiste Daroussin {
55761d06d6bSBaptiste Daroussin
55861d06d6bSBaptiste Daroussin resp_begin_html(200, NULL, NULL);
55961d06d6bSBaptiste Daroussin resp_searchform(req, FOCUS_QUERY);
56061d06d6bSBaptiste Daroussin printf("<p>\n"
56161d06d6bSBaptiste Daroussin "This web interface is documented in the\n"
56261d06d6bSBaptiste Daroussin "<a class=\"Xr\" href=\"/%s%sman.cgi.8\">man.cgi(8)</a>\n"
56361d06d6bSBaptiste Daroussin "manual, and the\n"
56461d06d6bSBaptiste Daroussin "<a class=\"Xr\" href=\"/%s%sapropos.1\">apropos(1)</a>\n"
56561d06d6bSBaptiste Daroussin "manual explains the query syntax.\n"
56661d06d6bSBaptiste Daroussin "</p>\n",
56761d06d6bSBaptiste Daroussin scriptname, *scriptname == '\0' ? "" : "/",
56861d06d6bSBaptiste Daroussin scriptname, *scriptname == '\0' ? "" : "/");
56961d06d6bSBaptiste Daroussin resp_end_html();
57061d06d6bSBaptiste Daroussin }
57161d06d6bSBaptiste Daroussin
57261d06d6bSBaptiste Daroussin static void
pg_noresult(const struct req * req,int code,const char * http_msg,const char * user_msg)573*6d38604fSBaptiste Daroussin pg_noresult(const struct req *req, int code, const char *http_msg,
574*6d38604fSBaptiste Daroussin const char *user_msg)
57561d06d6bSBaptiste Daroussin {
576*6d38604fSBaptiste Daroussin resp_begin_html(code, http_msg, NULL);
57761d06d6bSBaptiste Daroussin resp_searchform(req, FOCUS_QUERY);
57861d06d6bSBaptiste Daroussin puts("<p>");
579*6d38604fSBaptiste Daroussin puts(user_msg);
58061d06d6bSBaptiste Daroussin puts("</p>");
58161d06d6bSBaptiste Daroussin resp_end_html();
58261d06d6bSBaptiste Daroussin }
58361d06d6bSBaptiste Daroussin
58461d06d6bSBaptiste Daroussin static void
pg_error_badrequest(const char * msg)58561d06d6bSBaptiste Daroussin pg_error_badrequest(const char *msg)
58661d06d6bSBaptiste Daroussin {
58761d06d6bSBaptiste Daroussin
58861d06d6bSBaptiste Daroussin resp_begin_html(400, "Bad Request", NULL);
58961d06d6bSBaptiste Daroussin puts("<h1>Bad Request</h1>\n"
59061d06d6bSBaptiste Daroussin "<p>\n");
59161d06d6bSBaptiste Daroussin puts(msg);
59261d06d6bSBaptiste Daroussin printf("Try again from the\n"
59361d06d6bSBaptiste Daroussin "<a href=\"/%s\">main page</a>.\n"
59461d06d6bSBaptiste Daroussin "</p>", scriptname);
59561d06d6bSBaptiste Daroussin resp_end_html();
59661d06d6bSBaptiste Daroussin }
59761d06d6bSBaptiste Daroussin
59861d06d6bSBaptiste Daroussin static void
pg_error_internal(void)59961d06d6bSBaptiste Daroussin pg_error_internal(void)
60061d06d6bSBaptiste Daroussin {
60161d06d6bSBaptiste Daroussin resp_begin_html(500, "Internal Server Error", NULL);
60261d06d6bSBaptiste Daroussin puts("<p>Internal Server Error</p>");
60361d06d6bSBaptiste Daroussin resp_end_html();
60461d06d6bSBaptiste Daroussin }
60561d06d6bSBaptiste Daroussin
60661d06d6bSBaptiste Daroussin static void
pg_redirect(const struct req * req,const char * name)60761d06d6bSBaptiste Daroussin pg_redirect(const struct req *req, const char *name)
60861d06d6bSBaptiste Daroussin {
60961d06d6bSBaptiste Daroussin printf("Status: 303 See Other\r\n"
61061d06d6bSBaptiste Daroussin "Location: /");
61161d06d6bSBaptiste Daroussin if (*scriptname != '\0')
61261d06d6bSBaptiste Daroussin printf("%s/", scriptname);
61361d06d6bSBaptiste Daroussin if (strcmp(req->q.manpath, req->p[0]))
61461d06d6bSBaptiste Daroussin printf("%s/", req->q.manpath);
61561d06d6bSBaptiste Daroussin if (req->q.arch != NULL)
61661d06d6bSBaptiste Daroussin printf("%s/", req->q.arch);
6177295610fSBaptiste Daroussin http_encode(name);
6187295610fSBaptiste Daroussin if (req->q.sec != NULL) {
6197295610fSBaptiste Daroussin putchar('.');
6207295610fSBaptiste Daroussin http_encode(req->q.sec);
6217295610fSBaptiste Daroussin }
62261d06d6bSBaptiste Daroussin printf("\r\nContent-Type: text/html; charset=utf-8\r\n\r\n");
62361d06d6bSBaptiste Daroussin }
62461d06d6bSBaptiste Daroussin
62561d06d6bSBaptiste Daroussin static void
pg_searchres(const struct req * req,struct manpage * r,size_t sz)62661d06d6bSBaptiste Daroussin pg_searchres(const struct req *req, struct manpage *r, size_t sz)
62761d06d6bSBaptiste Daroussin {
62861d06d6bSBaptiste Daroussin char *arch, *archend;
62961d06d6bSBaptiste Daroussin const char *sec;
63061d06d6bSBaptiste Daroussin size_t i, iuse;
63161d06d6bSBaptiste Daroussin int archprio, archpriouse;
63261d06d6bSBaptiste Daroussin int prio, priouse;
63361d06d6bSBaptiste Daroussin
63461d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) {
63561d06d6bSBaptiste Daroussin if (validate_filename(r[i].file))
63661d06d6bSBaptiste Daroussin continue;
63761d06d6bSBaptiste Daroussin warnx("invalid filename %s in %s database",
63861d06d6bSBaptiste Daroussin r[i].file, req->q.manpath);
63961d06d6bSBaptiste Daroussin pg_error_internal();
64061d06d6bSBaptiste Daroussin return;
64161d06d6bSBaptiste Daroussin }
64261d06d6bSBaptiste Daroussin
64361d06d6bSBaptiste Daroussin if (req->isquery && sz == 1) {
64461d06d6bSBaptiste Daroussin /*
64561d06d6bSBaptiste Daroussin * If we have just one result, then jump there now
64661d06d6bSBaptiste Daroussin * without any delay.
64761d06d6bSBaptiste Daroussin */
64861d06d6bSBaptiste Daroussin printf("Status: 303 See Other\r\n"
64961d06d6bSBaptiste Daroussin "Location: /");
65061d06d6bSBaptiste Daroussin if (*scriptname != '\0')
65161d06d6bSBaptiste Daroussin printf("%s/", scriptname);
65261d06d6bSBaptiste Daroussin if (strcmp(req->q.manpath, req->p[0]))
65361d06d6bSBaptiste Daroussin printf("%s/", req->q.manpath);
65461d06d6bSBaptiste Daroussin printf("%s\r\n"
65561d06d6bSBaptiste Daroussin "Content-Type: text/html; charset=utf-8\r\n\r\n",
65661d06d6bSBaptiste Daroussin r[0].file);
65761d06d6bSBaptiste Daroussin return;
65861d06d6bSBaptiste Daroussin }
65961d06d6bSBaptiste Daroussin
66061d06d6bSBaptiste Daroussin /*
66161d06d6bSBaptiste Daroussin * In man(1) mode, show one of the pages
66261d06d6bSBaptiste Daroussin * even if more than one is found.
66361d06d6bSBaptiste Daroussin */
66461d06d6bSBaptiste Daroussin
66561d06d6bSBaptiste Daroussin iuse = 0;
66661d06d6bSBaptiste Daroussin if (req->q.equal || sz == 1) {
66761d06d6bSBaptiste Daroussin priouse = 20;
66861d06d6bSBaptiste Daroussin archpriouse = 3;
66961d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) {
67061d06d6bSBaptiste Daroussin sec = r[i].file;
67161d06d6bSBaptiste Daroussin sec += strcspn(sec, "123456789");
67261d06d6bSBaptiste Daroussin if (sec[0] == '\0')
67361d06d6bSBaptiste Daroussin continue;
67461d06d6bSBaptiste Daroussin prio = sec_prios[sec[0] - '1'];
67561d06d6bSBaptiste Daroussin if (sec[1] != '/')
67661d06d6bSBaptiste Daroussin prio += 10;
67761d06d6bSBaptiste Daroussin if (req->q.arch == NULL) {
67861d06d6bSBaptiste Daroussin archprio =
67961d06d6bSBaptiste Daroussin ((arch = strchr(sec + 1, '/'))
68061d06d6bSBaptiste Daroussin == NULL) ? 3 :
68161d06d6bSBaptiste Daroussin ((archend = strchr(arch + 1, '/'))
68261d06d6bSBaptiste Daroussin == NULL) ? 0 :
68361d06d6bSBaptiste Daroussin strncmp(arch, "amd64/",
68461d06d6bSBaptiste Daroussin archend - arch) ? 2 : 1;
68561d06d6bSBaptiste Daroussin if (archprio < archpriouse) {
68661d06d6bSBaptiste Daroussin archpriouse = archprio;
68761d06d6bSBaptiste Daroussin priouse = prio;
68861d06d6bSBaptiste Daroussin iuse = i;
68961d06d6bSBaptiste Daroussin continue;
69061d06d6bSBaptiste Daroussin }
69161d06d6bSBaptiste Daroussin if (archprio > archpriouse)
69261d06d6bSBaptiste Daroussin continue;
69361d06d6bSBaptiste Daroussin }
69461d06d6bSBaptiste Daroussin if (prio >= priouse)
69561d06d6bSBaptiste Daroussin continue;
69661d06d6bSBaptiste Daroussin priouse = prio;
69761d06d6bSBaptiste Daroussin iuse = i;
69861d06d6bSBaptiste Daroussin }
69961d06d6bSBaptiste Daroussin resp_begin_html(200, NULL, r[iuse].file);
70061d06d6bSBaptiste Daroussin } else
70161d06d6bSBaptiste Daroussin resp_begin_html(200, NULL, NULL);
70261d06d6bSBaptiste Daroussin
70361d06d6bSBaptiste Daroussin resp_searchform(req,
70461d06d6bSBaptiste Daroussin req->q.equal || sz == 1 ? FOCUS_NONE : FOCUS_QUERY);
70561d06d6bSBaptiste Daroussin
70661d06d6bSBaptiste Daroussin if (sz > 1) {
70761d06d6bSBaptiste Daroussin puts("<table class=\"results\">");
70861d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) {
70961d06d6bSBaptiste Daroussin printf(" <tr>\n"
71061d06d6bSBaptiste Daroussin " <td>"
71161d06d6bSBaptiste Daroussin "<a class=\"Xr\" href=\"/");
71261d06d6bSBaptiste Daroussin if (*scriptname != '\0')
71361d06d6bSBaptiste Daroussin printf("%s/", scriptname);
71461d06d6bSBaptiste Daroussin if (strcmp(req->q.manpath, req->p[0]))
71561d06d6bSBaptiste Daroussin printf("%s/", req->q.manpath);
71661d06d6bSBaptiste Daroussin printf("%s\">", r[i].file);
71761d06d6bSBaptiste Daroussin html_print(r[i].names);
71861d06d6bSBaptiste Daroussin printf("</a></td>\n"
71961d06d6bSBaptiste Daroussin " <td><span class=\"Nd\">");
72061d06d6bSBaptiste Daroussin html_print(r[i].output);
72161d06d6bSBaptiste Daroussin puts("</span></td>\n"
72261d06d6bSBaptiste Daroussin " </tr>");
72361d06d6bSBaptiste Daroussin }
72461d06d6bSBaptiste Daroussin puts("</table>");
72561d06d6bSBaptiste Daroussin }
72661d06d6bSBaptiste Daroussin
72761d06d6bSBaptiste Daroussin if (req->q.equal || sz == 1) {
72861d06d6bSBaptiste Daroussin puts("<hr>");
72961d06d6bSBaptiste Daroussin resp_show(req, r[iuse].file);
73061d06d6bSBaptiste Daroussin }
73161d06d6bSBaptiste Daroussin
73261d06d6bSBaptiste Daroussin resp_end_html();
73361d06d6bSBaptiste Daroussin }
73461d06d6bSBaptiste Daroussin
73561d06d6bSBaptiste Daroussin static void
resp_catman(const struct req * req,const char * file)73661d06d6bSBaptiste Daroussin resp_catman(const struct req *req, const char *file)
73761d06d6bSBaptiste Daroussin {
73861d06d6bSBaptiste Daroussin FILE *f;
73961d06d6bSBaptiste Daroussin char *p;
74061d06d6bSBaptiste Daroussin size_t sz;
74161d06d6bSBaptiste Daroussin ssize_t len;
74261d06d6bSBaptiste Daroussin int i;
74361d06d6bSBaptiste Daroussin int italic, bold;
74461d06d6bSBaptiste Daroussin
74561d06d6bSBaptiste Daroussin if ((f = fopen(file, "r")) == NULL) {
74661d06d6bSBaptiste Daroussin puts("<p>You specified an invalid manual file.</p>");
74761d06d6bSBaptiste Daroussin return;
74861d06d6bSBaptiste Daroussin }
74961d06d6bSBaptiste Daroussin
75061d06d6bSBaptiste Daroussin puts("<div class=\"catman\">\n"
75161d06d6bSBaptiste Daroussin "<pre>");
75261d06d6bSBaptiste Daroussin
75361d06d6bSBaptiste Daroussin p = NULL;
75461d06d6bSBaptiste Daroussin sz = 0;
75561d06d6bSBaptiste Daroussin
75661d06d6bSBaptiste Daroussin while ((len = getline(&p, &sz, f)) != -1) {
75761d06d6bSBaptiste Daroussin bold = italic = 0;
75861d06d6bSBaptiste Daroussin for (i = 0; i < len - 1; i++) {
75961d06d6bSBaptiste Daroussin /*
76061d06d6bSBaptiste Daroussin * This means that the catpage is out of state.
76161d06d6bSBaptiste Daroussin * Ignore it and keep going (although the
76261d06d6bSBaptiste Daroussin * catpage is bogus).
76361d06d6bSBaptiste Daroussin */
76461d06d6bSBaptiste Daroussin
76561d06d6bSBaptiste Daroussin if ('\b' == p[i] || '\n' == p[i])
76661d06d6bSBaptiste Daroussin continue;
76761d06d6bSBaptiste Daroussin
76861d06d6bSBaptiste Daroussin /*
76961d06d6bSBaptiste Daroussin * Print a regular character.
77061d06d6bSBaptiste Daroussin * Close out any bold/italic scopes.
77161d06d6bSBaptiste Daroussin * If we're in back-space mode, make sure we'll
77261d06d6bSBaptiste Daroussin * have something to enter when we backspace.
77361d06d6bSBaptiste Daroussin */
77461d06d6bSBaptiste Daroussin
77561d06d6bSBaptiste Daroussin if ('\b' != p[i + 1]) {
77661d06d6bSBaptiste Daroussin if (italic)
77761d06d6bSBaptiste Daroussin printf("</i>");
77861d06d6bSBaptiste Daroussin if (bold)
77961d06d6bSBaptiste Daroussin printf("</b>");
78061d06d6bSBaptiste Daroussin italic = bold = 0;
78161d06d6bSBaptiste Daroussin html_putchar(p[i]);
78261d06d6bSBaptiste Daroussin continue;
78361d06d6bSBaptiste Daroussin } else if (i + 2 >= len)
78461d06d6bSBaptiste Daroussin continue;
78561d06d6bSBaptiste Daroussin
78661d06d6bSBaptiste Daroussin /* Italic mode. */
78761d06d6bSBaptiste Daroussin
78861d06d6bSBaptiste Daroussin if ('_' == p[i]) {
78961d06d6bSBaptiste Daroussin if (bold)
79061d06d6bSBaptiste Daroussin printf("</b>");
79161d06d6bSBaptiste Daroussin if ( ! italic)
79261d06d6bSBaptiste Daroussin printf("<i>");
79361d06d6bSBaptiste Daroussin bold = 0;
79461d06d6bSBaptiste Daroussin italic = 1;
79561d06d6bSBaptiste Daroussin i += 2;
79661d06d6bSBaptiste Daroussin html_putchar(p[i]);
79761d06d6bSBaptiste Daroussin continue;
79861d06d6bSBaptiste Daroussin }
79961d06d6bSBaptiste Daroussin
80061d06d6bSBaptiste Daroussin /*
80161d06d6bSBaptiste Daroussin * Handle funny behaviour troff-isms.
80261d06d6bSBaptiste Daroussin * These grok'd from the original man2html.c.
80361d06d6bSBaptiste Daroussin */
80461d06d6bSBaptiste Daroussin
80561d06d6bSBaptiste Daroussin if (('+' == p[i] && 'o' == p[i + 2]) ||
80661d06d6bSBaptiste Daroussin ('o' == p[i] && '+' == p[i + 2]) ||
80761d06d6bSBaptiste Daroussin ('|' == p[i] && '=' == p[i + 2]) ||
80861d06d6bSBaptiste Daroussin ('=' == p[i] && '|' == p[i + 2]) ||
80961d06d6bSBaptiste Daroussin ('*' == p[i] && '=' == p[i + 2]) ||
81061d06d6bSBaptiste Daroussin ('=' == p[i] && '*' == p[i + 2]) ||
81161d06d6bSBaptiste Daroussin ('*' == p[i] && '|' == p[i + 2]) ||
81261d06d6bSBaptiste Daroussin ('|' == p[i] && '*' == p[i + 2])) {
81361d06d6bSBaptiste Daroussin if (italic)
81461d06d6bSBaptiste Daroussin printf("</i>");
81561d06d6bSBaptiste Daroussin if (bold)
81661d06d6bSBaptiste Daroussin printf("</b>");
81761d06d6bSBaptiste Daroussin italic = bold = 0;
81861d06d6bSBaptiste Daroussin putchar('*');
81961d06d6bSBaptiste Daroussin i += 2;
82061d06d6bSBaptiste Daroussin continue;
82161d06d6bSBaptiste Daroussin } else if (('|' == p[i] && '-' == p[i + 2]) ||
82261d06d6bSBaptiste Daroussin ('-' == p[i] && '|' == p[i + 1]) ||
82361d06d6bSBaptiste Daroussin ('+' == p[i] && '-' == p[i + 1]) ||
82461d06d6bSBaptiste Daroussin ('-' == p[i] && '+' == p[i + 1]) ||
82561d06d6bSBaptiste Daroussin ('+' == p[i] && '|' == p[i + 1]) ||
82661d06d6bSBaptiste Daroussin ('|' == p[i] && '+' == p[i + 1])) {
82761d06d6bSBaptiste Daroussin if (italic)
82861d06d6bSBaptiste Daroussin printf("</i>");
82961d06d6bSBaptiste Daroussin if (bold)
83061d06d6bSBaptiste Daroussin printf("</b>");
83161d06d6bSBaptiste Daroussin italic = bold = 0;
83261d06d6bSBaptiste Daroussin putchar('+');
83361d06d6bSBaptiste Daroussin i += 2;
83461d06d6bSBaptiste Daroussin continue;
83561d06d6bSBaptiste Daroussin }
83661d06d6bSBaptiste Daroussin
83761d06d6bSBaptiste Daroussin /* Bold mode. */
83861d06d6bSBaptiste Daroussin
83961d06d6bSBaptiste Daroussin if (italic)
84061d06d6bSBaptiste Daroussin printf("</i>");
84161d06d6bSBaptiste Daroussin if ( ! bold)
84261d06d6bSBaptiste Daroussin printf("<b>");
84361d06d6bSBaptiste Daroussin bold = 1;
84461d06d6bSBaptiste Daroussin italic = 0;
84561d06d6bSBaptiste Daroussin i += 2;
84661d06d6bSBaptiste Daroussin html_putchar(p[i]);
84761d06d6bSBaptiste Daroussin }
84861d06d6bSBaptiste Daroussin
84961d06d6bSBaptiste Daroussin /*
85061d06d6bSBaptiste Daroussin * Clean up the last character.
85161d06d6bSBaptiste Daroussin * We can get to a newline; don't print that.
85261d06d6bSBaptiste Daroussin */
85361d06d6bSBaptiste Daroussin
85461d06d6bSBaptiste Daroussin if (italic)
85561d06d6bSBaptiste Daroussin printf("</i>");
85661d06d6bSBaptiste Daroussin if (bold)
85761d06d6bSBaptiste Daroussin printf("</b>");
85861d06d6bSBaptiste Daroussin
85961d06d6bSBaptiste Daroussin if (i == len - 1 && p[i] != '\n')
86061d06d6bSBaptiste Daroussin html_putchar(p[i]);
86161d06d6bSBaptiste Daroussin
86261d06d6bSBaptiste Daroussin putchar('\n');
86361d06d6bSBaptiste Daroussin }
86461d06d6bSBaptiste Daroussin free(p);
86561d06d6bSBaptiste Daroussin
86661d06d6bSBaptiste Daroussin puts("</pre>\n"
86761d06d6bSBaptiste Daroussin "</div>");
86861d06d6bSBaptiste Daroussin
86961d06d6bSBaptiste Daroussin fclose(f);
87061d06d6bSBaptiste Daroussin }
87161d06d6bSBaptiste Daroussin
87261d06d6bSBaptiste Daroussin static void
resp_format(const struct req * req,const char * file)87361d06d6bSBaptiste Daroussin resp_format(const struct req *req, const char *file)
87461d06d6bSBaptiste Daroussin {
87561d06d6bSBaptiste Daroussin struct manoutput conf;
87661d06d6bSBaptiste Daroussin struct mparse *mp;
8777295610fSBaptiste Daroussin struct roff_meta *meta;
87861d06d6bSBaptiste Daroussin void *vp;
87961d06d6bSBaptiste Daroussin int fd;
88061d06d6bSBaptiste Daroussin int usepath;
88161d06d6bSBaptiste Daroussin
88261d06d6bSBaptiste Daroussin if (-1 == (fd = open(file, O_RDONLY, 0))) {
88361d06d6bSBaptiste Daroussin puts("<p>You specified an invalid manual file.</p>");
88461d06d6bSBaptiste Daroussin return;
88561d06d6bSBaptiste Daroussin }
88661d06d6bSBaptiste Daroussin
88761d06d6bSBaptiste Daroussin mchars_alloc();
8887295610fSBaptiste Daroussin mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
8897295610fSBaptiste Daroussin MPARSE_VALIDATE, MANDOC_OS_OTHER, req->q.manpath);
89061d06d6bSBaptiste Daroussin mparse_readfd(mp, fd, file);
89161d06d6bSBaptiste Daroussin close(fd);
8927295610fSBaptiste Daroussin meta = mparse_result(mp);
89361d06d6bSBaptiste Daroussin
89461d06d6bSBaptiste Daroussin memset(&conf, 0, sizeof(conf));
89561d06d6bSBaptiste Daroussin conf.fragment = 1;
89661d06d6bSBaptiste Daroussin conf.style = mandoc_strdup(CSS_DIR "/mandoc.css");
89761d06d6bSBaptiste Daroussin usepath = strcmp(req->q.manpath, req->p[0]);
89861d06d6bSBaptiste Daroussin mandoc_asprintf(&conf.man, "/%s%s%s%s%%N.%%S",
89961d06d6bSBaptiste Daroussin scriptname, *scriptname == '\0' ? "" : "/",
90061d06d6bSBaptiste Daroussin usepath ? req->q.manpath : "", usepath ? "/" : "");
90161d06d6bSBaptiste Daroussin
90261d06d6bSBaptiste Daroussin vp = html_alloc(&conf);
9037295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MDOC)
9047295610fSBaptiste Daroussin html_mdoc(vp, meta);
9057295610fSBaptiste Daroussin else
9067295610fSBaptiste Daroussin html_man(vp, meta);
90761d06d6bSBaptiste Daroussin
90861d06d6bSBaptiste Daroussin html_free(vp);
90961d06d6bSBaptiste Daroussin mparse_free(mp);
91061d06d6bSBaptiste Daroussin mchars_free();
91161d06d6bSBaptiste Daroussin free(conf.man);
91261d06d6bSBaptiste Daroussin free(conf.style);
91361d06d6bSBaptiste Daroussin }
91461d06d6bSBaptiste Daroussin
91561d06d6bSBaptiste Daroussin static void
resp_show(const struct req * req,const char * file)91661d06d6bSBaptiste Daroussin resp_show(const struct req *req, const char *file)
91761d06d6bSBaptiste Daroussin {
91861d06d6bSBaptiste Daroussin
91961d06d6bSBaptiste Daroussin if ('.' == file[0] && '/' == file[1])
92061d06d6bSBaptiste Daroussin file += 2;
92161d06d6bSBaptiste Daroussin
92261d06d6bSBaptiste Daroussin if ('c' == *file)
92361d06d6bSBaptiste Daroussin resp_catman(req, file);
92461d06d6bSBaptiste Daroussin else
92561d06d6bSBaptiste Daroussin resp_format(req, file);
92661d06d6bSBaptiste Daroussin }
92761d06d6bSBaptiste Daroussin
92861d06d6bSBaptiste Daroussin static void
pg_show(struct req * req,const char * fullpath)92961d06d6bSBaptiste Daroussin pg_show(struct req *req, const char *fullpath)
93061d06d6bSBaptiste Daroussin {
93161d06d6bSBaptiste Daroussin char *manpath;
93261d06d6bSBaptiste Daroussin const char *file;
93361d06d6bSBaptiste Daroussin
93461d06d6bSBaptiste Daroussin if ((file = strchr(fullpath, '/')) == NULL) {
93561d06d6bSBaptiste Daroussin pg_error_badrequest(
93661d06d6bSBaptiste Daroussin "You did not specify a page to show.");
93761d06d6bSBaptiste Daroussin return;
93861d06d6bSBaptiste Daroussin }
93961d06d6bSBaptiste Daroussin manpath = mandoc_strndup(fullpath, file - fullpath);
94061d06d6bSBaptiste Daroussin file++;
94161d06d6bSBaptiste Daroussin
94261d06d6bSBaptiste Daroussin if ( ! validate_manpath(req, manpath)) {
94361d06d6bSBaptiste Daroussin pg_error_badrequest(
94461d06d6bSBaptiste Daroussin "You specified an invalid manpath.");
94561d06d6bSBaptiste Daroussin free(manpath);
94661d06d6bSBaptiste Daroussin return;
94761d06d6bSBaptiste Daroussin }
94861d06d6bSBaptiste Daroussin
94961d06d6bSBaptiste Daroussin /*
95061d06d6bSBaptiste Daroussin * Begin by chdir()ing into the manpath.
95161d06d6bSBaptiste Daroussin * This way we can pick up the database files, which are
95261d06d6bSBaptiste Daroussin * relative to the manpath root.
95361d06d6bSBaptiste Daroussin */
95461d06d6bSBaptiste Daroussin
95561d06d6bSBaptiste Daroussin if (chdir(manpath) == -1) {
95661d06d6bSBaptiste Daroussin warn("chdir %s", manpath);
95761d06d6bSBaptiste Daroussin pg_error_internal();
95861d06d6bSBaptiste Daroussin free(manpath);
95961d06d6bSBaptiste Daroussin return;
96061d06d6bSBaptiste Daroussin }
96161d06d6bSBaptiste Daroussin free(manpath);
96261d06d6bSBaptiste Daroussin
96361d06d6bSBaptiste Daroussin if ( ! validate_filename(file)) {
96461d06d6bSBaptiste Daroussin pg_error_badrequest(
96561d06d6bSBaptiste Daroussin "You specified an invalid manual file.");
96661d06d6bSBaptiste Daroussin return;
96761d06d6bSBaptiste Daroussin }
96861d06d6bSBaptiste Daroussin
96961d06d6bSBaptiste Daroussin resp_begin_html(200, NULL, file);
97061d06d6bSBaptiste Daroussin resp_searchform(req, FOCUS_NONE);
97161d06d6bSBaptiste Daroussin resp_show(req, file);
97261d06d6bSBaptiste Daroussin resp_end_html();
97361d06d6bSBaptiste Daroussin }
97461d06d6bSBaptiste Daroussin
97561d06d6bSBaptiste Daroussin static void
pg_search(const struct req * req)97661d06d6bSBaptiste Daroussin pg_search(const struct req *req)
97761d06d6bSBaptiste Daroussin {
97861d06d6bSBaptiste Daroussin struct mansearch search;
97961d06d6bSBaptiste Daroussin struct manpaths paths;
98061d06d6bSBaptiste Daroussin struct manpage *res;
98161d06d6bSBaptiste Daroussin char **argv;
98261d06d6bSBaptiste Daroussin char *query, *rp, *wp;
98361d06d6bSBaptiste Daroussin size_t ressz;
98461d06d6bSBaptiste Daroussin int argc;
98561d06d6bSBaptiste Daroussin
98661d06d6bSBaptiste Daroussin /*
98761d06d6bSBaptiste Daroussin * Begin by chdir()ing into the root of the manpath.
98861d06d6bSBaptiste Daroussin * This way we can pick up the database files, which are
98961d06d6bSBaptiste Daroussin * relative to the manpath root.
99061d06d6bSBaptiste Daroussin */
99161d06d6bSBaptiste Daroussin
99261d06d6bSBaptiste Daroussin if (chdir(req->q.manpath) == -1) {
99361d06d6bSBaptiste Daroussin warn("chdir %s", req->q.manpath);
99461d06d6bSBaptiste Daroussin pg_error_internal();
99561d06d6bSBaptiste Daroussin return;
99661d06d6bSBaptiste Daroussin }
99761d06d6bSBaptiste Daroussin
99861d06d6bSBaptiste Daroussin search.arch = req->q.arch;
99961d06d6bSBaptiste Daroussin search.sec = req->q.sec;
100061d06d6bSBaptiste Daroussin search.outkey = "Nd";
100161d06d6bSBaptiste Daroussin search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR;
100261d06d6bSBaptiste Daroussin search.firstmatch = 1;
100361d06d6bSBaptiste Daroussin
100461d06d6bSBaptiste Daroussin paths.sz = 1;
100561d06d6bSBaptiste Daroussin paths.paths = mandoc_malloc(sizeof(char *));
100661d06d6bSBaptiste Daroussin paths.paths[0] = mandoc_strdup(".");
100761d06d6bSBaptiste Daroussin
100861d06d6bSBaptiste Daroussin /*
100961d06d6bSBaptiste Daroussin * Break apart at spaces with backslash-escaping.
101061d06d6bSBaptiste Daroussin */
101161d06d6bSBaptiste Daroussin
101261d06d6bSBaptiste Daroussin argc = 0;
101361d06d6bSBaptiste Daroussin argv = NULL;
101461d06d6bSBaptiste Daroussin rp = query = mandoc_strdup(req->q.query);
101561d06d6bSBaptiste Daroussin for (;;) {
101661d06d6bSBaptiste Daroussin while (isspace((unsigned char)*rp))
101761d06d6bSBaptiste Daroussin rp++;
101861d06d6bSBaptiste Daroussin if (*rp == '\0')
101961d06d6bSBaptiste Daroussin break;
102061d06d6bSBaptiste Daroussin argv = mandoc_reallocarray(argv, argc + 1, sizeof(char *));
102161d06d6bSBaptiste Daroussin argv[argc++] = wp = rp;
102261d06d6bSBaptiste Daroussin for (;;) {
102361d06d6bSBaptiste Daroussin if (isspace((unsigned char)*rp)) {
102461d06d6bSBaptiste Daroussin *wp = '\0';
102561d06d6bSBaptiste Daroussin rp++;
102661d06d6bSBaptiste Daroussin break;
102761d06d6bSBaptiste Daroussin }
102861d06d6bSBaptiste Daroussin if (rp[0] == '\\' && rp[1] != '\0')
102961d06d6bSBaptiste Daroussin rp++;
103061d06d6bSBaptiste Daroussin if (wp != rp)
103161d06d6bSBaptiste Daroussin *wp = *rp;
103261d06d6bSBaptiste Daroussin if (*rp == '\0')
103361d06d6bSBaptiste Daroussin break;
103461d06d6bSBaptiste Daroussin wp++;
103561d06d6bSBaptiste Daroussin rp++;
103661d06d6bSBaptiste Daroussin }
103761d06d6bSBaptiste Daroussin }
103861d06d6bSBaptiste Daroussin
103961d06d6bSBaptiste Daroussin res = NULL;
104061d06d6bSBaptiste Daroussin ressz = 0;
104161d06d6bSBaptiste Daroussin if (req->isquery && req->q.equal && argc == 1)
104261d06d6bSBaptiste Daroussin pg_redirect(req, argv[0]);
104361d06d6bSBaptiste Daroussin else if (mansearch(&search, &paths, argc, argv, &res, &ressz) == 0)
1044*6d38604fSBaptiste Daroussin pg_noresult(req, 400, "Bad Request",
1045*6d38604fSBaptiste Daroussin "You entered an invalid query.");
104661d06d6bSBaptiste Daroussin else if (ressz == 0)
1047*6d38604fSBaptiste Daroussin pg_noresult(req, 404, "Not Found", "No results found.");
104861d06d6bSBaptiste Daroussin else
104961d06d6bSBaptiste Daroussin pg_searchres(req, res, ressz);
105061d06d6bSBaptiste Daroussin
105161d06d6bSBaptiste Daroussin free(query);
105261d06d6bSBaptiste Daroussin mansearch_free(res, ressz);
105361d06d6bSBaptiste Daroussin free(paths.paths[0]);
105461d06d6bSBaptiste Daroussin free(paths.paths);
105561d06d6bSBaptiste Daroussin }
105661d06d6bSBaptiste Daroussin
105761d06d6bSBaptiste Daroussin int
main(void)105861d06d6bSBaptiste Daroussin main(void)
105961d06d6bSBaptiste Daroussin {
106061d06d6bSBaptiste Daroussin struct req req;
106161d06d6bSBaptiste Daroussin struct itimerval itimer;
106261d06d6bSBaptiste Daroussin const char *path;
106361d06d6bSBaptiste Daroussin const char *querystring;
106461d06d6bSBaptiste Daroussin int i;
106561d06d6bSBaptiste Daroussin
106661d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
106761d06d6bSBaptiste Daroussin /*
106861d06d6bSBaptiste Daroussin * The "rpath" pledge could be revoked after mparse_readfd()
106961d06d6bSBaptiste Daroussin * if the file desciptor to "/footer.html" would be opened
107061d06d6bSBaptiste Daroussin * up front, but it's probably not worth the complication
107161d06d6bSBaptiste Daroussin * of the code it would cause: it would require scattering
107261d06d6bSBaptiste Daroussin * pledge() calls in multiple low-level resp_*() functions.
107361d06d6bSBaptiste Daroussin */
107461d06d6bSBaptiste Daroussin
107561d06d6bSBaptiste Daroussin if (pledge("stdio rpath", NULL) == -1) {
107661d06d6bSBaptiste Daroussin warn("pledge");
107761d06d6bSBaptiste Daroussin pg_error_internal();
107861d06d6bSBaptiste Daroussin return EXIT_FAILURE;
107961d06d6bSBaptiste Daroussin }
108061d06d6bSBaptiste Daroussin #endif
108161d06d6bSBaptiste Daroussin
108261d06d6bSBaptiste Daroussin /* Poor man's ReDoS mitigation. */
108361d06d6bSBaptiste Daroussin
108461d06d6bSBaptiste Daroussin itimer.it_value.tv_sec = 2;
108561d06d6bSBaptiste Daroussin itimer.it_value.tv_usec = 0;
108661d06d6bSBaptiste Daroussin itimer.it_interval.tv_sec = 2;
108761d06d6bSBaptiste Daroussin itimer.it_interval.tv_usec = 0;
108861d06d6bSBaptiste Daroussin if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) {
108961d06d6bSBaptiste Daroussin warn("setitimer");
109061d06d6bSBaptiste Daroussin pg_error_internal();
109161d06d6bSBaptiste Daroussin return EXIT_FAILURE;
109261d06d6bSBaptiste Daroussin }
109361d06d6bSBaptiste Daroussin
109461d06d6bSBaptiste Daroussin /*
109561d06d6bSBaptiste Daroussin * First we change directory into the MAN_DIR so that
109661d06d6bSBaptiste Daroussin * subsequent scanning for manpath directories is rooted
109761d06d6bSBaptiste Daroussin * relative to the same position.
109861d06d6bSBaptiste Daroussin */
109961d06d6bSBaptiste Daroussin
110061d06d6bSBaptiste Daroussin if (chdir(MAN_DIR) == -1) {
110161d06d6bSBaptiste Daroussin warn("MAN_DIR: %s", MAN_DIR);
110261d06d6bSBaptiste Daroussin pg_error_internal();
110361d06d6bSBaptiste Daroussin return EXIT_FAILURE;
110461d06d6bSBaptiste Daroussin }
110561d06d6bSBaptiste Daroussin
110661d06d6bSBaptiste Daroussin memset(&req, 0, sizeof(struct req));
110761d06d6bSBaptiste Daroussin req.q.equal = 1;
110861d06d6bSBaptiste Daroussin parse_manpath_conf(&req);
110961d06d6bSBaptiste Daroussin
111061d06d6bSBaptiste Daroussin /* Parse the path info and the query string. */
111161d06d6bSBaptiste Daroussin
111261d06d6bSBaptiste Daroussin if ((path = getenv("PATH_INFO")) == NULL)
111361d06d6bSBaptiste Daroussin path = "";
111461d06d6bSBaptiste Daroussin else if (*path == '/')
111561d06d6bSBaptiste Daroussin path++;
111661d06d6bSBaptiste Daroussin
111761d06d6bSBaptiste Daroussin if (*path != '\0') {
111861d06d6bSBaptiste Daroussin parse_path_info(&req, path);
111961d06d6bSBaptiste Daroussin if (req.q.manpath == NULL || req.q.sec == NULL ||
112061d06d6bSBaptiste Daroussin *req.q.query == '\0' || access(path, F_OK) == -1)
112161d06d6bSBaptiste Daroussin path = "";
112261d06d6bSBaptiste Daroussin } else if ((querystring = getenv("QUERY_STRING")) != NULL)
112361d06d6bSBaptiste Daroussin parse_query_string(&req, querystring);
112461d06d6bSBaptiste Daroussin
112561d06d6bSBaptiste Daroussin /* Validate parsed data and add defaults. */
112661d06d6bSBaptiste Daroussin
112761d06d6bSBaptiste Daroussin if (req.q.manpath == NULL)
112861d06d6bSBaptiste Daroussin req.q.manpath = mandoc_strdup(req.p[0]);
112961d06d6bSBaptiste Daroussin else if ( ! validate_manpath(&req, req.q.manpath)) {
113061d06d6bSBaptiste Daroussin pg_error_badrequest(
113161d06d6bSBaptiste Daroussin "You specified an invalid manpath.");
113261d06d6bSBaptiste Daroussin return EXIT_FAILURE;
113361d06d6bSBaptiste Daroussin }
113461d06d6bSBaptiste Daroussin
11357295610fSBaptiste Daroussin if (req.q.arch != NULL && validate_arch(req.q.arch) == 0) {
113661d06d6bSBaptiste Daroussin pg_error_badrequest(
113761d06d6bSBaptiste Daroussin "You specified an invalid architecture.");
113861d06d6bSBaptiste Daroussin return EXIT_FAILURE;
113961d06d6bSBaptiste Daroussin }
114061d06d6bSBaptiste Daroussin
114161d06d6bSBaptiste Daroussin /* Dispatch to the three different pages. */
114261d06d6bSBaptiste Daroussin
114361d06d6bSBaptiste Daroussin if ('\0' != *path)
114461d06d6bSBaptiste Daroussin pg_show(&req, path);
114561d06d6bSBaptiste Daroussin else if (NULL != req.q.query)
114661d06d6bSBaptiste Daroussin pg_search(&req);
114761d06d6bSBaptiste Daroussin else
114861d06d6bSBaptiste Daroussin pg_index(&req);
114961d06d6bSBaptiste Daroussin
115061d06d6bSBaptiste Daroussin free(req.q.manpath);
115161d06d6bSBaptiste Daroussin free(req.q.arch);
115261d06d6bSBaptiste Daroussin free(req.q.sec);
115361d06d6bSBaptiste Daroussin free(req.q.query);
115461d06d6bSBaptiste Daroussin for (i = 0; i < (int)req.psz; i++)
115561d06d6bSBaptiste Daroussin free(req.p[i]);
115661d06d6bSBaptiste Daroussin free(req.p);
115761d06d6bSBaptiste Daroussin return EXIT_SUCCESS;
115861d06d6bSBaptiste Daroussin }
115961d06d6bSBaptiste Daroussin
116061d06d6bSBaptiste Daroussin /*
11617295610fSBaptiste Daroussin * Translate PATH_INFO to a query.
116261d06d6bSBaptiste Daroussin */
116361d06d6bSBaptiste Daroussin static void
parse_path_info(struct req * req,const char * path)116461d06d6bSBaptiste Daroussin parse_path_info(struct req *req, const char *path)
116561d06d6bSBaptiste Daroussin {
11667295610fSBaptiste Daroussin const char *name, *sec, *end;
116761d06d6bSBaptiste Daroussin
116861d06d6bSBaptiste Daroussin req->isquery = 0;
116961d06d6bSBaptiste Daroussin req->q.equal = 1;
11707295610fSBaptiste Daroussin req->q.manpath = NULL;
117161d06d6bSBaptiste Daroussin req->q.arch = NULL;
117261d06d6bSBaptiste Daroussin
117361d06d6bSBaptiste Daroussin /* Mandatory manual page name. */
11747295610fSBaptiste Daroussin if ((name = strrchr(path, '/')) == NULL)
11757295610fSBaptiste Daroussin name = path;
11767295610fSBaptiste Daroussin else
11777295610fSBaptiste Daroussin name++;
117861d06d6bSBaptiste Daroussin
117961d06d6bSBaptiste Daroussin /* Optional trailing section. */
11807295610fSBaptiste Daroussin sec = strrchr(name, '.');
11817295610fSBaptiste Daroussin if (sec != NULL && isdigit((unsigned char)*++sec)) {
11827295610fSBaptiste Daroussin req->q.query = mandoc_strndup(name, sec - name - 1);
11837295610fSBaptiste Daroussin req->q.sec = mandoc_strdup(sec);
11847295610fSBaptiste Daroussin } else {
11857295610fSBaptiste Daroussin req->q.query = mandoc_strdup(name);
118661d06d6bSBaptiste Daroussin req->q.sec = NULL;
118761d06d6bSBaptiste Daroussin }
118861d06d6bSBaptiste Daroussin
118961d06d6bSBaptiste Daroussin /* Handle the case of name[.section] only. */
11907295610fSBaptiste Daroussin if (name == path)
119161d06d6bSBaptiste Daroussin return;
119261d06d6bSBaptiste Daroussin
11937295610fSBaptiste Daroussin /* Optional manpath. */
11947295610fSBaptiste Daroussin end = strchr(path, '/');
11957295610fSBaptiste Daroussin req->q.manpath = mandoc_strndup(path, end - path);
11967295610fSBaptiste Daroussin if (validate_manpath(req, req->q.manpath)) {
11977295610fSBaptiste Daroussin path = end + 1;
11987295610fSBaptiste Daroussin if (name == path)
11997295610fSBaptiste Daroussin return;
12007295610fSBaptiste Daroussin } else {
12017295610fSBaptiste Daroussin free(req->q.manpath);
12027295610fSBaptiste Daroussin req->q.manpath = NULL;
12037295610fSBaptiste Daroussin }
12047295610fSBaptiste Daroussin
12057295610fSBaptiste Daroussin /* Optional section. */
12067295610fSBaptiste Daroussin if (strncmp(path, "man", 3) == 0 || strncmp(path, "cat", 3) == 0) {
12077295610fSBaptiste Daroussin path += 3;
12087295610fSBaptiste Daroussin end = strchr(path, '/');
12097295610fSBaptiste Daroussin free(req->q.sec);
12107295610fSBaptiste Daroussin req->q.sec = mandoc_strndup(path, end - path);
12117295610fSBaptiste Daroussin path = end + 1;
12127295610fSBaptiste Daroussin if (name == path)
12137295610fSBaptiste Daroussin return;
12147295610fSBaptiste Daroussin }
12157295610fSBaptiste Daroussin
12167295610fSBaptiste Daroussin /* Optional architecture. */
12177295610fSBaptiste Daroussin end = strchr(path, '/');
12187295610fSBaptiste Daroussin if (end + 1 != name) {
121961d06d6bSBaptiste Daroussin pg_error_badrequest(
122061d06d6bSBaptiste Daroussin "You specified too many directory components.");
122161d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
122261d06d6bSBaptiste Daroussin }
12237295610fSBaptiste Daroussin req->q.arch = mandoc_strndup(path, end - path);
12247295610fSBaptiste Daroussin if (validate_arch(req->q.arch) == 0) {
122561d06d6bSBaptiste Daroussin pg_error_badrequest(
122661d06d6bSBaptiste Daroussin "You specified an invalid directory component.");
122761d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
122861d06d6bSBaptiste Daroussin }
122961d06d6bSBaptiste Daroussin }
123061d06d6bSBaptiste Daroussin
123161d06d6bSBaptiste Daroussin /*
123261d06d6bSBaptiste Daroussin * Scan for indexable paths.
123361d06d6bSBaptiste Daroussin */
123461d06d6bSBaptiste Daroussin static void
parse_manpath_conf(struct req * req)123561d06d6bSBaptiste Daroussin parse_manpath_conf(struct req *req)
123661d06d6bSBaptiste Daroussin {
123761d06d6bSBaptiste Daroussin FILE *fp;
123861d06d6bSBaptiste Daroussin char *dp;
123961d06d6bSBaptiste Daroussin size_t dpsz;
124061d06d6bSBaptiste Daroussin ssize_t len;
124161d06d6bSBaptiste Daroussin
124261d06d6bSBaptiste Daroussin if ((fp = fopen("manpath.conf", "r")) == NULL) {
124361d06d6bSBaptiste Daroussin warn("%s/manpath.conf", MAN_DIR);
124461d06d6bSBaptiste Daroussin pg_error_internal();
124561d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
124661d06d6bSBaptiste Daroussin }
124761d06d6bSBaptiste Daroussin
124861d06d6bSBaptiste Daroussin dp = NULL;
124961d06d6bSBaptiste Daroussin dpsz = 0;
125061d06d6bSBaptiste Daroussin
125161d06d6bSBaptiste Daroussin while ((len = getline(&dp, &dpsz, fp)) != -1) {
125261d06d6bSBaptiste Daroussin if (dp[len - 1] == '\n')
125361d06d6bSBaptiste Daroussin dp[--len] = '\0';
125461d06d6bSBaptiste Daroussin req->p = mandoc_realloc(req->p,
125561d06d6bSBaptiste Daroussin (req->psz + 1) * sizeof(char *));
125661d06d6bSBaptiste Daroussin if ( ! validate_urifrag(dp)) {
125761d06d6bSBaptiste Daroussin warnx("%s/manpath.conf contains "
125861d06d6bSBaptiste Daroussin "unsafe path \"%s\"", MAN_DIR, dp);
125961d06d6bSBaptiste Daroussin pg_error_internal();
126061d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
126161d06d6bSBaptiste Daroussin }
126261d06d6bSBaptiste Daroussin if (strchr(dp, '/') != NULL) {
126361d06d6bSBaptiste Daroussin warnx("%s/manpath.conf contains "
126461d06d6bSBaptiste Daroussin "path with slash \"%s\"", MAN_DIR, dp);
126561d06d6bSBaptiste Daroussin pg_error_internal();
126661d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
126761d06d6bSBaptiste Daroussin }
126861d06d6bSBaptiste Daroussin req->p[req->psz++] = dp;
126961d06d6bSBaptiste Daroussin dp = NULL;
127061d06d6bSBaptiste Daroussin dpsz = 0;
127161d06d6bSBaptiste Daroussin }
127261d06d6bSBaptiste Daroussin free(dp);
127361d06d6bSBaptiste Daroussin
127461d06d6bSBaptiste Daroussin if (req->p == NULL) {
127561d06d6bSBaptiste Daroussin warnx("%s/manpath.conf is empty", MAN_DIR);
127661d06d6bSBaptiste Daroussin pg_error_internal();
127761d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
127861d06d6bSBaptiste Daroussin }
127961d06d6bSBaptiste Daroussin }
1280