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