1*c1c95addSBrooks Davis /* $Id: cgi.c,v 1.181 2023/04/28 19:11:03 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*c1c95addSBrooks Davis * Copyright (c) 2014-2019, 2021, 2022 Ingo Schwarze <schwarze@usta.de>
461d06d6bSBaptiste Daroussin * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
5*c1c95addSBrooks Davis * Copyright (c) 2022 Anna Vyalkova <cyber@sysrq.in>
661d06d6bSBaptiste Daroussin *
761d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any
861d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above
961d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies.
1061d06d6bSBaptiste Daroussin *
1161d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1261d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1361d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1461d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1561d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1661d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1761d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
186d38604fSBaptiste Daroussin *
196d38604fSBaptiste Daroussin * Implementation of the man.cgi(8) program.
2061d06d6bSBaptiste Daroussin */
2161d06d6bSBaptiste Daroussin #include "config.h"
2261d06d6bSBaptiste Daroussin
2361d06d6bSBaptiste Daroussin #include <sys/types.h>
2461d06d6bSBaptiste Daroussin #include <sys/time.h>
2561d06d6bSBaptiste Daroussin
2661d06d6bSBaptiste Daroussin #include <ctype.h>
2761d06d6bSBaptiste Daroussin #if HAVE_ERR
2861d06d6bSBaptiste Daroussin #include <err.h>
2961d06d6bSBaptiste Daroussin #endif
3061d06d6bSBaptiste Daroussin #include <errno.h>
3161d06d6bSBaptiste Daroussin #include <fcntl.h>
3261d06d6bSBaptiste Daroussin #include <limits.h>
3361d06d6bSBaptiste Daroussin #include <stdint.h>
3461d06d6bSBaptiste Daroussin #include <stdio.h>
3561d06d6bSBaptiste Daroussin #include <stdlib.h>
3661d06d6bSBaptiste Daroussin #include <string.h>
3761d06d6bSBaptiste Daroussin #include <unistd.h>
3861d06d6bSBaptiste Daroussin
3961d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
4061d06d6bSBaptiste Daroussin #include "mandoc.h"
4161d06d6bSBaptiste Daroussin #include "roff.h"
4261d06d6bSBaptiste Daroussin #include "mdoc.h"
4361d06d6bSBaptiste Daroussin #include "man.h"
447295610fSBaptiste Daroussin #include "mandoc_parse.h"
4561d06d6bSBaptiste Daroussin #include "main.h"
4661d06d6bSBaptiste Daroussin #include "manconf.h"
4761d06d6bSBaptiste Daroussin #include "mansearch.h"
4861d06d6bSBaptiste Daroussin #include "cgi.h"
4961d06d6bSBaptiste Daroussin
5061d06d6bSBaptiste Daroussin /*
5161d06d6bSBaptiste Daroussin * A query as passed to the search function.
5261d06d6bSBaptiste Daroussin */
5361d06d6bSBaptiste Daroussin struct query {
5461d06d6bSBaptiste Daroussin char *manpath; /* desired manual directory */
5561d06d6bSBaptiste Daroussin char *arch; /* architecture */
5661d06d6bSBaptiste Daroussin char *sec; /* manual section */
5761d06d6bSBaptiste Daroussin char *query; /* unparsed query expression */
5861d06d6bSBaptiste Daroussin int equal; /* match whole names, not substrings */
5961d06d6bSBaptiste Daroussin };
6061d06d6bSBaptiste Daroussin
6161d06d6bSBaptiste Daroussin struct req {
6261d06d6bSBaptiste Daroussin struct query q;
6361d06d6bSBaptiste Daroussin char **p; /* array of available manpaths */
6461d06d6bSBaptiste Daroussin size_t psz; /* number of available manpaths */
6561d06d6bSBaptiste Daroussin int isquery; /* QUERY_STRING used, not PATH_INFO */
6661d06d6bSBaptiste Daroussin };
6761d06d6bSBaptiste Daroussin
6861d06d6bSBaptiste Daroussin enum focus {
6961d06d6bSBaptiste Daroussin FOCUS_NONE = 0,
7061d06d6bSBaptiste Daroussin FOCUS_QUERY
7161d06d6bSBaptiste Daroussin };
7261d06d6bSBaptiste Daroussin
7361d06d6bSBaptiste Daroussin static void html_print(const char *);
7461d06d6bSBaptiste Daroussin static void html_putchar(char);
7561d06d6bSBaptiste Daroussin static int http_decode(char *);
766d38604fSBaptiste Daroussin static void http_encode(const char *);
7761d06d6bSBaptiste Daroussin static void parse_manpath_conf(struct req *);
786d38604fSBaptiste Daroussin static void parse_path_info(struct req *, const char *);
7961d06d6bSBaptiste Daroussin static void parse_query_string(struct req *, const char *);
8061d06d6bSBaptiste Daroussin static void pg_error_badrequest(const char *);
8161d06d6bSBaptiste Daroussin static void pg_error_internal(void);
8261d06d6bSBaptiste Daroussin static void pg_index(const struct req *);
836d38604fSBaptiste Daroussin static void pg_noresult(const struct req *, int, const char *,
846d38604fSBaptiste Daroussin const char *);
8561d06d6bSBaptiste Daroussin static void pg_redirect(const struct req *, const char *);
8661d06d6bSBaptiste Daroussin static void pg_search(const struct req *);
8761d06d6bSBaptiste Daroussin static void pg_searchres(const struct req *,
8861d06d6bSBaptiste Daroussin struct manpage *, size_t);
8961d06d6bSBaptiste Daroussin static void pg_show(struct req *, const char *);
90*c1c95addSBrooks Davis static int resp_begin_html(int, const char *, const char *);
9161d06d6bSBaptiste Daroussin static void resp_begin_http(int, const char *);
9261d06d6bSBaptiste Daroussin static void resp_catman(const struct req *, const char *);
93*c1c95addSBrooks Davis static int resp_copy(const char *, const char *);
9461d06d6bSBaptiste Daroussin static void resp_end_html(void);
9561d06d6bSBaptiste Daroussin static void resp_format(const struct req *, const char *);
9661d06d6bSBaptiste Daroussin static void resp_searchform(const struct req *, enum focus);
9761d06d6bSBaptiste Daroussin static void resp_show(const struct req *, const char *);
9861d06d6bSBaptiste Daroussin static void set_query_attr(char **, char **);
997295610fSBaptiste Daroussin static int validate_arch(const char *);
10061d06d6bSBaptiste Daroussin static int validate_filename(const char *);
10161d06d6bSBaptiste Daroussin static int validate_manpath(const struct req *, const char *);
10261d06d6bSBaptiste Daroussin static int validate_urifrag(const char *);
10361d06d6bSBaptiste Daroussin
10461d06d6bSBaptiste Daroussin static const char *scriptname = SCRIPT_NAME;
10561d06d6bSBaptiste Daroussin
10661d06d6bSBaptiste Daroussin static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
10761d06d6bSBaptiste Daroussin static const char *const sec_numbers[] = {
10861d06d6bSBaptiste Daroussin "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9"
10961d06d6bSBaptiste Daroussin };
11061d06d6bSBaptiste Daroussin static const char *const sec_names[] = {
11161d06d6bSBaptiste Daroussin "All Sections",
11261d06d6bSBaptiste Daroussin "1 - General Commands",
11361d06d6bSBaptiste Daroussin "2 - System Calls",
11461d06d6bSBaptiste Daroussin "3 - Library Functions",
11561d06d6bSBaptiste Daroussin "3p - Perl Library",
11661d06d6bSBaptiste Daroussin "4 - Device Drivers",
11761d06d6bSBaptiste Daroussin "5 - File Formats",
11861d06d6bSBaptiste Daroussin "6 - Games",
11961d06d6bSBaptiste Daroussin "7 - Miscellaneous Information",
12061d06d6bSBaptiste Daroussin "8 - System Manager\'s Manual",
12161d06d6bSBaptiste Daroussin "9 - Kernel Developer\'s Manual"
12261d06d6bSBaptiste Daroussin };
12361d06d6bSBaptiste Daroussin static const int sec_MAX = sizeof(sec_names) / sizeof(char *);
12461d06d6bSBaptiste Daroussin
12561d06d6bSBaptiste Daroussin static const char *const arch_names[] = {
12661d06d6bSBaptiste Daroussin "amd64", "alpha", "armv7", "arm64",
1276d38604fSBaptiste Daroussin "hppa", "i386", "landisk", "loongson",
1286d38604fSBaptiste Daroussin "luna88k", "macppc", "mips64", "octeon",
1296d38604fSBaptiste Daroussin "powerpc64", "riscv64", "sparc64",
1306d38604fSBaptiste Daroussin
13161d06d6bSBaptiste Daroussin "amiga", "arc", "armish", "arm32",
13261d06d6bSBaptiste Daroussin "atari", "aviion", "beagle", "cats",
13361d06d6bSBaptiste Daroussin "hppa64", "hp300",
13461d06d6bSBaptiste Daroussin "ia64", "mac68k", "mvme68k", "mvme88k",
13561d06d6bSBaptiste Daroussin "mvmeppc", "palm", "pc532", "pegasos",
1366d38604fSBaptiste Daroussin "pmax", "powerpc", "sgi", "socppc",
1376d38604fSBaptiste Daroussin "solbourne", "sparc",
13861d06d6bSBaptiste Daroussin "sun3", "vax", "wgrisc", "x68k",
13961d06d6bSBaptiste Daroussin "zaurus"
14061d06d6bSBaptiste Daroussin };
14161d06d6bSBaptiste Daroussin static const int arch_MAX = sizeof(arch_names) / sizeof(char *);
14261d06d6bSBaptiste Daroussin
14361d06d6bSBaptiste Daroussin /*
14461d06d6bSBaptiste Daroussin * Print a character, escaping HTML along the way.
14561d06d6bSBaptiste Daroussin * This will pass non-ASCII straight to output: be warned!
14661d06d6bSBaptiste Daroussin */
14761d06d6bSBaptiste Daroussin static void
html_putchar(char c)14861d06d6bSBaptiste Daroussin html_putchar(char c)
14961d06d6bSBaptiste Daroussin {
15061d06d6bSBaptiste Daroussin
15161d06d6bSBaptiste Daroussin switch (c) {
15261d06d6bSBaptiste Daroussin case '"':
15361d06d6bSBaptiste Daroussin printf(""");
15461d06d6bSBaptiste Daroussin break;
15561d06d6bSBaptiste Daroussin case '&':
15661d06d6bSBaptiste Daroussin printf("&");
15761d06d6bSBaptiste Daroussin break;
15861d06d6bSBaptiste Daroussin case '>':
15961d06d6bSBaptiste Daroussin printf(">");
16061d06d6bSBaptiste Daroussin break;
16161d06d6bSBaptiste Daroussin case '<':
16261d06d6bSBaptiste Daroussin printf("<");
16361d06d6bSBaptiste Daroussin break;
16461d06d6bSBaptiste Daroussin default:
16561d06d6bSBaptiste Daroussin putchar((unsigned char)c);
16661d06d6bSBaptiste Daroussin break;
16761d06d6bSBaptiste Daroussin }
16861d06d6bSBaptiste Daroussin }
16961d06d6bSBaptiste Daroussin
17061d06d6bSBaptiste Daroussin /*
17161d06d6bSBaptiste Daroussin * Call through to html_putchar().
17261d06d6bSBaptiste Daroussin * Accepts NULL strings.
17361d06d6bSBaptiste Daroussin */
17461d06d6bSBaptiste Daroussin static void
html_print(const char * p)17561d06d6bSBaptiste Daroussin html_print(const char *p)
17661d06d6bSBaptiste Daroussin {
17761d06d6bSBaptiste Daroussin
17861d06d6bSBaptiste Daroussin if (NULL == p)
17961d06d6bSBaptiste Daroussin return;
18061d06d6bSBaptiste Daroussin while ('\0' != *p)
18161d06d6bSBaptiste Daroussin html_putchar(*p++);
18261d06d6bSBaptiste Daroussin }
18361d06d6bSBaptiste Daroussin
18461d06d6bSBaptiste Daroussin /*
18561d06d6bSBaptiste Daroussin * Transfer the responsibility for the allocated string *val
18661d06d6bSBaptiste Daroussin * to the query structure.
18761d06d6bSBaptiste Daroussin */
18861d06d6bSBaptiste Daroussin static void
set_query_attr(char ** attr,char ** val)18961d06d6bSBaptiste Daroussin set_query_attr(char **attr, char **val)
19061d06d6bSBaptiste Daroussin {
19161d06d6bSBaptiste Daroussin
19261d06d6bSBaptiste Daroussin free(*attr);
19361d06d6bSBaptiste Daroussin if (**val == '\0') {
19461d06d6bSBaptiste Daroussin *attr = NULL;
19561d06d6bSBaptiste Daroussin free(*val);
19661d06d6bSBaptiste Daroussin } else
19761d06d6bSBaptiste Daroussin *attr = *val;
19861d06d6bSBaptiste Daroussin *val = NULL;
19961d06d6bSBaptiste Daroussin }
20061d06d6bSBaptiste Daroussin
20161d06d6bSBaptiste Daroussin /*
20261d06d6bSBaptiste Daroussin * Parse the QUERY_STRING for key-value pairs
20361d06d6bSBaptiste Daroussin * and store the values into the query structure.
20461d06d6bSBaptiste Daroussin */
20561d06d6bSBaptiste Daroussin static void
parse_query_string(struct req * req,const char * qs)20661d06d6bSBaptiste Daroussin parse_query_string(struct req *req, const char *qs)
20761d06d6bSBaptiste Daroussin {
20861d06d6bSBaptiste Daroussin char *key, *val;
20961d06d6bSBaptiste Daroussin size_t keysz, valsz;
21061d06d6bSBaptiste Daroussin
21161d06d6bSBaptiste Daroussin req->isquery = 1;
21261d06d6bSBaptiste Daroussin req->q.manpath = NULL;
21361d06d6bSBaptiste Daroussin req->q.arch = NULL;
21461d06d6bSBaptiste Daroussin req->q.sec = NULL;
21561d06d6bSBaptiste Daroussin req->q.query = NULL;
21661d06d6bSBaptiste Daroussin req->q.equal = 1;
21761d06d6bSBaptiste Daroussin
21861d06d6bSBaptiste Daroussin key = val = NULL;
21961d06d6bSBaptiste Daroussin while (*qs != '\0') {
22061d06d6bSBaptiste Daroussin
22161d06d6bSBaptiste Daroussin /* Parse one key. */
22261d06d6bSBaptiste Daroussin
22361d06d6bSBaptiste Daroussin keysz = strcspn(qs, "=;&");
22461d06d6bSBaptiste Daroussin key = mandoc_strndup(qs, keysz);
22561d06d6bSBaptiste Daroussin qs += keysz;
22661d06d6bSBaptiste Daroussin if (*qs != '=')
22761d06d6bSBaptiste Daroussin goto next;
22861d06d6bSBaptiste Daroussin
22961d06d6bSBaptiste Daroussin /* Parse one value. */
23061d06d6bSBaptiste Daroussin
23161d06d6bSBaptiste Daroussin valsz = strcspn(++qs, ";&");
23261d06d6bSBaptiste Daroussin val = mandoc_strndup(qs, valsz);
23361d06d6bSBaptiste Daroussin qs += valsz;
23461d06d6bSBaptiste Daroussin
23561d06d6bSBaptiste Daroussin /* Decode and catch encoding errors. */
23661d06d6bSBaptiste Daroussin
23761d06d6bSBaptiste Daroussin if ( ! (http_decode(key) && http_decode(val)))
23861d06d6bSBaptiste Daroussin goto next;
23961d06d6bSBaptiste Daroussin
24061d06d6bSBaptiste Daroussin /* Handle key-value pairs. */
24161d06d6bSBaptiste Daroussin
24261d06d6bSBaptiste Daroussin if ( ! strcmp(key, "query"))
24361d06d6bSBaptiste Daroussin set_query_attr(&req->q.query, &val);
24461d06d6bSBaptiste Daroussin
24561d06d6bSBaptiste Daroussin else if ( ! strcmp(key, "apropos"))
24661d06d6bSBaptiste Daroussin req->q.equal = !strcmp(val, "0");
24761d06d6bSBaptiste Daroussin
24861d06d6bSBaptiste Daroussin else if ( ! strcmp(key, "manpath")) {
24961d06d6bSBaptiste Daroussin #ifdef COMPAT_OLDURI
25061d06d6bSBaptiste Daroussin if ( ! strncmp(val, "OpenBSD ", 8)) {
25161d06d6bSBaptiste Daroussin val[7] = '-';
25261d06d6bSBaptiste Daroussin if ('C' == val[8])
25361d06d6bSBaptiste Daroussin val[8] = 'c';
25461d06d6bSBaptiste Daroussin }
25561d06d6bSBaptiste Daroussin #endif
25661d06d6bSBaptiste Daroussin set_query_attr(&req->q.manpath, &val);
25761d06d6bSBaptiste Daroussin }
25861d06d6bSBaptiste Daroussin
25961d06d6bSBaptiste Daroussin else if ( ! (strcmp(key, "sec")
26061d06d6bSBaptiste Daroussin #ifdef COMPAT_OLDURI
26161d06d6bSBaptiste Daroussin && strcmp(key, "sektion")
26261d06d6bSBaptiste Daroussin #endif
26361d06d6bSBaptiste Daroussin )) {
26461d06d6bSBaptiste Daroussin if ( ! strcmp(val, "0"))
26561d06d6bSBaptiste Daroussin *val = '\0';
26661d06d6bSBaptiste Daroussin set_query_attr(&req->q.sec, &val);
26761d06d6bSBaptiste Daroussin }
26861d06d6bSBaptiste Daroussin
26961d06d6bSBaptiste Daroussin else if ( ! strcmp(key, "arch")) {
27061d06d6bSBaptiste Daroussin if ( ! strcmp(val, "default"))
27161d06d6bSBaptiste Daroussin *val = '\0';
27261d06d6bSBaptiste Daroussin set_query_attr(&req->q.arch, &val);
27361d06d6bSBaptiste Daroussin }
27461d06d6bSBaptiste Daroussin
27561d06d6bSBaptiste Daroussin /*
27661d06d6bSBaptiste Daroussin * The key must be freed in any case.
27761d06d6bSBaptiste Daroussin * The val may have been handed over to the query
27861d06d6bSBaptiste Daroussin * structure, in which case it is now NULL.
27961d06d6bSBaptiste Daroussin */
28061d06d6bSBaptiste Daroussin next:
28161d06d6bSBaptiste Daroussin free(key);
28261d06d6bSBaptiste Daroussin key = NULL;
28361d06d6bSBaptiste Daroussin free(val);
28461d06d6bSBaptiste Daroussin val = NULL;
28561d06d6bSBaptiste Daroussin
28661d06d6bSBaptiste Daroussin if (*qs != '\0')
28761d06d6bSBaptiste Daroussin qs++;
28861d06d6bSBaptiste Daroussin }
28961d06d6bSBaptiste Daroussin }
29061d06d6bSBaptiste Daroussin
29161d06d6bSBaptiste Daroussin /*
29261d06d6bSBaptiste Daroussin * HTTP-decode a string. The standard explanation is that this turns
29361d06d6bSBaptiste Daroussin * "%4e+foo" into "n foo" in the regular way. This is done in-place
29461d06d6bSBaptiste Daroussin * over the allocated string.
29561d06d6bSBaptiste Daroussin */
29661d06d6bSBaptiste Daroussin static int
http_decode(char * p)29761d06d6bSBaptiste Daroussin http_decode(char *p)
29861d06d6bSBaptiste Daroussin {
29961d06d6bSBaptiste Daroussin char hex[3];
30061d06d6bSBaptiste Daroussin char *q;
30161d06d6bSBaptiste Daroussin int c;
30261d06d6bSBaptiste Daroussin
30361d06d6bSBaptiste Daroussin hex[2] = '\0';
30461d06d6bSBaptiste Daroussin
30561d06d6bSBaptiste Daroussin q = p;
30661d06d6bSBaptiste Daroussin for ( ; '\0' != *p; p++, q++) {
30761d06d6bSBaptiste Daroussin if ('%' == *p) {
30861d06d6bSBaptiste Daroussin if ('\0' == (hex[0] = *(p + 1)))
30961d06d6bSBaptiste Daroussin return 0;
31061d06d6bSBaptiste Daroussin if ('\0' == (hex[1] = *(p + 2)))
31161d06d6bSBaptiste Daroussin return 0;
31261d06d6bSBaptiste Daroussin if (1 != sscanf(hex, "%x", &c))
31361d06d6bSBaptiste Daroussin return 0;
31461d06d6bSBaptiste Daroussin if ('\0' == c)
31561d06d6bSBaptiste Daroussin return 0;
31661d06d6bSBaptiste Daroussin
31761d06d6bSBaptiste Daroussin *q = (char)c;
31861d06d6bSBaptiste Daroussin p += 2;
31961d06d6bSBaptiste Daroussin } else
32061d06d6bSBaptiste Daroussin *q = '+' == *p ? ' ' : *p;
32161d06d6bSBaptiste Daroussin }
32261d06d6bSBaptiste Daroussin
32361d06d6bSBaptiste Daroussin *q = '\0';
32461d06d6bSBaptiste Daroussin return 1;
32561d06d6bSBaptiste Daroussin }
32661d06d6bSBaptiste Daroussin
32761d06d6bSBaptiste Daroussin static void
http_encode(const char * p)3287295610fSBaptiste Daroussin http_encode(const char *p)
3297295610fSBaptiste Daroussin {
3307295610fSBaptiste Daroussin for (; *p != '\0'; p++) {
3317295610fSBaptiste Daroussin if (isalnum((unsigned char)*p) == 0 &&
3327295610fSBaptiste Daroussin strchr("-._~", *p) == NULL)
3337295610fSBaptiste Daroussin printf("%%%2.2X", (unsigned char)*p);
3347295610fSBaptiste Daroussin else
3357295610fSBaptiste Daroussin putchar(*p);
3367295610fSBaptiste Daroussin }
3377295610fSBaptiste Daroussin }
3387295610fSBaptiste Daroussin
3397295610fSBaptiste Daroussin static void
resp_begin_http(int code,const char * msg)34061d06d6bSBaptiste Daroussin resp_begin_http(int code, const char *msg)
34161d06d6bSBaptiste Daroussin {
34261d06d6bSBaptiste Daroussin
34361d06d6bSBaptiste Daroussin if (200 != code)
34461d06d6bSBaptiste Daroussin printf("Status: %d %s\r\n", code, msg);
34561d06d6bSBaptiste Daroussin
34661d06d6bSBaptiste Daroussin printf("Content-Type: text/html; charset=utf-8\r\n"
34761d06d6bSBaptiste Daroussin "Cache-Control: no-cache\r\n"
3486d38604fSBaptiste Daroussin "Content-Security-Policy: default-src 'none'; "
3496d38604fSBaptiste Daroussin "style-src 'self' 'unsafe-inline'\r\n"
35061d06d6bSBaptiste Daroussin "Pragma: no-cache\r\n"
35161d06d6bSBaptiste Daroussin "\r\n");
35261d06d6bSBaptiste Daroussin
35361d06d6bSBaptiste Daroussin fflush(stdout);
35461d06d6bSBaptiste Daroussin }
35561d06d6bSBaptiste Daroussin
356*c1c95addSBrooks Davis static int
resp_copy(const char * element,const char * filename)357*c1c95addSBrooks Davis resp_copy(const char *element, const char *filename)
35861d06d6bSBaptiste Daroussin {
35961d06d6bSBaptiste Daroussin char buf[4096];
36061d06d6bSBaptiste Daroussin ssize_t sz;
36161d06d6bSBaptiste Daroussin int fd;
36261d06d6bSBaptiste Daroussin
363*c1c95addSBrooks Davis if ((fd = open(filename, O_RDONLY)) == -1)
364*c1c95addSBrooks Davis return 0;
365*c1c95addSBrooks Davis
366*c1c95addSBrooks Davis if (element != NULL)
367*c1c95addSBrooks Davis printf("<%s>\n", element);
36861d06d6bSBaptiste Daroussin fflush(stdout);
36961d06d6bSBaptiste Daroussin while ((sz = read(fd, buf, sizeof(buf))) > 0)
37061d06d6bSBaptiste Daroussin write(STDOUT_FILENO, buf, sz);
37161d06d6bSBaptiste Daroussin close(fd);
372*c1c95addSBrooks Davis return 1;
37361d06d6bSBaptiste Daroussin }
37461d06d6bSBaptiste Daroussin
375*c1c95addSBrooks Davis static int
resp_begin_html(int code,const char * msg,const char * file)37661d06d6bSBaptiste Daroussin resp_begin_html(int code, const char *msg, const char *file)
37761d06d6bSBaptiste Daroussin {
3786d38604fSBaptiste Daroussin const char *name, *sec, *cp;
3796d38604fSBaptiste Daroussin int namesz, secsz;
38061d06d6bSBaptiste Daroussin
38161d06d6bSBaptiste Daroussin resp_begin_http(code, msg);
38261d06d6bSBaptiste Daroussin
38361d06d6bSBaptiste Daroussin printf("<!DOCTYPE html>\n"
38461d06d6bSBaptiste Daroussin "<html>\n"
38561d06d6bSBaptiste Daroussin "<head>\n"
38661d06d6bSBaptiste Daroussin " <meta charset=\"UTF-8\"/>\n"
38761d06d6bSBaptiste Daroussin " <meta name=\"viewport\""
38861d06d6bSBaptiste Daroussin " content=\"width=device-width, initial-scale=1.0\">\n"
38961d06d6bSBaptiste Daroussin " <link rel=\"stylesheet\" href=\"%s/mandoc.css\""
39061d06d6bSBaptiste Daroussin " type=\"text/css\" media=\"all\">\n"
39161d06d6bSBaptiste Daroussin " <title>",
39261d06d6bSBaptiste Daroussin CSS_DIR);
39361d06d6bSBaptiste Daroussin if (file != NULL) {
3946d38604fSBaptiste Daroussin cp = strrchr(file, '/');
3956d38604fSBaptiste Daroussin name = cp == NULL ? file : cp + 1;
3966d38604fSBaptiste Daroussin cp = strrchr(name, '.');
3976d38604fSBaptiste Daroussin namesz = cp == NULL ? strlen(name) : cp - name;
3986d38604fSBaptiste Daroussin sec = NULL;
3996d38604fSBaptiste Daroussin if (cp != NULL && cp[1] != '0') {
4006d38604fSBaptiste Daroussin sec = cp + 1;
4016d38604fSBaptiste Daroussin secsz = strlen(sec);
4026d38604fSBaptiste Daroussin } else if (name - file > 1) {
4036d38604fSBaptiste Daroussin for (cp = name - 2; cp >= file; cp--) {
4046d38604fSBaptiste Daroussin if (*cp < '1' || *cp > '9')
4056d38604fSBaptiste Daroussin continue;
4066d38604fSBaptiste Daroussin sec = cp;
4076d38604fSBaptiste Daroussin secsz = name - cp - 1;
4086d38604fSBaptiste Daroussin break;
4096d38604fSBaptiste Daroussin }
4106d38604fSBaptiste Daroussin }
4116d38604fSBaptiste Daroussin printf("%.*s", namesz, name);
4126d38604fSBaptiste Daroussin if (sec != NULL)
4136d38604fSBaptiste Daroussin printf("(%.*s)", secsz, sec);
4146d38604fSBaptiste Daroussin fputs(" - ", stdout);
41561d06d6bSBaptiste Daroussin }
41661d06d6bSBaptiste Daroussin printf("%s</title>\n"
41761d06d6bSBaptiste Daroussin "</head>\n"
41861d06d6bSBaptiste Daroussin "<body>\n",
41961d06d6bSBaptiste Daroussin CUSTOMIZE_TITLE);
42061d06d6bSBaptiste Daroussin
421*c1c95addSBrooks Davis return resp_copy("header", MAN_DIR "/header.html");
42261d06d6bSBaptiste Daroussin }
42361d06d6bSBaptiste Daroussin
42461d06d6bSBaptiste Daroussin static void
resp_end_html(void)42561d06d6bSBaptiste Daroussin resp_end_html(void)
42661d06d6bSBaptiste Daroussin {
427*c1c95addSBrooks Davis if (resp_copy("footer", MAN_DIR "/footer.html"))
428*c1c95addSBrooks Davis puts("</footer>");
42961d06d6bSBaptiste Daroussin
43061d06d6bSBaptiste Daroussin puts("</body>\n"
43161d06d6bSBaptiste Daroussin "</html>");
43261d06d6bSBaptiste Daroussin }
43361d06d6bSBaptiste Daroussin
43461d06d6bSBaptiste Daroussin static void
resp_searchform(const struct req * req,enum focus focus)43561d06d6bSBaptiste Daroussin resp_searchform(const struct req *req, enum focus focus)
43661d06d6bSBaptiste Daroussin {
43761d06d6bSBaptiste Daroussin int i;
43861d06d6bSBaptiste Daroussin
439*c1c95addSBrooks Davis printf("<form role=\"search\" action=\"/%s\" method=\"get\" "
4406d38604fSBaptiste Daroussin "autocomplete=\"off\" autocapitalize=\"none\">\n"
44161d06d6bSBaptiste Daroussin " <fieldset>\n"
44261d06d6bSBaptiste Daroussin " <legend>Manual Page Search Parameters</legend>\n",
44361d06d6bSBaptiste Daroussin scriptname);
44461d06d6bSBaptiste Daroussin
44561d06d6bSBaptiste Daroussin /* Write query input box. */
44661d06d6bSBaptiste Daroussin
447*c1c95addSBrooks Davis printf(" <label>Search query:\n"
448*c1c95addSBrooks Davis " <input type=\"search\" name=\"query\" value=\"");
44961d06d6bSBaptiste Daroussin if (req->q.query != NULL)
45061d06d6bSBaptiste Daroussin html_print(req->q.query);
45161d06d6bSBaptiste Daroussin printf("\" size=\"40\"");
45261d06d6bSBaptiste Daroussin if (focus == FOCUS_QUERY)
45361d06d6bSBaptiste Daroussin printf(" autofocus");
454*c1c95addSBrooks Davis puts(">\n </label>");
45561d06d6bSBaptiste Daroussin
45661d06d6bSBaptiste Daroussin /* Write submission buttons. */
45761d06d6bSBaptiste Daroussin
45861d06d6bSBaptiste Daroussin printf( " <button type=\"submit\" name=\"apropos\" value=\"0\">"
45961d06d6bSBaptiste Daroussin "man</button>\n"
46061d06d6bSBaptiste Daroussin " <button type=\"submit\" name=\"apropos\" value=\"1\">"
46161d06d6bSBaptiste Daroussin "apropos</button>\n"
46261d06d6bSBaptiste Daroussin " <br/>\n");
46361d06d6bSBaptiste Daroussin
46461d06d6bSBaptiste Daroussin /* Write section selector. */
46561d06d6bSBaptiste Daroussin
466*c1c95addSBrooks Davis puts(" <select name=\"sec\" aria-label=\"Manual section\">");
46761d06d6bSBaptiste Daroussin for (i = 0; i < sec_MAX; i++) {
46861d06d6bSBaptiste Daroussin printf(" <option value=\"%s\"", sec_numbers[i]);
46961d06d6bSBaptiste Daroussin if (NULL != req->q.sec &&
47061d06d6bSBaptiste Daroussin 0 == strcmp(sec_numbers[i], req->q.sec))
47161d06d6bSBaptiste Daroussin printf(" selected=\"selected\"");
47261d06d6bSBaptiste Daroussin printf(">%s</option>\n", sec_names[i]);
47361d06d6bSBaptiste Daroussin }
47461d06d6bSBaptiste Daroussin puts(" </select>");
47561d06d6bSBaptiste Daroussin
47661d06d6bSBaptiste Daroussin /* Write architecture selector. */
47761d06d6bSBaptiste Daroussin
478*c1c95addSBrooks Davis printf( " <select name=\"arch\" aria-label=\"CPU architecture\">\n"
47961d06d6bSBaptiste Daroussin " <option value=\"default\"");
48061d06d6bSBaptiste Daroussin if (NULL == req->q.arch)
48161d06d6bSBaptiste Daroussin printf(" selected=\"selected\"");
48261d06d6bSBaptiste Daroussin puts(">All Architectures</option>");
48361d06d6bSBaptiste Daroussin for (i = 0; i < arch_MAX; i++) {
48461d06d6bSBaptiste Daroussin printf(" <option");
48561d06d6bSBaptiste Daroussin if (NULL != req->q.arch &&
48661d06d6bSBaptiste Daroussin 0 == strcmp(arch_names[i], req->q.arch))
48761d06d6bSBaptiste Daroussin printf(" selected=\"selected\"");
48861d06d6bSBaptiste Daroussin printf(">%s</option>\n", arch_names[i]);
48961d06d6bSBaptiste Daroussin }
49061d06d6bSBaptiste Daroussin puts(" </select>");
49161d06d6bSBaptiste Daroussin
49261d06d6bSBaptiste Daroussin /* Write manpath selector. */
49361d06d6bSBaptiste Daroussin
49461d06d6bSBaptiste Daroussin if (req->psz > 1) {
495*c1c95addSBrooks Davis puts(" <select name=\"manpath\""
496*c1c95addSBrooks Davis " aria-label=\"Manual path\">");
49761d06d6bSBaptiste Daroussin for (i = 0; i < (int)req->psz; i++) {
49861d06d6bSBaptiste Daroussin printf(" <option");
49961d06d6bSBaptiste Daroussin if (strcmp(req->q.manpath, req->p[i]) == 0)
50061d06d6bSBaptiste Daroussin printf(" selected=\"selected\"");
50161d06d6bSBaptiste Daroussin printf(">");
50261d06d6bSBaptiste Daroussin html_print(req->p[i]);
50361d06d6bSBaptiste Daroussin puts("</option>");
50461d06d6bSBaptiste Daroussin }
50561d06d6bSBaptiste Daroussin puts(" </select>");
50661d06d6bSBaptiste Daroussin }
50761d06d6bSBaptiste Daroussin
50861d06d6bSBaptiste Daroussin puts(" </fieldset>\n"
50961d06d6bSBaptiste Daroussin "</form>");
51061d06d6bSBaptiste Daroussin }
51161d06d6bSBaptiste Daroussin
51261d06d6bSBaptiste Daroussin static int
validate_urifrag(const char * frag)51361d06d6bSBaptiste Daroussin validate_urifrag(const char *frag)
51461d06d6bSBaptiste Daroussin {
51561d06d6bSBaptiste Daroussin
51661d06d6bSBaptiste Daroussin while ('\0' != *frag) {
51761d06d6bSBaptiste Daroussin if ( ! (isalnum((unsigned char)*frag) ||
51861d06d6bSBaptiste Daroussin '-' == *frag || '.' == *frag ||
51961d06d6bSBaptiste Daroussin '/' == *frag || '_' == *frag))
52061d06d6bSBaptiste Daroussin return 0;
52161d06d6bSBaptiste Daroussin frag++;
52261d06d6bSBaptiste Daroussin }
52361d06d6bSBaptiste Daroussin return 1;
52461d06d6bSBaptiste Daroussin }
52561d06d6bSBaptiste Daroussin
52661d06d6bSBaptiste Daroussin static int
validate_manpath(const struct req * req,const char * manpath)52761d06d6bSBaptiste Daroussin validate_manpath(const struct req *req, const char* manpath)
52861d06d6bSBaptiste Daroussin {
52961d06d6bSBaptiste Daroussin size_t i;
53061d06d6bSBaptiste Daroussin
53161d06d6bSBaptiste Daroussin for (i = 0; i < req->psz; i++)
53261d06d6bSBaptiste Daroussin if ( ! strcmp(manpath, req->p[i]))
53361d06d6bSBaptiste Daroussin return 1;
53461d06d6bSBaptiste Daroussin
53561d06d6bSBaptiste Daroussin return 0;
53661d06d6bSBaptiste Daroussin }
53761d06d6bSBaptiste Daroussin
53861d06d6bSBaptiste Daroussin static int
validate_arch(const char * arch)5397295610fSBaptiste Daroussin validate_arch(const char *arch)
5407295610fSBaptiste Daroussin {
5417295610fSBaptiste Daroussin int i;
5427295610fSBaptiste Daroussin
5437295610fSBaptiste Daroussin for (i = 0; i < arch_MAX; i++)
5447295610fSBaptiste Daroussin if (strcmp(arch, arch_names[i]) == 0)
5457295610fSBaptiste Daroussin return 1;
5467295610fSBaptiste Daroussin
5477295610fSBaptiste Daroussin return 0;
5487295610fSBaptiste Daroussin }
5497295610fSBaptiste Daroussin
5507295610fSBaptiste Daroussin static int
validate_filename(const char * file)55161d06d6bSBaptiste Daroussin validate_filename(const char *file)
55261d06d6bSBaptiste Daroussin {
55361d06d6bSBaptiste Daroussin
55461d06d6bSBaptiste Daroussin if ('.' == file[0] && '/' == file[1])
55561d06d6bSBaptiste Daroussin file += 2;
55661d06d6bSBaptiste Daroussin
55761d06d6bSBaptiste Daroussin return ! (strstr(file, "../") || strstr(file, "/..") ||
55861d06d6bSBaptiste Daroussin (strncmp(file, "man", 3) && strncmp(file, "cat", 3)));
55961d06d6bSBaptiste Daroussin }
56061d06d6bSBaptiste Daroussin
56161d06d6bSBaptiste Daroussin static void
pg_index(const struct req * req)56261d06d6bSBaptiste Daroussin pg_index(const struct req *req)
56361d06d6bSBaptiste Daroussin {
564*c1c95addSBrooks Davis if (resp_begin_html(200, NULL, NULL) == 0)
565*c1c95addSBrooks Davis puts("<header>");
56661d06d6bSBaptiste Daroussin resp_searchform(req, FOCUS_QUERY);
567*c1c95addSBrooks Davis printf("</header>\n"
568*c1c95addSBrooks Davis "<main>\n"
569*c1c95addSBrooks Davis "<p role=\"doc-notice\" aria-label=\"Usage\">\n"
57061d06d6bSBaptiste Daroussin "This web interface is documented in the\n"
571*c1c95addSBrooks Davis "<a class=\"Xr\" href=\"/%s%sman.cgi.8\""
572*c1c95addSBrooks Davis " aria-label=\"man dot CGI, section 8\">man.cgi(8)</a>\n"
57361d06d6bSBaptiste Daroussin "manual, and the\n"
574*c1c95addSBrooks Davis "<a class=\"Xr\" href=\"/%s%sapropos.1\""
575*c1c95addSBrooks Davis " aria-label=\"apropos, section 1\">apropos(1)</a>\n"
57661d06d6bSBaptiste Daroussin "manual explains the query syntax.\n"
577*c1c95addSBrooks Davis "</p>\n"
578*c1c95addSBrooks Davis "</main>\n",
57961d06d6bSBaptiste Daroussin scriptname, *scriptname == '\0' ? "" : "/",
58061d06d6bSBaptiste Daroussin scriptname, *scriptname == '\0' ? "" : "/");
58161d06d6bSBaptiste Daroussin resp_end_html();
58261d06d6bSBaptiste Daroussin }
58361d06d6bSBaptiste Daroussin
58461d06d6bSBaptiste Daroussin static void
pg_noresult(const struct req * req,int code,const char * http_msg,const char * user_msg)5856d38604fSBaptiste Daroussin pg_noresult(const struct req *req, int code, const char *http_msg,
5866d38604fSBaptiste Daroussin const char *user_msg)
58761d06d6bSBaptiste Daroussin {
588*c1c95addSBrooks Davis if (resp_begin_html(code, http_msg, NULL) == 0)
589*c1c95addSBrooks Davis puts("<header>");
59061d06d6bSBaptiste Daroussin resp_searchform(req, FOCUS_QUERY);
591*c1c95addSBrooks Davis puts("</header>");
592*c1c95addSBrooks Davis puts("<main>");
593*c1c95addSBrooks Davis puts("<p role=\"doc-notice\" aria-label=\"No result\">");
5946d38604fSBaptiste Daroussin puts(user_msg);
59561d06d6bSBaptiste Daroussin puts("</p>");
596*c1c95addSBrooks Davis puts("</main>");
59761d06d6bSBaptiste Daroussin resp_end_html();
59861d06d6bSBaptiste Daroussin }
59961d06d6bSBaptiste Daroussin
60061d06d6bSBaptiste Daroussin static void
pg_error_badrequest(const char * msg)60161d06d6bSBaptiste Daroussin pg_error_badrequest(const char *msg)
60261d06d6bSBaptiste Daroussin {
603*c1c95addSBrooks Davis if (resp_begin_html(400, "Bad Request", NULL))
604*c1c95addSBrooks Davis puts("</header>");
605*c1c95addSBrooks Davis puts("<main>\n"
606*c1c95addSBrooks Davis "<h1>Bad Request</h1>\n"
607*c1c95addSBrooks Davis "<p role=\"doc-notice\" aria-label=\"Bad Request\">");
60861d06d6bSBaptiste Daroussin puts(msg);
60961d06d6bSBaptiste Daroussin printf("Try again from the\n"
61061d06d6bSBaptiste Daroussin "<a href=\"/%s\">main page</a>.\n"
611*c1c95addSBrooks Davis "</p>\n"
612*c1c95addSBrooks Davis "</main>\n", scriptname);
61361d06d6bSBaptiste Daroussin resp_end_html();
61461d06d6bSBaptiste Daroussin }
61561d06d6bSBaptiste Daroussin
61661d06d6bSBaptiste Daroussin static void
pg_error_internal(void)61761d06d6bSBaptiste Daroussin pg_error_internal(void)
61861d06d6bSBaptiste Daroussin {
619*c1c95addSBrooks Davis if (resp_begin_html(500, "Internal Server Error", NULL))
620*c1c95addSBrooks Davis puts("</header>");
621*c1c95addSBrooks Davis puts("<main><p role=\"doc-notice\">Internal Server Error</p></main>");
62261d06d6bSBaptiste Daroussin resp_end_html();
62361d06d6bSBaptiste Daroussin }
62461d06d6bSBaptiste Daroussin
62561d06d6bSBaptiste Daroussin static void
pg_redirect(const struct req * req,const char * name)62661d06d6bSBaptiste Daroussin pg_redirect(const struct req *req, const char *name)
62761d06d6bSBaptiste Daroussin {
62861d06d6bSBaptiste Daroussin printf("Status: 303 See Other\r\n"
62961d06d6bSBaptiste Daroussin "Location: /");
63061d06d6bSBaptiste Daroussin if (*scriptname != '\0')
63161d06d6bSBaptiste Daroussin printf("%s/", scriptname);
63261d06d6bSBaptiste Daroussin if (strcmp(req->q.manpath, req->p[0]))
63361d06d6bSBaptiste Daroussin printf("%s/", req->q.manpath);
63461d06d6bSBaptiste Daroussin if (req->q.arch != NULL)
63561d06d6bSBaptiste Daroussin printf("%s/", req->q.arch);
6367295610fSBaptiste Daroussin http_encode(name);
6377295610fSBaptiste Daroussin if (req->q.sec != NULL) {
6387295610fSBaptiste Daroussin putchar('.');
6397295610fSBaptiste Daroussin http_encode(req->q.sec);
6407295610fSBaptiste Daroussin }
64161d06d6bSBaptiste Daroussin printf("\r\nContent-Type: text/html; charset=utf-8\r\n\r\n");
64261d06d6bSBaptiste Daroussin }
64361d06d6bSBaptiste Daroussin
64461d06d6bSBaptiste Daroussin static void
pg_searchres(const struct req * req,struct manpage * r,size_t sz)64561d06d6bSBaptiste Daroussin pg_searchres(const struct req *req, struct manpage *r, size_t sz)
64661d06d6bSBaptiste Daroussin {
64761d06d6bSBaptiste Daroussin char *arch, *archend;
64861d06d6bSBaptiste Daroussin const char *sec;
64961d06d6bSBaptiste Daroussin size_t i, iuse;
65061d06d6bSBaptiste Daroussin int archprio, archpriouse;
65161d06d6bSBaptiste Daroussin int prio, priouse;
652*c1c95addSBrooks Davis int have_header;
65361d06d6bSBaptiste Daroussin
65461d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) {
65561d06d6bSBaptiste Daroussin if (validate_filename(r[i].file))
65661d06d6bSBaptiste Daroussin continue;
65761d06d6bSBaptiste Daroussin warnx("invalid filename %s in %s database",
65861d06d6bSBaptiste Daroussin r[i].file, req->q.manpath);
65961d06d6bSBaptiste Daroussin pg_error_internal();
66061d06d6bSBaptiste Daroussin return;
66161d06d6bSBaptiste Daroussin }
66261d06d6bSBaptiste Daroussin
66361d06d6bSBaptiste Daroussin if (req->isquery && sz == 1) {
66461d06d6bSBaptiste Daroussin /*
66561d06d6bSBaptiste Daroussin * If we have just one result, then jump there now
66661d06d6bSBaptiste Daroussin * without any delay.
66761d06d6bSBaptiste Daroussin */
66861d06d6bSBaptiste Daroussin printf("Status: 303 See Other\r\n"
66961d06d6bSBaptiste Daroussin "Location: /");
67061d06d6bSBaptiste Daroussin if (*scriptname != '\0')
67161d06d6bSBaptiste Daroussin printf("%s/", scriptname);
67261d06d6bSBaptiste Daroussin if (strcmp(req->q.manpath, req->p[0]))
67361d06d6bSBaptiste Daroussin printf("%s/", req->q.manpath);
67461d06d6bSBaptiste Daroussin printf("%s\r\n"
67561d06d6bSBaptiste Daroussin "Content-Type: text/html; charset=utf-8\r\n\r\n",
67661d06d6bSBaptiste Daroussin r[0].file);
67761d06d6bSBaptiste Daroussin return;
67861d06d6bSBaptiste Daroussin }
67961d06d6bSBaptiste Daroussin
68061d06d6bSBaptiste Daroussin /*
68161d06d6bSBaptiste Daroussin * In man(1) mode, show one of the pages
68261d06d6bSBaptiste Daroussin * even if more than one is found.
68361d06d6bSBaptiste Daroussin */
68461d06d6bSBaptiste Daroussin
68561d06d6bSBaptiste Daroussin iuse = 0;
68661d06d6bSBaptiste Daroussin if (req->q.equal || sz == 1) {
68761d06d6bSBaptiste Daroussin priouse = 20;
68861d06d6bSBaptiste Daroussin archpriouse = 3;
68961d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) {
69061d06d6bSBaptiste Daroussin sec = r[i].file;
69161d06d6bSBaptiste Daroussin sec += strcspn(sec, "123456789");
69261d06d6bSBaptiste Daroussin if (sec[0] == '\0')
69361d06d6bSBaptiste Daroussin continue;
69461d06d6bSBaptiste Daroussin prio = sec_prios[sec[0] - '1'];
69561d06d6bSBaptiste Daroussin if (sec[1] != '/')
69661d06d6bSBaptiste Daroussin prio += 10;
69761d06d6bSBaptiste Daroussin if (req->q.arch == NULL) {
69861d06d6bSBaptiste Daroussin archprio =
69961d06d6bSBaptiste Daroussin ((arch = strchr(sec + 1, '/'))
70061d06d6bSBaptiste Daroussin == NULL) ? 3 :
70161d06d6bSBaptiste Daroussin ((archend = strchr(arch + 1, '/'))
70261d06d6bSBaptiste Daroussin == NULL) ? 0 :
70361d06d6bSBaptiste Daroussin strncmp(arch, "amd64/",
70461d06d6bSBaptiste Daroussin archend - arch) ? 2 : 1;
70561d06d6bSBaptiste Daroussin if (archprio < archpriouse) {
70661d06d6bSBaptiste Daroussin archpriouse = archprio;
70761d06d6bSBaptiste Daroussin priouse = prio;
70861d06d6bSBaptiste Daroussin iuse = i;
70961d06d6bSBaptiste Daroussin continue;
71061d06d6bSBaptiste Daroussin }
71161d06d6bSBaptiste Daroussin if (archprio > archpriouse)
71261d06d6bSBaptiste Daroussin continue;
71361d06d6bSBaptiste Daroussin }
71461d06d6bSBaptiste Daroussin if (prio >= priouse)
71561d06d6bSBaptiste Daroussin continue;
71661d06d6bSBaptiste Daroussin priouse = prio;
71761d06d6bSBaptiste Daroussin iuse = i;
71861d06d6bSBaptiste Daroussin }
719*c1c95addSBrooks Davis have_header = resp_begin_html(200, NULL, r[iuse].file);
72061d06d6bSBaptiste Daroussin } else
721*c1c95addSBrooks Davis have_header = resp_begin_html(200, NULL, NULL);
72261d06d6bSBaptiste Daroussin
723*c1c95addSBrooks Davis if (have_header == 0)
724*c1c95addSBrooks Davis puts("<header>");
72561d06d6bSBaptiste Daroussin resp_searchform(req,
72661d06d6bSBaptiste Daroussin req->q.equal || sz == 1 ? FOCUS_NONE : FOCUS_QUERY);
727*c1c95addSBrooks Davis puts("</header>");
72861d06d6bSBaptiste Daroussin
72961d06d6bSBaptiste Daroussin if (sz > 1) {
730*c1c95addSBrooks Davis puts("<nav>");
73161d06d6bSBaptiste Daroussin puts("<table class=\"results\">");
73261d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) {
73361d06d6bSBaptiste Daroussin printf(" <tr>\n"
73461d06d6bSBaptiste Daroussin " <td>"
73561d06d6bSBaptiste Daroussin "<a class=\"Xr\" href=\"/");
73661d06d6bSBaptiste Daroussin if (*scriptname != '\0')
73761d06d6bSBaptiste Daroussin printf("%s/", scriptname);
73861d06d6bSBaptiste Daroussin if (strcmp(req->q.manpath, req->p[0]))
73961d06d6bSBaptiste Daroussin printf("%s/", req->q.manpath);
74061d06d6bSBaptiste Daroussin printf("%s\">", r[i].file);
74161d06d6bSBaptiste Daroussin html_print(r[i].names);
74261d06d6bSBaptiste Daroussin printf("</a></td>\n"
74361d06d6bSBaptiste Daroussin " <td><span class=\"Nd\">");
74461d06d6bSBaptiste Daroussin html_print(r[i].output);
74561d06d6bSBaptiste Daroussin puts("</span></td>\n"
74661d06d6bSBaptiste Daroussin " </tr>");
74761d06d6bSBaptiste Daroussin }
74861d06d6bSBaptiste Daroussin puts("</table>");
749*c1c95addSBrooks Davis puts("</nav>");
75061d06d6bSBaptiste Daroussin }
75161d06d6bSBaptiste Daroussin
75261d06d6bSBaptiste Daroussin if (req->q.equal || sz == 1) {
75361d06d6bSBaptiste Daroussin puts("<hr>");
75461d06d6bSBaptiste Daroussin resp_show(req, r[iuse].file);
75561d06d6bSBaptiste Daroussin }
75661d06d6bSBaptiste Daroussin
75761d06d6bSBaptiste Daroussin resp_end_html();
75861d06d6bSBaptiste Daroussin }
75961d06d6bSBaptiste Daroussin
76061d06d6bSBaptiste Daroussin static void
resp_catman(const struct req * req,const char * file)76161d06d6bSBaptiste Daroussin resp_catman(const struct req *req, const char *file)
76261d06d6bSBaptiste Daroussin {
76361d06d6bSBaptiste Daroussin FILE *f;
76461d06d6bSBaptiste Daroussin char *p;
76561d06d6bSBaptiste Daroussin size_t sz;
76661d06d6bSBaptiste Daroussin ssize_t len;
76761d06d6bSBaptiste Daroussin int i;
76861d06d6bSBaptiste Daroussin int italic, bold;
76961d06d6bSBaptiste Daroussin
77061d06d6bSBaptiste Daroussin if ((f = fopen(file, "r")) == NULL) {
771*c1c95addSBrooks Davis puts("<p role=\"doc-notice\">\n"
772*c1c95addSBrooks Davis " You specified an invalid manual file.\n"
773*c1c95addSBrooks Davis "</p>");
77461d06d6bSBaptiste Daroussin return;
77561d06d6bSBaptiste Daroussin }
77661d06d6bSBaptiste Daroussin
77761d06d6bSBaptiste Daroussin puts("<div class=\"catman\">\n"
77861d06d6bSBaptiste Daroussin "<pre>");
77961d06d6bSBaptiste Daroussin
78061d06d6bSBaptiste Daroussin p = NULL;
78161d06d6bSBaptiste Daroussin sz = 0;
78261d06d6bSBaptiste Daroussin
78361d06d6bSBaptiste Daroussin while ((len = getline(&p, &sz, f)) != -1) {
78461d06d6bSBaptiste Daroussin bold = italic = 0;
78561d06d6bSBaptiste Daroussin for (i = 0; i < len - 1; i++) {
78661d06d6bSBaptiste Daroussin /*
78761d06d6bSBaptiste Daroussin * This means that the catpage is out of state.
78861d06d6bSBaptiste Daroussin * Ignore it and keep going (although the
78961d06d6bSBaptiste Daroussin * catpage is bogus).
79061d06d6bSBaptiste Daroussin */
79161d06d6bSBaptiste Daroussin
79261d06d6bSBaptiste Daroussin if ('\b' == p[i] || '\n' == p[i])
79361d06d6bSBaptiste Daroussin continue;
79461d06d6bSBaptiste Daroussin
79561d06d6bSBaptiste Daroussin /*
79661d06d6bSBaptiste Daroussin * Print a regular character.
79761d06d6bSBaptiste Daroussin * Close out any bold/italic scopes.
79861d06d6bSBaptiste Daroussin * If we're in back-space mode, make sure we'll
79961d06d6bSBaptiste Daroussin * have something to enter when we backspace.
80061d06d6bSBaptiste Daroussin */
80161d06d6bSBaptiste Daroussin
80261d06d6bSBaptiste Daroussin if ('\b' != p[i + 1]) {
80361d06d6bSBaptiste Daroussin if (italic)
80461d06d6bSBaptiste Daroussin printf("</i>");
80561d06d6bSBaptiste Daroussin if (bold)
80661d06d6bSBaptiste Daroussin printf("</b>");
80761d06d6bSBaptiste Daroussin italic = bold = 0;
80861d06d6bSBaptiste Daroussin html_putchar(p[i]);
80961d06d6bSBaptiste Daroussin continue;
81061d06d6bSBaptiste Daroussin } else if (i + 2 >= len)
81161d06d6bSBaptiste Daroussin continue;
81261d06d6bSBaptiste Daroussin
81361d06d6bSBaptiste Daroussin /* Italic mode. */
81461d06d6bSBaptiste Daroussin
81561d06d6bSBaptiste Daroussin if ('_' == p[i]) {
81661d06d6bSBaptiste Daroussin if (bold)
81761d06d6bSBaptiste Daroussin printf("</b>");
81861d06d6bSBaptiste Daroussin if ( ! italic)
81961d06d6bSBaptiste Daroussin printf("<i>");
82061d06d6bSBaptiste Daroussin bold = 0;
82161d06d6bSBaptiste Daroussin italic = 1;
82261d06d6bSBaptiste Daroussin i += 2;
82361d06d6bSBaptiste Daroussin html_putchar(p[i]);
82461d06d6bSBaptiste Daroussin continue;
82561d06d6bSBaptiste Daroussin }
82661d06d6bSBaptiste Daroussin
82761d06d6bSBaptiste Daroussin /*
82861d06d6bSBaptiste Daroussin * Handle funny behaviour troff-isms.
82961d06d6bSBaptiste Daroussin * These grok'd from the original man2html.c.
83061d06d6bSBaptiste Daroussin */
83161d06d6bSBaptiste Daroussin
83261d06d6bSBaptiste Daroussin if (('+' == p[i] && 'o' == p[i + 2]) ||
83361d06d6bSBaptiste Daroussin ('o' == p[i] && '+' == p[i + 2]) ||
83461d06d6bSBaptiste Daroussin ('|' == p[i] && '=' == p[i + 2]) ||
83561d06d6bSBaptiste Daroussin ('=' == p[i] && '|' == p[i + 2]) ||
83661d06d6bSBaptiste Daroussin ('*' == p[i] && '=' == p[i + 2]) ||
83761d06d6bSBaptiste Daroussin ('=' == p[i] && '*' == p[i + 2]) ||
83861d06d6bSBaptiste Daroussin ('*' == p[i] && '|' == p[i + 2]) ||
83961d06d6bSBaptiste Daroussin ('|' == p[i] && '*' == p[i + 2])) {
84061d06d6bSBaptiste Daroussin if (italic)
84161d06d6bSBaptiste Daroussin printf("</i>");
84261d06d6bSBaptiste Daroussin if (bold)
84361d06d6bSBaptiste Daroussin printf("</b>");
84461d06d6bSBaptiste Daroussin italic = bold = 0;
84561d06d6bSBaptiste Daroussin putchar('*');
84661d06d6bSBaptiste Daroussin i += 2;
84761d06d6bSBaptiste Daroussin continue;
84861d06d6bSBaptiste Daroussin } else if (('|' == p[i] && '-' == p[i + 2]) ||
84961d06d6bSBaptiste Daroussin ('-' == p[i] && '|' == p[i + 1]) ||
85061d06d6bSBaptiste Daroussin ('+' == p[i] && '-' == p[i + 1]) ||
85161d06d6bSBaptiste Daroussin ('-' == p[i] && '+' == p[i + 1]) ||
85261d06d6bSBaptiste Daroussin ('+' == p[i] && '|' == p[i + 1]) ||
85361d06d6bSBaptiste Daroussin ('|' == p[i] && '+' == p[i + 1])) {
85461d06d6bSBaptiste Daroussin if (italic)
85561d06d6bSBaptiste Daroussin printf("</i>");
85661d06d6bSBaptiste Daroussin if (bold)
85761d06d6bSBaptiste Daroussin printf("</b>");
85861d06d6bSBaptiste Daroussin italic = bold = 0;
85961d06d6bSBaptiste Daroussin putchar('+');
86061d06d6bSBaptiste Daroussin i += 2;
86161d06d6bSBaptiste Daroussin continue;
86261d06d6bSBaptiste Daroussin }
86361d06d6bSBaptiste Daroussin
86461d06d6bSBaptiste Daroussin /* Bold mode. */
86561d06d6bSBaptiste Daroussin
86661d06d6bSBaptiste Daroussin if (italic)
86761d06d6bSBaptiste Daroussin printf("</i>");
86861d06d6bSBaptiste Daroussin if ( ! bold)
86961d06d6bSBaptiste Daroussin printf("<b>");
87061d06d6bSBaptiste Daroussin bold = 1;
87161d06d6bSBaptiste Daroussin italic = 0;
87261d06d6bSBaptiste Daroussin i += 2;
87361d06d6bSBaptiste Daroussin html_putchar(p[i]);
87461d06d6bSBaptiste Daroussin }
87561d06d6bSBaptiste Daroussin
87661d06d6bSBaptiste Daroussin /*
87761d06d6bSBaptiste Daroussin * Clean up the last character.
87861d06d6bSBaptiste Daroussin * We can get to a newline; don't print that.
87961d06d6bSBaptiste Daroussin */
88061d06d6bSBaptiste Daroussin
88161d06d6bSBaptiste Daroussin if (italic)
88261d06d6bSBaptiste Daroussin printf("</i>");
88361d06d6bSBaptiste Daroussin if (bold)
88461d06d6bSBaptiste Daroussin printf("</b>");
88561d06d6bSBaptiste Daroussin
88661d06d6bSBaptiste Daroussin if (i == len - 1 && p[i] != '\n')
88761d06d6bSBaptiste Daroussin html_putchar(p[i]);
88861d06d6bSBaptiste Daroussin
88961d06d6bSBaptiste Daroussin putchar('\n');
89061d06d6bSBaptiste Daroussin }
89161d06d6bSBaptiste Daroussin free(p);
89261d06d6bSBaptiste Daroussin
89361d06d6bSBaptiste Daroussin puts("</pre>\n"
89461d06d6bSBaptiste Daroussin "</div>");
89561d06d6bSBaptiste Daroussin
89661d06d6bSBaptiste Daroussin fclose(f);
89761d06d6bSBaptiste Daroussin }
89861d06d6bSBaptiste Daroussin
89961d06d6bSBaptiste Daroussin static void
resp_format(const struct req * req,const char * file)90061d06d6bSBaptiste Daroussin resp_format(const struct req *req, const char *file)
90161d06d6bSBaptiste Daroussin {
90261d06d6bSBaptiste Daroussin struct manoutput conf;
90361d06d6bSBaptiste Daroussin struct mparse *mp;
9047295610fSBaptiste Daroussin struct roff_meta *meta;
90561d06d6bSBaptiste Daroussin void *vp;
90661d06d6bSBaptiste Daroussin int fd;
90761d06d6bSBaptiste Daroussin int usepath;
90861d06d6bSBaptiste Daroussin
909*c1c95addSBrooks Davis if (-1 == (fd = open(file, O_RDONLY))) {
910*c1c95addSBrooks Davis puts("<p role=\"doc-notice\">\n"
911*c1c95addSBrooks Davis " You specified an invalid manual file.\n"
912*c1c95addSBrooks Davis "</p>");
91361d06d6bSBaptiste Daroussin return;
91461d06d6bSBaptiste Daroussin }
91561d06d6bSBaptiste Daroussin
91661d06d6bSBaptiste Daroussin mchars_alloc();
9177295610fSBaptiste Daroussin mp = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
9187295610fSBaptiste Daroussin MPARSE_VALIDATE, MANDOC_OS_OTHER, req->q.manpath);
91961d06d6bSBaptiste Daroussin mparse_readfd(mp, fd, file);
92061d06d6bSBaptiste Daroussin close(fd);
9217295610fSBaptiste Daroussin meta = mparse_result(mp);
92261d06d6bSBaptiste Daroussin
92361d06d6bSBaptiste Daroussin memset(&conf, 0, sizeof(conf));
92461d06d6bSBaptiste Daroussin conf.fragment = 1;
92561d06d6bSBaptiste Daroussin conf.style = mandoc_strdup(CSS_DIR "/mandoc.css");
92661d06d6bSBaptiste Daroussin usepath = strcmp(req->q.manpath, req->p[0]);
92761d06d6bSBaptiste Daroussin mandoc_asprintf(&conf.man, "/%s%s%s%s%%N.%%S",
92861d06d6bSBaptiste Daroussin scriptname, *scriptname == '\0' ? "" : "/",
92961d06d6bSBaptiste Daroussin usepath ? req->q.manpath : "", usepath ? "/" : "");
93061d06d6bSBaptiste Daroussin
93161d06d6bSBaptiste Daroussin vp = html_alloc(&conf);
9327295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MDOC)
9337295610fSBaptiste Daroussin html_mdoc(vp, meta);
9347295610fSBaptiste Daroussin else
9357295610fSBaptiste Daroussin html_man(vp, meta);
93661d06d6bSBaptiste Daroussin
93761d06d6bSBaptiste Daroussin html_free(vp);
93861d06d6bSBaptiste Daroussin mparse_free(mp);
93961d06d6bSBaptiste Daroussin mchars_free();
94061d06d6bSBaptiste Daroussin free(conf.man);
94161d06d6bSBaptiste Daroussin free(conf.style);
94261d06d6bSBaptiste Daroussin }
94361d06d6bSBaptiste Daroussin
94461d06d6bSBaptiste Daroussin static void
resp_show(const struct req * req,const char * file)94561d06d6bSBaptiste Daroussin resp_show(const struct req *req, const char *file)
94661d06d6bSBaptiste Daroussin {
94761d06d6bSBaptiste Daroussin
94861d06d6bSBaptiste Daroussin if ('.' == file[0] && '/' == file[1])
94961d06d6bSBaptiste Daroussin file += 2;
95061d06d6bSBaptiste Daroussin
95161d06d6bSBaptiste Daroussin if ('c' == *file)
95261d06d6bSBaptiste Daroussin resp_catman(req, file);
95361d06d6bSBaptiste Daroussin else
95461d06d6bSBaptiste Daroussin resp_format(req, file);
95561d06d6bSBaptiste Daroussin }
95661d06d6bSBaptiste Daroussin
95761d06d6bSBaptiste Daroussin static void
pg_show(struct req * req,const char * fullpath)95861d06d6bSBaptiste Daroussin pg_show(struct req *req, const char *fullpath)
95961d06d6bSBaptiste Daroussin {
96061d06d6bSBaptiste Daroussin char *manpath;
96161d06d6bSBaptiste Daroussin const char *file;
96261d06d6bSBaptiste Daroussin
96361d06d6bSBaptiste Daroussin if ((file = strchr(fullpath, '/')) == NULL) {
96461d06d6bSBaptiste Daroussin pg_error_badrequest(
96561d06d6bSBaptiste Daroussin "You did not specify a page to show.");
96661d06d6bSBaptiste Daroussin return;
96761d06d6bSBaptiste Daroussin }
96861d06d6bSBaptiste Daroussin manpath = mandoc_strndup(fullpath, file - fullpath);
96961d06d6bSBaptiste Daroussin file++;
97061d06d6bSBaptiste Daroussin
97161d06d6bSBaptiste Daroussin if ( ! validate_manpath(req, manpath)) {
97261d06d6bSBaptiste Daroussin pg_error_badrequest(
97361d06d6bSBaptiste Daroussin "You specified an invalid manpath.");
97461d06d6bSBaptiste Daroussin free(manpath);
97561d06d6bSBaptiste Daroussin return;
97661d06d6bSBaptiste Daroussin }
97761d06d6bSBaptiste Daroussin
97861d06d6bSBaptiste Daroussin /*
97961d06d6bSBaptiste Daroussin * Begin by chdir()ing into the manpath.
98061d06d6bSBaptiste Daroussin * This way we can pick up the database files, which are
98161d06d6bSBaptiste Daroussin * relative to the manpath root.
98261d06d6bSBaptiste Daroussin */
98361d06d6bSBaptiste Daroussin
98461d06d6bSBaptiste Daroussin if (chdir(manpath) == -1) {
98561d06d6bSBaptiste Daroussin warn("chdir %s", manpath);
98661d06d6bSBaptiste Daroussin pg_error_internal();
98761d06d6bSBaptiste Daroussin free(manpath);
98861d06d6bSBaptiste Daroussin return;
98961d06d6bSBaptiste Daroussin }
99061d06d6bSBaptiste Daroussin free(manpath);
99161d06d6bSBaptiste Daroussin
99261d06d6bSBaptiste Daroussin if ( ! validate_filename(file)) {
99361d06d6bSBaptiste Daroussin pg_error_badrequest(
99461d06d6bSBaptiste Daroussin "You specified an invalid manual file.");
99561d06d6bSBaptiste Daroussin return;
99661d06d6bSBaptiste Daroussin }
99761d06d6bSBaptiste Daroussin
998*c1c95addSBrooks Davis if (resp_begin_html(200, NULL, file) == 0)
999*c1c95addSBrooks Davis puts("<header>");
100061d06d6bSBaptiste Daroussin resp_searchform(req, FOCUS_NONE);
1001*c1c95addSBrooks Davis puts("</header>");
100261d06d6bSBaptiste Daroussin resp_show(req, file);
100361d06d6bSBaptiste Daroussin resp_end_html();
100461d06d6bSBaptiste Daroussin }
100561d06d6bSBaptiste Daroussin
100661d06d6bSBaptiste Daroussin static void
pg_search(const struct req * req)100761d06d6bSBaptiste Daroussin pg_search(const struct req *req)
100861d06d6bSBaptiste Daroussin {
100961d06d6bSBaptiste Daroussin struct mansearch search;
101061d06d6bSBaptiste Daroussin struct manpaths paths;
101161d06d6bSBaptiste Daroussin struct manpage *res;
101261d06d6bSBaptiste Daroussin char **argv;
101361d06d6bSBaptiste Daroussin char *query, *rp, *wp;
101461d06d6bSBaptiste Daroussin size_t ressz;
101561d06d6bSBaptiste Daroussin int argc;
101661d06d6bSBaptiste Daroussin
101761d06d6bSBaptiste Daroussin /*
101861d06d6bSBaptiste Daroussin * Begin by chdir()ing into the root of the manpath.
101961d06d6bSBaptiste Daroussin * This way we can pick up the database files, which are
102061d06d6bSBaptiste Daroussin * relative to the manpath root.
102161d06d6bSBaptiste Daroussin */
102261d06d6bSBaptiste Daroussin
102361d06d6bSBaptiste Daroussin if (chdir(req->q.manpath) == -1) {
102461d06d6bSBaptiste Daroussin warn("chdir %s", req->q.manpath);
102561d06d6bSBaptiste Daroussin pg_error_internal();
102661d06d6bSBaptiste Daroussin return;
102761d06d6bSBaptiste Daroussin }
102861d06d6bSBaptiste Daroussin
102961d06d6bSBaptiste Daroussin search.arch = req->q.arch;
103061d06d6bSBaptiste Daroussin search.sec = req->q.sec;
103161d06d6bSBaptiste Daroussin search.outkey = "Nd";
103261d06d6bSBaptiste Daroussin search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR;
103361d06d6bSBaptiste Daroussin search.firstmatch = 1;
103461d06d6bSBaptiste Daroussin
103561d06d6bSBaptiste Daroussin paths.sz = 1;
103661d06d6bSBaptiste Daroussin paths.paths = mandoc_malloc(sizeof(char *));
103761d06d6bSBaptiste Daroussin paths.paths[0] = mandoc_strdup(".");
103861d06d6bSBaptiste Daroussin
103961d06d6bSBaptiste Daroussin /*
104061d06d6bSBaptiste Daroussin * Break apart at spaces with backslash-escaping.
104161d06d6bSBaptiste Daroussin */
104261d06d6bSBaptiste Daroussin
104361d06d6bSBaptiste Daroussin argc = 0;
104461d06d6bSBaptiste Daroussin argv = NULL;
104561d06d6bSBaptiste Daroussin rp = query = mandoc_strdup(req->q.query);
104661d06d6bSBaptiste Daroussin for (;;) {
104761d06d6bSBaptiste Daroussin while (isspace((unsigned char)*rp))
104861d06d6bSBaptiste Daroussin rp++;
104961d06d6bSBaptiste Daroussin if (*rp == '\0')
105061d06d6bSBaptiste Daroussin break;
105161d06d6bSBaptiste Daroussin argv = mandoc_reallocarray(argv, argc + 1, sizeof(char *));
105261d06d6bSBaptiste Daroussin argv[argc++] = wp = rp;
105361d06d6bSBaptiste Daroussin for (;;) {
105461d06d6bSBaptiste Daroussin if (isspace((unsigned char)*rp)) {
105561d06d6bSBaptiste Daroussin *wp = '\0';
105661d06d6bSBaptiste Daroussin rp++;
105761d06d6bSBaptiste Daroussin break;
105861d06d6bSBaptiste Daroussin }
105961d06d6bSBaptiste Daroussin if (rp[0] == '\\' && rp[1] != '\0')
106061d06d6bSBaptiste Daroussin rp++;
106161d06d6bSBaptiste Daroussin if (wp != rp)
106261d06d6bSBaptiste Daroussin *wp = *rp;
106361d06d6bSBaptiste Daroussin if (*rp == '\0')
106461d06d6bSBaptiste Daroussin break;
106561d06d6bSBaptiste Daroussin wp++;
106661d06d6bSBaptiste Daroussin rp++;
106761d06d6bSBaptiste Daroussin }
106861d06d6bSBaptiste Daroussin }
106961d06d6bSBaptiste Daroussin
107061d06d6bSBaptiste Daroussin res = NULL;
107161d06d6bSBaptiste Daroussin ressz = 0;
107261d06d6bSBaptiste Daroussin if (req->isquery && req->q.equal && argc == 1)
107361d06d6bSBaptiste Daroussin pg_redirect(req, argv[0]);
107461d06d6bSBaptiste Daroussin else if (mansearch(&search, &paths, argc, argv, &res, &ressz) == 0)
10756d38604fSBaptiste Daroussin pg_noresult(req, 400, "Bad Request",
10766d38604fSBaptiste Daroussin "You entered an invalid query.");
107761d06d6bSBaptiste Daroussin else if (ressz == 0)
10786d38604fSBaptiste Daroussin pg_noresult(req, 404, "Not Found", "No results found.");
107961d06d6bSBaptiste Daroussin else
108061d06d6bSBaptiste Daroussin pg_searchres(req, res, ressz);
108161d06d6bSBaptiste Daroussin
108261d06d6bSBaptiste Daroussin free(query);
108361d06d6bSBaptiste Daroussin mansearch_free(res, ressz);
108461d06d6bSBaptiste Daroussin free(paths.paths[0]);
108561d06d6bSBaptiste Daroussin free(paths.paths);
108661d06d6bSBaptiste Daroussin }
108761d06d6bSBaptiste Daroussin
108861d06d6bSBaptiste Daroussin int
main(void)108961d06d6bSBaptiste Daroussin main(void)
109061d06d6bSBaptiste Daroussin {
109161d06d6bSBaptiste Daroussin struct req req;
109261d06d6bSBaptiste Daroussin struct itimerval itimer;
109361d06d6bSBaptiste Daroussin const char *path;
109461d06d6bSBaptiste Daroussin const char *querystring;
109561d06d6bSBaptiste Daroussin int i;
109661d06d6bSBaptiste Daroussin
109761d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
109861d06d6bSBaptiste Daroussin /*
109961d06d6bSBaptiste Daroussin * The "rpath" pledge could be revoked after mparse_readfd()
1100*c1c95addSBrooks Davis * if the file descriptor to "/footer.html" would be opened
110161d06d6bSBaptiste Daroussin * up front, but it's probably not worth the complication
110261d06d6bSBaptiste Daroussin * of the code it would cause: it would require scattering
110361d06d6bSBaptiste Daroussin * pledge() calls in multiple low-level resp_*() functions.
110461d06d6bSBaptiste Daroussin */
110561d06d6bSBaptiste Daroussin
110661d06d6bSBaptiste Daroussin if (pledge("stdio rpath", NULL) == -1) {
110761d06d6bSBaptiste Daroussin warn("pledge");
110861d06d6bSBaptiste Daroussin pg_error_internal();
110961d06d6bSBaptiste Daroussin return EXIT_FAILURE;
111061d06d6bSBaptiste Daroussin }
111161d06d6bSBaptiste Daroussin #endif
111261d06d6bSBaptiste Daroussin
111361d06d6bSBaptiste Daroussin /* Poor man's ReDoS mitigation. */
111461d06d6bSBaptiste Daroussin
111561d06d6bSBaptiste Daroussin itimer.it_value.tv_sec = 2;
111661d06d6bSBaptiste Daroussin itimer.it_value.tv_usec = 0;
111761d06d6bSBaptiste Daroussin itimer.it_interval.tv_sec = 2;
111861d06d6bSBaptiste Daroussin itimer.it_interval.tv_usec = 0;
111961d06d6bSBaptiste Daroussin if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) {
112061d06d6bSBaptiste Daroussin warn("setitimer");
112161d06d6bSBaptiste Daroussin pg_error_internal();
112261d06d6bSBaptiste Daroussin return EXIT_FAILURE;
112361d06d6bSBaptiste Daroussin }
112461d06d6bSBaptiste Daroussin
112561d06d6bSBaptiste Daroussin /*
112661d06d6bSBaptiste Daroussin * First we change directory into the MAN_DIR so that
112761d06d6bSBaptiste Daroussin * subsequent scanning for manpath directories is rooted
112861d06d6bSBaptiste Daroussin * relative to the same position.
112961d06d6bSBaptiste Daroussin */
113061d06d6bSBaptiste Daroussin
113161d06d6bSBaptiste Daroussin if (chdir(MAN_DIR) == -1) {
113261d06d6bSBaptiste Daroussin warn("MAN_DIR: %s", MAN_DIR);
113361d06d6bSBaptiste Daroussin pg_error_internal();
113461d06d6bSBaptiste Daroussin return EXIT_FAILURE;
113561d06d6bSBaptiste Daroussin }
113661d06d6bSBaptiste Daroussin
113761d06d6bSBaptiste Daroussin memset(&req, 0, sizeof(struct req));
113861d06d6bSBaptiste Daroussin req.q.equal = 1;
113961d06d6bSBaptiste Daroussin parse_manpath_conf(&req);
114061d06d6bSBaptiste Daroussin
114161d06d6bSBaptiste Daroussin /* Parse the path info and the query string. */
114261d06d6bSBaptiste Daroussin
114361d06d6bSBaptiste Daroussin if ((path = getenv("PATH_INFO")) == NULL)
114461d06d6bSBaptiste Daroussin path = "";
114561d06d6bSBaptiste Daroussin else if (*path == '/')
114661d06d6bSBaptiste Daroussin path++;
114761d06d6bSBaptiste Daroussin
114861d06d6bSBaptiste Daroussin if (*path != '\0') {
114961d06d6bSBaptiste Daroussin parse_path_info(&req, path);
115061d06d6bSBaptiste Daroussin if (req.q.manpath == NULL || req.q.sec == NULL ||
115161d06d6bSBaptiste Daroussin *req.q.query == '\0' || access(path, F_OK) == -1)
115261d06d6bSBaptiste Daroussin path = "";
115361d06d6bSBaptiste Daroussin } else if ((querystring = getenv("QUERY_STRING")) != NULL)
115461d06d6bSBaptiste Daroussin parse_query_string(&req, querystring);
115561d06d6bSBaptiste Daroussin
115661d06d6bSBaptiste Daroussin /* Validate parsed data and add defaults. */
115761d06d6bSBaptiste Daroussin
115861d06d6bSBaptiste Daroussin if (req.q.manpath == NULL)
115961d06d6bSBaptiste Daroussin req.q.manpath = mandoc_strdup(req.p[0]);
116061d06d6bSBaptiste Daroussin else if ( ! validate_manpath(&req, req.q.manpath)) {
116161d06d6bSBaptiste Daroussin pg_error_badrequest(
116261d06d6bSBaptiste Daroussin "You specified an invalid manpath.");
116361d06d6bSBaptiste Daroussin return EXIT_FAILURE;
116461d06d6bSBaptiste Daroussin }
116561d06d6bSBaptiste Daroussin
11667295610fSBaptiste Daroussin if (req.q.arch != NULL && validate_arch(req.q.arch) == 0) {
116761d06d6bSBaptiste Daroussin pg_error_badrequest(
116861d06d6bSBaptiste Daroussin "You specified an invalid architecture.");
116961d06d6bSBaptiste Daroussin return EXIT_FAILURE;
117061d06d6bSBaptiste Daroussin }
117161d06d6bSBaptiste Daroussin
117261d06d6bSBaptiste Daroussin /* Dispatch to the three different pages. */
117361d06d6bSBaptiste Daroussin
117461d06d6bSBaptiste Daroussin if ('\0' != *path)
117561d06d6bSBaptiste Daroussin pg_show(&req, path);
117661d06d6bSBaptiste Daroussin else if (NULL != req.q.query)
117761d06d6bSBaptiste Daroussin pg_search(&req);
117861d06d6bSBaptiste Daroussin else
117961d06d6bSBaptiste Daroussin pg_index(&req);
118061d06d6bSBaptiste Daroussin
118161d06d6bSBaptiste Daroussin free(req.q.manpath);
118261d06d6bSBaptiste Daroussin free(req.q.arch);
118361d06d6bSBaptiste Daroussin free(req.q.sec);
118461d06d6bSBaptiste Daroussin free(req.q.query);
118561d06d6bSBaptiste Daroussin for (i = 0; i < (int)req.psz; i++)
118661d06d6bSBaptiste Daroussin free(req.p[i]);
118761d06d6bSBaptiste Daroussin free(req.p);
118861d06d6bSBaptiste Daroussin return EXIT_SUCCESS;
118961d06d6bSBaptiste Daroussin }
119061d06d6bSBaptiste Daroussin
119161d06d6bSBaptiste Daroussin /*
11927295610fSBaptiste Daroussin * Translate PATH_INFO to a query.
119361d06d6bSBaptiste Daroussin */
119461d06d6bSBaptiste Daroussin static void
parse_path_info(struct req * req,const char * path)119561d06d6bSBaptiste Daroussin parse_path_info(struct req *req, const char *path)
119661d06d6bSBaptiste Daroussin {
11977295610fSBaptiste Daroussin const char *name, *sec, *end;
119861d06d6bSBaptiste Daroussin
119961d06d6bSBaptiste Daroussin req->isquery = 0;
120061d06d6bSBaptiste Daroussin req->q.equal = 1;
12017295610fSBaptiste Daroussin req->q.manpath = NULL;
120261d06d6bSBaptiste Daroussin req->q.arch = NULL;
120361d06d6bSBaptiste Daroussin
120461d06d6bSBaptiste Daroussin /* Mandatory manual page name. */
12057295610fSBaptiste Daroussin if ((name = strrchr(path, '/')) == NULL)
12067295610fSBaptiste Daroussin name = path;
12077295610fSBaptiste Daroussin else
12087295610fSBaptiste Daroussin name++;
120961d06d6bSBaptiste Daroussin
121061d06d6bSBaptiste Daroussin /* Optional trailing section. */
12117295610fSBaptiste Daroussin sec = strrchr(name, '.');
12127295610fSBaptiste Daroussin if (sec != NULL && isdigit((unsigned char)*++sec)) {
12137295610fSBaptiste Daroussin req->q.query = mandoc_strndup(name, sec - name - 1);
12147295610fSBaptiste Daroussin req->q.sec = mandoc_strdup(sec);
12157295610fSBaptiste Daroussin } else {
12167295610fSBaptiste Daroussin req->q.query = mandoc_strdup(name);
121761d06d6bSBaptiste Daroussin req->q.sec = NULL;
121861d06d6bSBaptiste Daroussin }
121961d06d6bSBaptiste Daroussin
122061d06d6bSBaptiste Daroussin /* Handle the case of name[.section] only. */
12217295610fSBaptiste Daroussin if (name == path)
122261d06d6bSBaptiste Daroussin return;
122361d06d6bSBaptiste Daroussin
12247295610fSBaptiste Daroussin /* Optional manpath. */
12257295610fSBaptiste Daroussin end = strchr(path, '/');
12267295610fSBaptiste Daroussin req->q.manpath = mandoc_strndup(path, end - path);
12277295610fSBaptiste Daroussin if (validate_manpath(req, req->q.manpath)) {
12287295610fSBaptiste Daroussin path = end + 1;
12297295610fSBaptiste Daroussin if (name == path)
12307295610fSBaptiste Daroussin return;
12317295610fSBaptiste Daroussin } else {
12327295610fSBaptiste Daroussin free(req->q.manpath);
12337295610fSBaptiste Daroussin req->q.manpath = NULL;
12347295610fSBaptiste Daroussin }
12357295610fSBaptiste Daroussin
12367295610fSBaptiste Daroussin /* Optional section. */
12377295610fSBaptiste Daroussin if (strncmp(path, "man", 3) == 0 || strncmp(path, "cat", 3) == 0) {
12387295610fSBaptiste Daroussin path += 3;
12397295610fSBaptiste Daroussin end = strchr(path, '/');
12407295610fSBaptiste Daroussin free(req->q.sec);
12417295610fSBaptiste Daroussin req->q.sec = mandoc_strndup(path, end - path);
12427295610fSBaptiste Daroussin path = end + 1;
12437295610fSBaptiste Daroussin if (name == path)
12447295610fSBaptiste Daroussin return;
12457295610fSBaptiste Daroussin }
12467295610fSBaptiste Daroussin
12477295610fSBaptiste Daroussin /* Optional architecture. */
12487295610fSBaptiste Daroussin end = strchr(path, '/');
12497295610fSBaptiste Daroussin if (end + 1 != name) {
125061d06d6bSBaptiste Daroussin pg_error_badrequest(
125161d06d6bSBaptiste Daroussin "You specified too many directory components.");
125261d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
125361d06d6bSBaptiste Daroussin }
12547295610fSBaptiste Daroussin req->q.arch = mandoc_strndup(path, end - path);
12557295610fSBaptiste Daroussin if (validate_arch(req->q.arch) == 0) {
125661d06d6bSBaptiste Daroussin pg_error_badrequest(
125761d06d6bSBaptiste Daroussin "You specified an invalid directory component.");
125861d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
125961d06d6bSBaptiste Daroussin }
126061d06d6bSBaptiste Daroussin }
126161d06d6bSBaptiste Daroussin
126261d06d6bSBaptiste Daroussin /*
126361d06d6bSBaptiste Daroussin * Scan for indexable paths.
126461d06d6bSBaptiste Daroussin */
126561d06d6bSBaptiste Daroussin static void
parse_manpath_conf(struct req * req)126661d06d6bSBaptiste Daroussin parse_manpath_conf(struct req *req)
126761d06d6bSBaptiste Daroussin {
126861d06d6bSBaptiste Daroussin FILE *fp;
126961d06d6bSBaptiste Daroussin char *dp;
127061d06d6bSBaptiste Daroussin size_t dpsz;
127161d06d6bSBaptiste Daroussin ssize_t len;
127261d06d6bSBaptiste Daroussin
127361d06d6bSBaptiste Daroussin if ((fp = fopen("manpath.conf", "r")) == NULL) {
127461d06d6bSBaptiste Daroussin warn("%s/manpath.conf", MAN_DIR);
127561d06d6bSBaptiste Daroussin pg_error_internal();
127661d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
127761d06d6bSBaptiste Daroussin }
127861d06d6bSBaptiste Daroussin
127961d06d6bSBaptiste Daroussin dp = NULL;
128061d06d6bSBaptiste Daroussin dpsz = 0;
128161d06d6bSBaptiste Daroussin
128261d06d6bSBaptiste Daroussin while ((len = getline(&dp, &dpsz, fp)) != -1) {
128361d06d6bSBaptiste Daroussin if (dp[len - 1] == '\n')
128461d06d6bSBaptiste Daroussin dp[--len] = '\0';
128561d06d6bSBaptiste Daroussin req->p = mandoc_realloc(req->p,
128661d06d6bSBaptiste Daroussin (req->psz + 1) * sizeof(char *));
128761d06d6bSBaptiste Daroussin if ( ! validate_urifrag(dp)) {
128861d06d6bSBaptiste Daroussin warnx("%s/manpath.conf contains "
128961d06d6bSBaptiste Daroussin "unsafe path \"%s\"", MAN_DIR, dp);
129061d06d6bSBaptiste Daroussin pg_error_internal();
129161d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
129261d06d6bSBaptiste Daroussin }
129361d06d6bSBaptiste Daroussin if (strchr(dp, '/') != NULL) {
129461d06d6bSBaptiste Daroussin warnx("%s/manpath.conf contains "
129561d06d6bSBaptiste Daroussin "path with slash \"%s\"", MAN_DIR, dp);
129661d06d6bSBaptiste Daroussin pg_error_internal();
129761d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
129861d06d6bSBaptiste Daroussin }
129961d06d6bSBaptiste Daroussin req->p[req->psz++] = dp;
130061d06d6bSBaptiste Daroussin dp = NULL;
130161d06d6bSBaptiste Daroussin dpsz = 0;
130261d06d6bSBaptiste Daroussin }
130361d06d6bSBaptiste Daroussin free(dp);
130461d06d6bSBaptiste Daroussin
130561d06d6bSBaptiste Daroussin if (req->p == NULL) {
130661d06d6bSBaptiste Daroussin warnx("%s/manpath.conf is empty", MAN_DIR);
130761d06d6bSBaptiste Daroussin pg_error_internal();
130861d06d6bSBaptiste Daroussin exit(EXIT_FAILURE);
130961d06d6bSBaptiste Daroussin }
131061d06d6bSBaptiste Daroussin }
1311