cgi.c revision 279527
1279527Sbapt/* $Id: cgi.c,v 1.104 2015/02/10 08:05:30 schwarze Exp $ */ 2274880Sbapt/* 3274880Sbapt * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv> 4274880Sbapt * Copyright (c) 2014 Ingo Schwarze <schwarze@usta.de> 5274880Sbapt * 6274880Sbapt * Permission to use, copy, modify, and distribute this software for any 7274880Sbapt * purpose with or without fee is hereby granted, provided that the above 8274880Sbapt * copyright notice and this permission notice appear in all copies. 9274880Sbapt * 10274880Sbapt * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11274880Sbapt * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12274880Sbapt * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13274880Sbapt * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14274880Sbapt * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15274880Sbapt * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16274880Sbapt * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17274880Sbapt */ 18274880Sbapt#include "config.h" 19274880Sbapt 20275432Sbapt#include <sys/types.h> 21275432Sbapt#include <sys/time.h> 22275432Sbapt 23274880Sbapt#include <ctype.h> 24274880Sbapt#include <errno.h> 25274880Sbapt#include <fcntl.h> 26274880Sbapt#include <limits.h> 27274880Sbapt#include <stdint.h> 28274880Sbapt#include <stdio.h> 29274880Sbapt#include <stdlib.h> 30274880Sbapt#include <string.h> 31274880Sbapt#include <unistd.h> 32274880Sbapt 33274880Sbapt#include "mandoc.h" 34274880Sbapt#include "mandoc_aux.h" 35274880Sbapt#include "main.h" 36274880Sbapt#include "manpath.h" 37274880Sbapt#include "mansearch.h" 38274880Sbapt#include "cgi.h" 39274880Sbapt 40274880Sbapt/* 41274880Sbapt * A query as passed to the search function. 42274880Sbapt */ 43274880Sbaptstruct query { 44274880Sbapt char *manpath; /* desired manual directory */ 45274880Sbapt char *arch; /* architecture */ 46274880Sbapt char *sec; /* manual section */ 47274880Sbapt char *query; /* unparsed query expression */ 48274880Sbapt int equal; /* match whole names, not substrings */ 49274880Sbapt}; 50274880Sbapt 51274880Sbaptstruct req { 52274880Sbapt struct query q; 53274880Sbapt char **p; /* array of available manpaths */ 54274880Sbapt size_t psz; /* number of available manpaths */ 55274880Sbapt}; 56274880Sbapt 57274880Sbaptstatic void catman(const struct req *, const char *); 58274880Sbaptstatic void format(const struct req *, const char *); 59274880Sbaptstatic void html_print(const char *); 60274880Sbaptstatic void html_putchar(char); 61279527Sbaptstatic int http_decode(char *); 62274880Sbaptstatic void http_parse(struct req *, const char *); 63274880Sbaptstatic void http_print(const char *); 64279527Sbaptstatic void http_putchar(char); 65274880Sbaptstatic void http_printquery(const struct req *, const char *); 66274880Sbaptstatic void pathgen(struct req *); 67274880Sbaptstatic void pg_error_badrequest(const char *); 68274880Sbaptstatic void pg_error_internal(void); 69274880Sbaptstatic void pg_index(const struct req *); 70274880Sbaptstatic void pg_noresult(const struct req *, const char *); 71274880Sbaptstatic void pg_search(const struct req *); 72274880Sbaptstatic void pg_searchres(const struct req *, 73274880Sbapt struct manpage *, size_t); 74274880Sbaptstatic void pg_show(struct req *, const char *); 75274880Sbaptstatic void resp_begin_html(int, const char *); 76274880Sbaptstatic void resp_begin_http(int, const char *); 77274880Sbaptstatic void resp_end_html(void); 78274880Sbaptstatic void resp_searchform(const struct req *); 79274880Sbaptstatic void resp_show(const struct req *, const char *); 80274880Sbaptstatic void set_query_attr(char **, char **); 81274880Sbaptstatic int validate_filename(const char *); 82274880Sbaptstatic int validate_manpath(const struct req *, const char *); 83274880Sbaptstatic int validate_urifrag(const char *); 84274880Sbapt 85274880Sbaptstatic const char *scriptname; /* CGI script name */ 86274880Sbapt 87274880Sbaptstatic const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; 88274880Sbaptstatic const char *const sec_numbers[] = { 89274880Sbapt "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9" 90274880Sbapt}; 91274880Sbaptstatic const char *const sec_names[] = { 92274880Sbapt "All Sections", 93274880Sbapt "1 - General Commands", 94274880Sbapt "2 - System Calls", 95275432Sbapt "3 - Library Functions", 96275432Sbapt "3p - Perl Library", 97275432Sbapt "4 - Device Drivers", 98274880Sbapt "5 - File Formats", 99274880Sbapt "6 - Games", 100275432Sbapt "7 - Miscellaneous Information", 101275432Sbapt "8 - System Manager\'s Manual", 102275432Sbapt "9 - Kernel Developer\'s Manual" 103274880Sbapt}; 104274880Sbaptstatic const int sec_MAX = sizeof(sec_names) / sizeof(char *); 105274880Sbapt 106274880Sbaptstatic const char *const arch_names[] = { 107274880Sbapt "amd64", "alpha", "armish", "armv7", 108274880Sbapt "aviion", "hppa", "hppa64", "i386", 109274880Sbapt "ia64", "landisk", "loongson", "luna88k", 110274880Sbapt "macppc", "mips64", "octeon", "sgi", 111274880Sbapt "socppc", "solbourne", "sparc", "sparc64", 112274880Sbapt "vax", "zaurus", 113274880Sbapt "amiga", "arc", "arm32", "atari", 114274880Sbapt "beagle", "cats", "hp300", "mac68k", 115274880Sbapt "mvme68k", "mvme88k", "mvmeppc", "palm", 116274880Sbapt "pc532", "pegasos", "pmax", "powerpc", 117274880Sbapt "sun3", "wgrisc", "x68k" 118274880Sbapt}; 119274880Sbaptstatic const int arch_MAX = sizeof(arch_names) / sizeof(char *); 120274880Sbapt 121274880Sbapt/* 122274880Sbapt * Print a character, escaping HTML along the way. 123274880Sbapt * This will pass non-ASCII straight to output: be warned! 124274880Sbapt */ 125274880Sbaptstatic void 126274880Sbapthtml_putchar(char c) 127274880Sbapt{ 128274880Sbapt 129274880Sbapt switch (c) { 130274880Sbapt case ('"'): 131274880Sbapt printf(""e;"); 132274880Sbapt break; 133274880Sbapt case ('&'): 134274880Sbapt printf("&"); 135274880Sbapt break; 136274880Sbapt case ('>'): 137274880Sbapt printf(">"); 138274880Sbapt break; 139274880Sbapt case ('<'): 140274880Sbapt printf("<"); 141274880Sbapt break; 142274880Sbapt default: 143274880Sbapt putchar((unsigned char)c); 144274880Sbapt break; 145274880Sbapt } 146274880Sbapt} 147274880Sbapt 148274880Sbaptstatic void 149274880Sbapthttp_printquery(const struct req *req, const char *sep) 150274880Sbapt{ 151274880Sbapt 152274880Sbapt if (NULL != req->q.query) { 153274880Sbapt printf("query="); 154274880Sbapt http_print(req->q.query); 155274880Sbapt } 156274880Sbapt if (0 == req->q.equal) 157274880Sbapt printf("%sapropos=1", sep); 158274880Sbapt if (NULL != req->q.sec) { 159274880Sbapt printf("%ssec=", sep); 160274880Sbapt http_print(req->q.sec); 161274880Sbapt } 162274880Sbapt if (NULL != req->q.arch) { 163274880Sbapt printf("%sarch=", sep); 164274880Sbapt http_print(req->q.arch); 165274880Sbapt } 166275432Sbapt if (strcmp(req->q.manpath, req->p[0])) { 167274880Sbapt printf("%smanpath=", sep); 168274880Sbapt http_print(req->q.manpath); 169274880Sbapt } 170274880Sbapt} 171274880Sbapt 172274880Sbaptstatic void 173274880Sbapthttp_print(const char *p) 174274880Sbapt{ 175274880Sbapt 176274880Sbapt if (NULL == p) 177274880Sbapt return; 178274880Sbapt while ('\0' != *p) 179274880Sbapt http_putchar(*p++); 180274880Sbapt} 181274880Sbapt 182274880Sbapt/* 183274880Sbapt * Call through to html_putchar(). 184274880Sbapt * Accepts NULL strings. 185274880Sbapt */ 186274880Sbaptstatic void 187274880Sbapthtml_print(const char *p) 188274880Sbapt{ 189279527Sbapt 190274880Sbapt if (NULL == p) 191274880Sbapt return; 192274880Sbapt while ('\0' != *p) 193274880Sbapt html_putchar(*p++); 194274880Sbapt} 195274880Sbapt 196274880Sbapt/* 197274880Sbapt * Transfer the responsibility for the allocated string *val 198274880Sbapt * to the query structure. 199274880Sbapt */ 200274880Sbaptstatic void 201274880Sbaptset_query_attr(char **attr, char **val) 202274880Sbapt{ 203274880Sbapt 204274880Sbapt free(*attr); 205274880Sbapt if (**val == '\0') { 206274880Sbapt *attr = NULL; 207274880Sbapt free(*val); 208274880Sbapt } else 209274880Sbapt *attr = *val; 210274880Sbapt *val = NULL; 211274880Sbapt} 212274880Sbapt 213274880Sbapt/* 214274880Sbapt * Parse the QUERY_STRING for key-value pairs 215274880Sbapt * and store the values into the query structure. 216274880Sbapt */ 217274880Sbaptstatic void 218274880Sbapthttp_parse(struct req *req, const char *qs) 219274880Sbapt{ 220274880Sbapt char *key, *val; 221274880Sbapt size_t keysz, valsz; 222274880Sbapt 223274880Sbapt req->q.manpath = NULL; 224274880Sbapt req->q.arch = NULL; 225274880Sbapt req->q.sec = NULL; 226274880Sbapt req->q.query = NULL; 227274880Sbapt req->q.equal = 1; 228274880Sbapt 229274880Sbapt key = val = NULL; 230274880Sbapt while (*qs != '\0') { 231274880Sbapt 232274880Sbapt /* Parse one key. */ 233274880Sbapt 234274880Sbapt keysz = strcspn(qs, "=;&"); 235274880Sbapt key = mandoc_strndup(qs, keysz); 236274880Sbapt qs += keysz; 237274880Sbapt if (*qs != '=') 238274880Sbapt goto next; 239274880Sbapt 240274880Sbapt /* Parse one value. */ 241274880Sbapt 242274880Sbapt valsz = strcspn(++qs, ";&"); 243274880Sbapt val = mandoc_strndup(qs, valsz); 244274880Sbapt qs += valsz; 245274880Sbapt 246274880Sbapt /* Decode and catch encoding errors. */ 247274880Sbapt 248274880Sbapt if ( ! (http_decode(key) && http_decode(val))) 249274880Sbapt goto next; 250274880Sbapt 251274880Sbapt /* Handle key-value pairs. */ 252274880Sbapt 253274880Sbapt if ( ! strcmp(key, "query")) 254274880Sbapt set_query_attr(&req->q.query, &val); 255274880Sbapt 256274880Sbapt else if ( ! strcmp(key, "apropos")) 257274880Sbapt req->q.equal = !strcmp(val, "0"); 258274880Sbapt 259274880Sbapt else if ( ! strcmp(key, "manpath")) { 260274880Sbapt#ifdef COMPAT_OLDURI 261274880Sbapt if ( ! strncmp(val, "OpenBSD ", 8)) { 262274880Sbapt val[7] = '-'; 263274880Sbapt if ('C' == val[8]) 264274880Sbapt val[8] = 'c'; 265274880Sbapt } 266274880Sbapt#endif 267274880Sbapt set_query_attr(&req->q.manpath, &val); 268274880Sbapt } 269274880Sbapt 270274880Sbapt else if ( ! (strcmp(key, "sec") 271274880Sbapt#ifdef COMPAT_OLDURI 272274880Sbapt && strcmp(key, "sektion") 273274880Sbapt#endif 274274880Sbapt )) { 275274880Sbapt if ( ! strcmp(val, "0")) 276274880Sbapt *val = '\0'; 277274880Sbapt set_query_attr(&req->q.sec, &val); 278274880Sbapt } 279274880Sbapt 280274880Sbapt else if ( ! strcmp(key, "arch")) { 281274880Sbapt if ( ! strcmp(val, "default")) 282274880Sbapt *val = '\0'; 283274880Sbapt set_query_attr(&req->q.arch, &val); 284274880Sbapt } 285274880Sbapt 286274880Sbapt /* 287274880Sbapt * The key must be freed in any case. 288274880Sbapt * The val may have been handed over to the query 289274880Sbapt * structure, in which case it is now NULL. 290274880Sbapt */ 291274880Sbaptnext: 292274880Sbapt free(key); 293274880Sbapt key = NULL; 294274880Sbapt free(val); 295274880Sbapt val = NULL; 296274880Sbapt 297274880Sbapt if (*qs != '\0') 298274880Sbapt qs++; 299274880Sbapt } 300274880Sbapt} 301274880Sbapt 302274880Sbaptstatic void 303274880Sbapthttp_putchar(char c) 304274880Sbapt{ 305274880Sbapt 306274880Sbapt if (isalnum((unsigned char)c)) { 307274880Sbapt putchar((unsigned char)c); 308274880Sbapt return; 309274880Sbapt } else if (' ' == c) { 310274880Sbapt putchar('+'); 311274880Sbapt return; 312274880Sbapt } 313274880Sbapt printf("%%%.2x", c); 314274880Sbapt} 315274880Sbapt 316274880Sbapt/* 317274880Sbapt * HTTP-decode a string. The standard explanation is that this turns 318274880Sbapt * "%4e+foo" into "n foo" in the regular way. This is done in-place 319274880Sbapt * over the allocated string. 320274880Sbapt */ 321274880Sbaptstatic int 322274880Sbapthttp_decode(char *p) 323274880Sbapt{ 324274880Sbapt char hex[3]; 325274880Sbapt char *q; 326274880Sbapt int c; 327274880Sbapt 328274880Sbapt hex[2] = '\0'; 329274880Sbapt 330274880Sbapt q = p; 331274880Sbapt for ( ; '\0' != *p; p++, q++) { 332274880Sbapt if ('%' == *p) { 333274880Sbapt if ('\0' == (hex[0] = *(p + 1))) 334274880Sbapt return(0); 335274880Sbapt if ('\0' == (hex[1] = *(p + 2))) 336274880Sbapt return(0); 337274880Sbapt if (1 != sscanf(hex, "%x", &c)) 338274880Sbapt return(0); 339274880Sbapt if ('\0' == c) 340274880Sbapt return(0); 341274880Sbapt 342274880Sbapt *q = (char)c; 343274880Sbapt p += 2; 344274880Sbapt } else 345274880Sbapt *q = '+' == *p ? ' ' : *p; 346274880Sbapt } 347274880Sbapt 348274880Sbapt *q = '\0'; 349274880Sbapt return(1); 350274880Sbapt} 351274880Sbapt 352274880Sbaptstatic void 353274880Sbaptresp_begin_http(int code, const char *msg) 354274880Sbapt{ 355274880Sbapt 356274880Sbapt if (200 != code) 357274880Sbapt printf("Status: %d %s\r\n", code, msg); 358274880Sbapt 359274880Sbapt printf("Content-Type: text/html; charset=utf-8\r\n" 360274880Sbapt "Cache-Control: no-cache\r\n" 361274880Sbapt "Pragma: no-cache\r\n" 362274880Sbapt "\r\n"); 363274880Sbapt 364274880Sbapt fflush(stdout); 365274880Sbapt} 366274880Sbapt 367274880Sbaptstatic void 368274880Sbaptresp_begin_html(int code, const char *msg) 369274880Sbapt{ 370274880Sbapt 371274880Sbapt resp_begin_http(code, msg); 372274880Sbapt 373275432Sbapt printf("<!DOCTYPE html>\n" 374274880Sbapt "<HTML>\n" 375274880Sbapt "<HEAD>\n" 376275432Sbapt "<META CHARSET=\"UTF-8\" />\n" 377274880Sbapt "<LINK REL=\"stylesheet\" HREF=\"%s/man-cgi.css\"" 378274880Sbapt " TYPE=\"text/css\" media=\"all\">\n" 379274880Sbapt "<LINK REL=\"stylesheet\" HREF=\"%s/man.css\"" 380274880Sbapt " TYPE=\"text/css\" media=\"all\">\n" 381274880Sbapt "<TITLE>%s</TITLE>\n" 382274880Sbapt "</HEAD>\n" 383274880Sbapt "<BODY>\n" 384274880Sbapt "<!-- Begin page content. //-->\n", 385274880Sbapt CSS_DIR, CSS_DIR, CUSTOMIZE_TITLE); 386274880Sbapt} 387274880Sbapt 388274880Sbaptstatic void 389274880Sbaptresp_end_html(void) 390274880Sbapt{ 391274880Sbapt 392274880Sbapt puts("</BODY>\n" 393274880Sbapt "</HTML>"); 394274880Sbapt} 395274880Sbapt 396274880Sbaptstatic void 397274880Sbaptresp_searchform(const struct req *req) 398274880Sbapt{ 399274880Sbapt int i; 400274880Sbapt 401274880Sbapt puts(CUSTOMIZE_BEGIN); 402274880Sbapt puts("<!-- Begin search form. //-->"); 403274880Sbapt printf("<DIV ID=\"mancgi\">\n" 404274880Sbapt "<FORM ACTION=\"%s\" METHOD=\"get\">\n" 405274880Sbapt "<FIELDSET>\n" 406274880Sbapt "<LEGEND>Manual Page Search Parameters</LEGEND>\n", 407274880Sbapt scriptname); 408274880Sbapt 409274880Sbapt /* Write query input box. */ 410274880Sbapt 411274880Sbapt printf( "<TABLE><TR><TD>\n" 412274880Sbapt "<INPUT TYPE=\"text\" NAME=\"query\" VALUE=\""); 413274880Sbapt if (NULL != req->q.query) 414274880Sbapt html_print(req->q.query); 415274880Sbapt puts("\" SIZE=\"40\">"); 416274880Sbapt 417274880Sbapt /* Write submission and reset buttons. */ 418274880Sbapt 419274880Sbapt printf( "<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n" 420274880Sbapt "<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n"); 421274880Sbapt 422274880Sbapt /* Write show radio button */ 423274880Sbapt 424274880Sbapt printf( "</TD><TD>\n" 425274880Sbapt "<INPUT TYPE=\"radio\" "); 426274880Sbapt if (req->q.equal) 427274880Sbapt printf("CHECKED=\"checked\" "); 428274880Sbapt printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n" 429274880Sbapt "<LABEL FOR=\"show\">Show named manual page</LABEL>\n"); 430274880Sbapt 431274880Sbapt /* Write section selector. */ 432274880Sbapt 433274880Sbapt puts( "</TD></TR><TR><TD>\n" 434274880Sbapt "<SELECT NAME=\"sec\">"); 435274880Sbapt for (i = 0; i < sec_MAX; i++) { 436274880Sbapt printf("<OPTION VALUE=\"%s\"", sec_numbers[i]); 437274880Sbapt if (NULL != req->q.sec && 438274880Sbapt 0 == strcmp(sec_numbers[i], req->q.sec)) 439274880Sbapt printf(" SELECTED=\"selected\""); 440274880Sbapt printf(">%s</OPTION>\n", sec_names[i]); 441274880Sbapt } 442274880Sbapt puts("</SELECT>"); 443274880Sbapt 444274880Sbapt /* Write architecture selector. */ 445274880Sbapt 446274880Sbapt printf( "<SELECT NAME=\"arch\">\n" 447274880Sbapt "<OPTION VALUE=\"default\""); 448274880Sbapt if (NULL == req->q.arch) 449274880Sbapt printf(" SELECTED=\"selected\""); 450274880Sbapt puts(">All Architectures</OPTION>"); 451274880Sbapt for (i = 0; i < arch_MAX; i++) { 452274880Sbapt printf("<OPTION VALUE=\"%s\"", arch_names[i]); 453274880Sbapt if (NULL != req->q.arch && 454274880Sbapt 0 == strcmp(arch_names[i], req->q.arch)) 455274880Sbapt printf(" SELECTED=\"selected\""); 456274880Sbapt printf(">%s</OPTION>\n", arch_names[i]); 457274880Sbapt } 458274880Sbapt puts("</SELECT>"); 459274880Sbapt 460274880Sbapt /* Write manpath selector. */ 461274880Sbapt 462274880Sbapt if (req->psz > 1) { 463274880Sbapt puts("<SELECT NAME=\"manpath\">"); 464274880Sbapt for (i = 0; i < (int)req->psz; i++) { 465274880Sbapt printf("<OPTION "); 466275432Sbapt if (strcmp(req->q.manpath, req->p[i]) == 0) 467274880Sbapt printf("SELECTED=\"selected\" "); 468274880Sbapt printf("VALUE=\""); 469274880Sbapt html_print(req->p[i]); 470274880Sbapt printf("\">"); 471274880Sbapt html_print(req->p[i]); 472274880Sbapt puts("</OPTION>"); 473274880Sbapt } 474274880Sbapt puts("</SELECT>"); 475274880Sbapt } 476274880Sbapt 477274880Sbapt /* Write search radio button */ 478274880Sbapt 479274880Sbapt printf( "</TD><TD>\n" 480274880Sbapt "<INPUT TYPE=\"radio\" "); 481274880Sbapt if (0 == req->q.equal) 482274880Sbapt printf("CHECKED=\"checked\" "); 483274880Sbapt printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n" 484274880Sbapt "<LABEL FOR=\"search\">Search with apropos query</LABEL>\n"); 485274880Sbapt 486274880Sbapt puts("</TD></TR></TABLE>\n" 487274880Sbapt "</FIELDSET>\n" 488274880Sbapt "</FORM>\n" 489274880Sbapt "</DIV>"); 490274880Sbapt puts("<!-- End search form. //-->"); 491274880Sbapt} 492274880Sbapt 493274880Sbaptstatic int 494274880Sbaptvalidate_urifrag(const char *frag) 495274880Sbapt{ 496274880Sbapt 497274880Sbapt while ('\0' != *frag) { 498274880Sbapt if ( ! (isalnum((unsigned char)*frag) || 499274880Sbapt '-' == *frag || '.' == *frag || 500274880Sbapt '/' == *frag || '_' == *frag)) 501274880Sbapt return(0); 502274880Sbapt frag++; 503274880Sbapt } 504274880Sbapt return(1); 505274880Sbapt} 506274880Sbapt 507274880Sbaptstatic int 508274880Sbaptvalidate_manpath(const struct req *req, const char* manpath) 509274880Sbapt{ 510274880Sbapt size_t i; 511274880Sbapt 512274880Sbapt if ( ! strcmp(manpath, "mandoc")) 513274880Sbapt return(1); 514274880Sbapt 515274880Sbapt for (i = 0; i < req->psz; i++) 516274880Sbapt if ( ! strcmp(manpath, req->p[i])) 517274880Sbapt return(1); 518274880Sbapt 519274880Sbapt return(0); 520274880Sbapt} 521274880Sbapt 522274880Sbaptstatic int 523274880Sbaptvalidate_filename(const char *file) 524274880Sbapt{ 525274880Sbapt 526274880Sbapt if ('.' == file[0] && '/' == file[1]) 527274880Sbapt file += 2; 528274880Sbapt 529274880Sbapt return ( ! (strstr(file, "../") || strstr(file, "/..") || 530274880Sbapt (strncmp(file, "man", 3) && strncmp(file, "cat", 3)))); 531274880Sbapt} 532274880Sbapt 533274880Sbaptstatic void 534274880Sbaptpg_index(const struct req *req) 535274880Sbapt{ 536274880Sbapt 537274880Sbapt resp_begin_html(200, NULL); 538274880Sbapt resp_searchform(req); 539274880Sbapt printf("<P>\n" 540274880Sbapt "This web interface is documented in the\n" 541274880Sbapt "<A HREF=\"%s/mandoc/man8/man.cgi.8\">man.cgi</A>\n" 542274880Sbapt "manual, and the\n" 543274880Sbapt "<A HREF=\"%s/mandoc/man1/apropos.1\">apropos</A>\n" 544274880Sbapt "manual explains the query syntax.\n" 545274880Sbapt "</P>\n", 546274880Sbapt scriptname, scriptname); 547274880Sbapt resp_end_html(); 548274880Sbapt} 549274880Sbapt 550274880Sbaptstatic void 551274880Sbaptpg_noresult(const struct req *req, const char *msg) 552274880Sbapt{ 553274880Sbapt resp_begin_html(200, NULL); 554274880Sbapt resp_searchform(req); 555274880Sbapt puts("<P>"); 556274880Sbapt puts(msg); 557274880Sbapt puts("</P>"); 558274880Sbapt resp_end_html(); 559274880Sbapt} 560274880Sbapt 561274880Sbaptstatic void 562274880Sbaptpg_error_badrequest(const char *msg) 563274880Sbapt{ 564274880Sbapt 565274880Sbapt resp_begin_html(400, "Bad Request"); 566274880Sbapt puts("<H1>Bad Request</H1>\n" 567274880Sbapt "<P>\n"); 568274880Sbapt puts(msg); 569274880Sbapt printf("Try again from the\n" 570274880Sbapt "<A HREF=\"%s\">main page</A>.\n" 571274880Sbapt "</P>", scriptname); 572274880Sbapt resp_end_html(); 573274880Sbapt} 574274880Sbapt 575274880Sbaptstatic void 576274880Sbaptpg_error_internal(void) 577274880Sbapt{ 578274880Sbapt resp_begin_html(500, "Internal Server Error"); 579274880Sbapt puts("<P>Internal Server Error</P>"); 580274880Sbapt resp_end_html(); 581274880Sbapt} 582274880Sbapt 583274880Sbaptstatic void 584274880Sbaptpg_searchres(const struct req *req, struct manpage *r, size_t sz) 585274880Sbapt{ 586274880Sbapt char *arch, *archend; 587274880Sbapt size_t i, iuse, isec; 588274880Sbapt int archprio, archpriouse; 589274880Sbapt int prio, priouse; 590274880Sbapt char sec; 591274880Sbapt 592274880Sbapt for (i = 0; i < sz; i++) { 593274880Sbapt if (validate_filename(r[i].file)) 594274880Sbapt continue; 595274880Sbapt fprintf(stderr, "invalid filename %s in %s database\n", 596274880Sbapt r[i].file, req->q.manpath); 597274880Sbapt pg_error_internal(); 598274880Sbapt return; 599274880Sbapt } 600274880Sbapt 601274880Sbapt if (1 == sz) { 602274880Sbapt /* 603274880Sbapt * If we have just one result, then jump there now 604274880Sbapt * without any delay. 605274880Sbapt */ 606274880Sbapt printf("Status: 303 See Other\r\n"); 607274880Sbapt printf("Location: http://%s%s/%s/%s?", 608274880Sbapt HTTP_HOST, scriptname, req->q.manpath, r[0].file); 609274880Sbapt http_printquery(req, "&"); 610274880Sbapt printf("\r\n" 611274880Sbapt "Content-Type: text/html; charset=utf-8\r\n" 612274880Sbapt "\r\n"); 613274880Sbapt return; 614274880Sbapt } 615274880Sbapt 616274880Sbapt resp_begin_html(200, NULL); 617274880Sbapt resp_searchform(req); 618274880Sbapt puts("<DIV CLASS=\"results\">"); 619274880Sbapt puts("<TABLE>"); 620274880Sbapt 621274880Sbapt for (i = 0; i < sz; i++) { 622274880Sbapt printf("<TR>\n" 623274880Sbapt "<TD CLASS=\"title\">\n" 624279527Sbapt "<A HREF=\"%s/%s/%s?", 625274880Sbapt scriptname, req->q.manpath, r[i].file); 626274880Sbapt http_printquery(req, "&"); 627274880Sbapt printf("\">"); 628274880Sbapt html_print(r[i].names); 629274880Sbapt printf("</A>\n" 630274880Sbapt "</TD>\n" 631274880Sbapt "<TD CLASS=\"desc\">"); 632274880Sbapt html_print(r[i].output); 633274880Sbapt puts("</TD>\n" 634274880Sbapt "</TR>"); 635274880Sbapt } 636274880Sbapt 637274880Sbapt puts("</TABLE>\n" 638274880Sbapt "</DIV>"); 639274880Sbapt 640274880Sbapt /* 641274880Sbapt * In man(1) mode, show one of the pages 642274880Sbapt * even if more than one is found. 643274880Sbapt */ 644274880Sbapt 645274880Sbapt if (req->q.equal) { 646274880Sbapt puts("<HR>"); 647274880Sbapt iuse = 0; 648274880Sbapt priouse = 10; 649274880Sbapt archpriouse = 3; 650274880Sbapt for (i = 0; i < sz; i++) { 651274880Sbapt isec = strcspn(r[i].file, "123456789"); 652274880Sbapt sec = r[i].file[isec]; 653274880Sbapt if ('\0' == sec) 654274880Sbapt continue; 655274880Sbapt prio = sec_prios[sec - '1']; 656274880Sbapt if (NULL == req->q.arch) { 657274880Sbapt archprio = 658274880Sbapt (NULL == (arch = strchr( 659274880Sbapt r[i].file + isec, '/'))) ? 3 : 660274880Sbapt (NULL == (archend = strchr( 661274880Sbapt arch + 1, '/'))) ? 0 : 662274880Sbapt strncmp(arch, "amd64/", 663274880Sbapt archend - arch) ? 2 : 1; 664274880Sbapt if (archprio < archpriouse) { 665274880Sbapt archpriouse = archprio; 666274880Sbapt priouse = prio; 667274880Sbapt iuse = i; 668274880Sbapt continue; 669274880Sbapt } 670274880Sbapt if (archprio > archpriouse) 671274880Sbapt continue; 672274880Sbapt } 673274880Sbapt if (prio >= priouse) 674274880Sbapt continue; 675274880Sbapt priouse = prio; 676274880Sbapt iuse = i; 677274880Sbapt } 678274880Sbapt resp_show(req, r[iuse].file); 679274880Sbapt } 680274880Sbapt 681274880Sbapt resp_end_html(); 682274880Sbapt} 683274880Sbapt 684274880Sbaptstatic void 685274880Sbaptcatman(const struct req *req, const char *file) 686274880Sbapt{ 687274880Sbapt FILE *f; 688274880Sbapt size_t len; 689274880Sbapt int i; 690274880Sbapt char *p; 691274880Sbapt int italic, bold; 692274880Sbapt 693274880Sbapt if (NULL == (f = fopen(file, "r"))) { 694274880Sbapt puts("<P>You specified an invalid manual file.</P>"); 695274880Sbapt return; 696274880Sbapt } 697274880Sbapt 698274880Sbapt puts("<DIV CLASS=\"catman\">\n" 699274880Sbapt "<PRE>"); 700274880Sbapt 701274880Sbapt while (NULL != (p = fgetln(f, &len))) { 702274880Sbapt bold = italic = 0; 703274880Sbapt for (i = 0; i < (int)len - 1; i++) { 704279527Sbapt /* 705274880Sbapt * This means that the catpage is out of state. 706274880Sbapt * Ignore it and keep going (although the 707274880Sbapt * catpage is bogus). 708274880Sbapt */ 709274880Sbapt 710274880Sbapt if ('\b' == p[i] || '\n' == p[i]) 711274880Sbapt continue; 712274880Sbapt 713274880Sbapt /* 714274880Sbapt * Print a regular character. 715274880Sbapt * Close out any bold/italic scopes. 716274880Sbapt * If we're in back-space mode, make sure we'll 717274880Sbapt * have something to enter when we backspace. 718274880Sbapt */ 719274880Sbapt 720274880Sbapt if ('\b' != p[i + 1]) { 721274880Sbapt if (italic) 722274880Sbapt printf("</I>"); 723274880Sbapt if (bold) 724274880Sbapt printf("</B>"); 725274880Sbapt italic = bold = 0; 726274880Sbapt html_putchar(p[i]); 727274880Sbapt continue; 728274880Sbapt } else if (i + 2 >= (int)len) 729274880Sbapt continue; 730274880Sbapt 731274880Sbapt /* Italic mode. */ 732274880Sbapt 733274880Sbapt if ('_' == p[i]) { 734274880Sbapt if (bold) 735274880Sbapt printf("</B>"); 736274880Sbapt if ( ! italic) 737274880Sbapt printf("<I>"); 738274880Sbapt bold = 0; 739274880Sbapt italic = 1; 740274880Sbapt i += 2; 741274880Sbapt html_putchar(p[i]); 742274880Sbapt continue; 743274880Sbapt } 744274880Sbapt 745279527Sbapt /* 746274880Sbapt * Handle funny behaviour troff-isms. 747274880Sbapt * These grok'd from the original man2html.c. 748274880Sbapt */ 749274880Sbapt 750274880Sbapt if (('+' == p[i] && 'o' == p[i + 2]) || 751274880Sbapt ('o' == p[i] && '+' == p[i + 2]) || 752274880Sbapt ('|' == p[i] && '=' == p[i + 2]) || 753274880Sbapt ('=' == p[i] && '|' == p[i + 2]) || 754274880Sbapt ('*' == p[i] && '=' == p[i + 2]) || 755274880Sbapt ('=' == p[i] && '*' == p[i + 2]) || 756274880Sbapt ('*' == p[i] && '|' == p[i + 2]) || 757274880Sbapt ('|' == p[i] && '*' == p[i + 2])) { 758274880Sbapt if (italic) 759274880Sbapt printf("</I>"); 760274880Sbapt if (bold) 761274880Sbapt printf("</B>"); 762274880Sbapt italic = bold = 0; 763274880Sbapt putchar('*'); 764274880Sbapt i += 2; 765274880Sbapt continue; 766274880Sbapt } else if (('|' == p[i] && '-' == p[i + 2]) || 767274880Sbapt ('-' == p[i] && '|' == p[i + 1]) || 768274880Sbapt ('+' == p[i] && '-' == p[i + 1]) || 769274880Sbapt ('-' == p[i] && '+' == p[i + 1]) || 770274880Sbapt ('+' == p[i] && '|' == p[i + 1]) || 771274880Sbapt ('|' == p[i] && '+' == p[i + 1])) { 772274880Sbapt if (italic) 773274880Sbapt printf("</I>"); 774274880Sbapt if (bold) 775274880Sbapt printf("</B>"); 776274880Sbapt italic = bold = 0; 777274880Sbapt putchar('+'); 778274880Sbapt i += 2; 779274880Sbapt continue; 780274880Sbapt } 781274880Sbapt 782274880Sbapt /* Bold mode. */ 783279527Sbapt 784274880Sbapt if (italic) 785274880Sbapt printf("</I>"); 786274880Sbapt if ( ! bold) 787274880Sbapt printf("<B>"); 788274880Sbapt bold = 1; 789274880Sbapt italic = 0; 790274880Sbapt i += 2; 791274880Sbapt html_putchar(p[i]); 792274880Sbapt } 793274880Sbapt 794279527Sbapt /* 795274880Sbapt * Clean up the last character. 796279527Sbapt * We can get to a newline; don't print that. 797274880Sbapt */ 798274880Sbapt 799274880Sbapt if (italic) 800274880Sbapt printf("</I>"); 801274880Sbapt if (bold) 802274880Sbapt printf("</B>"); 803274880Sbapt 804274880Sbapt if (i == (int)len - 1 && '\n' != p[i]) 805274880Sbapt html_putchar(p[i]); 806274880Sbapt 807274880Sbapt putchar('\n'); 808274880Sbapt } 809274880Sbapt 810274880Sbapt puts("</PRE>\n" 811274880Sbapt "</DIV>"); 812274880Sbapt 813274880Sbapt fclose(f); 814274880Sbapt} 815274880Sbapt 816274880Sbaptstatic void 817274880Sbaptformat(const struct req *req, const char *file) 818274880Sbapt{ 819274880Sbapt struct mparse *mp; 820275432Sbapt struct mchars *mchars; 821274880Sbapt struct mdoc *mdoc; 822274880Sbapt struct man *man; 823274880Sbapt void *vp; 824274880Sbapt char *opts; 825274880Sbapt int fd; 826274880Sbapt int usepath; 827274880Sbapt 828274880Sbapt if (-1 == (fd = open(file, O_RDONLY, 0))) { 829274880Sbapt puts("<P>You specified an invalid manual file.</P>"); 830274880Sbapt return; 831274880Sbapt } 832274880Sbapt 833275432Sbapt mchars = mchars_alloc(); 834279527Sbapt mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_BADARG, NULL, 835275432Sbapt mchars, req->q.manpath); 836279527Sbapt mparse_readfd(mp, fd, file); 837274880Sbapt close(fd); 838274880Sbapt 839274880Sbapt usepath = strcmp(req->q.manpath, req->p[0]); 840274880Sbapt mandoc_asprintf(&opts, 841274880Sbapt "fragment,man=%s?query=%%N&sec=%%S%s%s%s%s", 842274880Sbapt scriptname, 843274880Sbapt req->q.arch ? "&arch=" : "", 844274880Sbapt req->q.arch ? req->q.arch : "", 845274880Sbapt usepath ? "&manpath=" : "", 846274880Sbapt usepath ? req->q.manpath : ""); 847274880Sbapt 848274880Sbapt mparse_result(mp, &mdoc, &man, NULL); 849274880Sbapt if (NULL == man && NULL == mdoc) { 850274880Sbapt fprintf(stderr, "fatal mandoc error: %s/%s\n", 851274880Sbapt req->q.manpath, file); 852274880Sbapt pg_error_internal(); 853274880Sbapt mparse_free(mp); 854275432Sbapt mchars_free(mchars); 855274880Sbapt return; 856274880Sbapt } 857274880Sbapt 858275432Sbapt vp = html_alloc(mchars, opts); 859274880Sbapt 860274880Sbapt if (NULL != mdoc) 861274880Sbapt html_mdoc(vp, mdoc); 862274880Sbapt else 863274880Sbapt html_man(vp, man); 864274880Sbapt 865274880Sbapt html_free(vp); 866274880Sbapt mparse_free(mp); 867275432Sbapt mchars_free(mchars); 868274880Sbapt free(opts); 869274880Sbapt} 870274880Sbapt 871274880Sbaptstatic void 872274880Sbaptresp_show(const struct req *req, const char *file) 873274880Sbapt{ 874274880Sbapt 875274880Sbapt if ('.' == file[0] && '/' == file[1]) 876274880Sbapt file += 2; 877274880Sbapt 878274880Sbapt if ('c' == *file) 879274880Sbapt catman(req, file); 880274880Sbapt else 881274880Sbapt format(req, file); 882274880Sbapt} 883274880Sbapt 884274880Sbaptstatic void 885274880Sbaptpg_show(struct req *req, const char *fullpath) 886274880Sbapt{ 887274880Sbapt char *manpath; 888274880Sbapt const char *file; 889274880Sbapt 890274880Sbapt if ((file = strchr(fullpath, '/')) == NULL) { 891274880Sbapt pg_error_badrequest( 892274880Sbapt "You did not specify a page to show."); 893274880Sbapt return; 894279527Sbapt } 895274880Sbapt manpath = mandoc_strndup(fullpath, file - fullpath); 896274880Sbapt file++; 897274880Sbapt 898274880Sbapt if ( ! validate_manpath(req, manpath)) { 899274880Sbapt pg_error_badrequest( 900274880Sbapt "You specified an invalid manpath."); 901274880Sbapt free(manpath); 902274880Sbapt return; 903274880Sbapt } 904274880Sbapt 905274880Sbapt /* 906274880Sbapt * Begin by chdir()ing into the manpath. 907274880Sbapt * This way we can pick up the database files, which are 908274880Sbapt * relative to the manpath root. 909274880Sbapt */ 910274880Sbapt 911274880Sbapt if (chdir(manpath) == -1) { 912274880Sbapt fprintf(stderr, "chdir %s: %s\n", 913274880Sbapt manpath, strerror(errno)); 914274880Sbapt pg_error_internal(); 915274880Sbapt free(manpath); 916274880Sbapt return; 917274880Sbapt } 918274880Sbapt 919274880Sbapt if (strcmp(manpath, "mandoc")) { 920274880Sbapt free(req->q.manpath); 921274880Sbapt req->q.manpath = manpath; 922274880Sbapt } else 923274880Sbapt free(manpath); 924274880Sbapt 925274880Sbapt if ( ! validate_filename(file)) { 926274880Sbapt pg_error_badrequest( 927274880Sbapt "You specified an invalid manual file."); 928274880Sbapt return; 929274880Sbapt } 930274880Sbapt 931274880Sbapt resp_begin_html(200, NULL); 932274880Sbapt resp_searchform(req); 933274880Sbapt resp_show(req, file); 934274880Sbapt resp_end_html(); 935274880Sbapt} 936274880Sbapt 937274880Sbaptstatic void 938274880Sbaptpg_search(const struct req *req) 939274880Sbapt{ 940274880Sbapt struct mansearch search; 941274880Sbapt struct manpaths paths; 942274880Sbapt struct manpage *res; 943275432Sbapt char **argv; 944275432Sbapt char *query, *rp, *wp; 945274880Sbapt size_t ressz; 946275432Sbapt int argc; 947274880Sbapt 948274880Sbapt /* 949274880Sbapt * Begin by chdir()ing into the root of the manpath. 950274880Sbapt * This way we can pick up the database files, which are 951274880Sbapt * relative to the manpath root. 952274880Sbapt */ 953274880Sbapt 954274880Sbapt if (-1 == (chdir(req->q.manpath))) { 955274880Sbapt fprintf(stderr, "chdir %s: %s\n", 956274880Sbapt req->q.manpath, strerror(errno)); 957274880Sbapt pg_error_internal(); 958274880Sbapt return; 959274880Sbapt } 960274880Sbapt 961274880Sbapt search.arch = req->q.arch; 962274880Sbapt search.sec = req->q.sec; 963275432Sbapt search.outkey = "Nd"; 964275432Sbapt search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR; 965275432Sbapt search.firstmatch = 1; 966274880Sbapt 967274880Sbapt paths.sz = 1; 968274880Sbapt paths.paths = mandoc_malloc(sizeof(char *)); 969274880Sbapt paths.paths[0] = mandoc_strdup("."); 970274880Sbapt 971274880Sbapt /* 972275432Sbapt * Break apart at spaces with backslash-escaping. 973274880Sbapt */ 974274880Sbapt 975275432Sbapt argc = 0; 976275432Sbapt argv = NULL; 977275432Sbapt rp = query = mandoc_strdup(req->q.query); 978275432Sbapt for (;;) { 979275432Sbapt while (isspace((unsigned char)*rp)) 980275432Sbapt rp++; 981275432Sbapt if (*rp == '\0') 982275432Sbapt break; 983275432Sbapt argv = mandoc_reallocarray(argv, argc + 1, sizeof(char *)); 984275432Sbapt argv[argc++] = wp = rp; 985275432Sbapt for (;;) { 986275432Sbapt if (isspace((unsigned char)*rp)) { 987275432Sbapt *wp = '\0'; 988275432Sbapt rp++; 989275432Sbapt break; 990275432Sbapt } 991275432Sbapt if (rp[0] == '\\' && rp[1] != '\0') 992275432Sbapt rp++; 993275432Sbapt if (wp != rp) 994275432Sbapt *wp = *rp; 995275432Sbapt if (*rp == '\0') 996275432Sbapt break; 997275432Sbapt wp++; 998275432Sbapt rp++; 999275432Sbapt } 1000274880Sbapt } 1001274880Sbapt 1002275432Sbapt if (0 == mansearch(&search, &paths, argc, argv, &res, &ressz)) 1003274880Sbapt pg_noresult(req, "You entered an invalid query."); 1004274880Sbapt else if (0 == ressz) 1005274880Sbapt pg_noresult(req, "No results found."); 1006274880Sbapt else 1007274880Sbapt pg_searchres(req, res, ressz); 1008274880Sbapt 1009275432Sbapt free(query); 1010275432Sbapt mansearch_free(res, ressz); 1011274880Sbapt free(paths.paths[0]); 1012274880Sbapt free(paths.paths); 1013274880Sbapt} 1014274880Sbapt 1015274880Sbaptint 1016274880Sbaptmain(void) 1017274880Sbapt{ 1018274880Sbapt struct req req; 1019275432Sbapt struct itimerval itimer; 1020274880Sbapt const char *path; 1021274880Sbapt const char *querystring; 1022274880Sbapt int i; 1023274880Sbapt 1024275432Sbapt /* Poor man's ReDoS mitigation. */ 1025275432Sbapt 1026275432Sbapt itimer.it_value.tv_sec = 2; 1027275432Sbapt itimer.it_value.tv_usec = 0; 1028275432Sbapt itimer.it_interval.tv_sec = 2; 1029275432Sbapt itimer.it_interval.tv_usec = 0; 1030275432Sbapt if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) { 1031275432Sbapt fprintf(stderr, "setitimer: %s\n", strerror(errno)); 1032275432Sbapt pg_error_internal(); 1033275432Sbapt return(EXIT_FAILURE); 1034275432Sbapt } 1035275432Sbapt 1036274880Sbapt /* Scan our run-time environment. */ 1037274880Sbapt 1038274880Sbapt if (NULL == (scriptname = getenv("SCRIPT_NAME"))) 1039274880Sbapt scriptname = ""; 1040274880Sbapt 1041274880Sbapt if ( ! validate_urifrag(scriptname)) { 1042274880Sbapt fprintf(stderr, "unsafe SCRIPT_NAME \"%s\"\n", 1043274880Sbapt scriptname); 1044274880Sbapt pg_error_internal(); 1045274880Sbapt return(EXIT_FAILURE); 1046274880Sbapt } 1047274880Sbapt 1048274880Sbapt /* 1049274880Sbapt * First we change directory into the MAN_DIR so that 1050274880Sbapt * subsequent scanning for manpath directories is rooted 1051274880Sbapt * relative to the same position. 1052274880Sbapt */ 1053274880Sbapt 1054274880Sbapt if (-1 == chdir(MAN_DIR)) { 1055274880Sbapt fprintf(stderr, "MAN_DIR: %s: %s\n", 1056274880Sbapt MAN_DIR, strerror(errno)); 1057274880Sbapt pg_error_internal(); 1058274880Sbapt return(EXIT_FAILURE); 1059279527Sbapt } 1060274880Sbapt 1061274880Sbapt memset(&req, 0, sizeof(struct req)); 1062274880Sbapt pathgen(&req); 1063274880Sbapt 1064274880Sbapt /* Next parse out the query string. */ 1065274880Sbapt 1066274880Sbapt if (NULL != (querystring = getenv("QUERY_STRING"))) 1067274880Sbapt http_parse(&req, querystring); 1068274880Sbapt 1069275432Sbapt if (req.q.manpath == NULL) 1070275432Sbapt req.q.manpath = mandoc_strdup(req.p[0]); 1071275432Sbapt else if ( ! validate_manpath(&req, req.q.manpath)) { 1072274880Sbapt pg_error_badrequest( 1073274880Sbapt "You specified an invalid manpath."); 1074274880Sbapt return(EXIT_FAILURE); 1075274880Sbapt } 1076274880Sbapt 1077274880Sbapt if ( ! (NULL == req.q.arch || validate_urifrag(req.q.arch))) { 1078274880Sbapt pg_error_badrequest( 1079274880Sbapt "You specified an invalid architecture."); 1080274880Sbapt return(EXIT_FAILURE); 1081274880Sbapt } 1082274880Sbapt 1083274880Sbapt /* Dispatch to the three different pages. */ 1084274880Sbapt 1085274880Sbapt path = getenv("PATH_INFO"); 1086274880Sbapt if (NULL == path) 1087274880Sbapt path = ""; 1088274880Sbapt else if ('/' == *path) 1089274880Sbapt path++; 1090274880Sbapt 1091274880Sbapt if ('\0' != *path) 1092274880Sbapt pg_show(&req, path); 1093274880Sbapt else if (NULL != req.q.query) 1094274880Sbapt pg_search(&req); 1095274880Sbapt else 1096274880Sbapt pg_index(&req); 1097274880Sbapt 1098274880Sbapt free(req.q.manpath); 1099274880Sbapt free(req.q.arch); 1100274880Sbapt free(req.q.sec); 1101274880Sbapt free(req.q.query); 1102274880Sbapt for (i = 0; i < (int)req.psz; i++) 1103274880Sbapt free(req.p[i]); 1104274880Sbapt free(req.p); 1105274880Sbapt return(EXIT_SUCCESS); 1106274880Sbapt} 1107274880Sbapt 1108274880Sbapt/* 1109274880Sbapt * Scan for indexable paths. 1110274880Sbapt */ 1111274880Sbaptstatic void 1112274880Sbaptpathgen(struct req *req) 1113274880Sbapt{ 1114274880Sbapt FILE *fp; 1115274880Sbapt char *dp; 1116274880Sbapt size_t dpsz; 1117274880Sbapt 1118274880Sbapt if (NULL == (fp = fopen("manpath.conf", "r"))) { 1119274880Sbapt fprintf(stderr, "%s/manpath.conf: %s\n", 1120274880Sbapt MAN_DIR, strerror(errno)); 1121274880Sbapt pg_error_internal(); 1122274880Sbapt exit(EXIT_FAILURE); 1123274880Sbapt } 1124274880Sbapt 1125274880Sbapt while (NULL != (dp = fgetln(fp, &dpsz))) { 1126274880Sbapt if ('\n' == dp[dpsz - 1]) 1127274880Sbapt dpsz--; 1128274880Sbapt req->p = mandoc_realloc(req->p, 1129274880Sbapt (req->psz + 1) * sizeof(char *)); 1130274880Sbapt dp = mandoc_strndup(dp, dpsz); 1131274880Sbapt if ( ! validate_urifrag(dp)) { 1132274880Sbapt fprintf(stderr, "%s/manpath.conf contains " 1133274880Sbapt "unsafe path \"%s\"\n", MAN_DIR, dp); 1134274880Sbapt pg_error_internal(); 1135274880Sbapt exit(EXIT_FAILURE); 1136274880Sbapt } 1137274880Sbapt if (NULL != strchr(dp, '/')) { 1138274880Sbapt fprintf(stderr, "%s/manpath.conf contains " 1139274880Sbapt "path with slash \"%s\"\n", MAN_DIR, dp); 1140274880Sbapt pg_error_internal(); 1141274880Sbapt exit(EXIT_FAILURE); 1142274880Sbapt } 1143274880Sbapt req->p[req->psz++] = dp; 1144274880Sbapt } 1145274880Sbapt 1146274880Sbapt if ( req->p == NULL ) { 1147274880Sbapt fprintf(stderr, "%s/manpath.conf is empty\n", MAN_DIR); 1148274880Sbapt pg_error_internal(); 1149274880Sbapt exit(EXIT_FAILURE); 1150274880Sbapt } 1151274880Sbapt} 1152