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