===================================================================
RCS file: /cvs/mandoc/cgi.c,v
retrieving revision 1.7
retrieving revision 1.94
diff -u -p -r1.7 -r1.94
--- mandoc/cgi.c 2011/11/24 12:27:18 1.7
+++ mandoc/cgi.c 2014/08/17 03:24:47 1.94
@@ -1,6 +1,7 @@
-/* $Id: cgi.c,v 1.7 2011/11/24 12:27:18 kristaps Exp $ */
+/* $Id: cgi.c,v 1.94 2014/08/17 03:24:47 schwarze Exp $ */
/*
- * Copyright (c) 2011 Kristaps Dzonsons Requested manual not found. Your search didn't work. \n"
+ "This web interface is documented in the\n"
+ "man.cgi\n"
+ "manual, and the\n"
+ "apropos\n"
+ "manual explains the query syntax.\n"
+ " Generic badness happened. ");
+ puts(msg);
+ puts(" \n");
+ puts(msg);
+ printf("Try again from the\n"
+ "main page.\n"
+ " Your database is broken. Internal Server Error No results found.Bad Request
\n"
+ "");
- if (0 == sz)
- puts("
\n"
+ "\n"
+ " ");
+ }
- for (i = 0; i < (int)sz; i++) {
- printf("\n"
+ "q.manpath, r[i].file);
+ http_printquery(req, "&");
+ printf("\">");
+ html_print(r[i].names);
+ printf("\n"
+ " \n"
+ "");
+ html_print(r[i].output);
+ puts(" \n"
+ "
");
+ iuse = 0;
+ priouse = 10;
+ archpriouse = 3;
+ for (i = 0; i < sz; i++) {
+ isec = strcspn(r[i].file, "123456789");
+ sec = r[i].file[isec];
+ if ('\0' == sec)
+ continue;
+ prio = sec_prios[sec - '1'];
+ if (NULL == req->q.arch) {
+ archprio =
+ (NULL == (arch = strchr(
+ r[i].file + isec, '/'))) ? 3 :
+ (NULL == (archend = strchr(
+ arch + 1, '/'))) ? 0 :
+ strncmp(arch, "amd64/",
+ archend - arch) ? 2 : 1;
+ if (archprio < archpriouse) {
+ archpriouse = archprio;
+ priouse = prio;
+ iuse = i;
+ continue;
+ }
+ if (archprio > archpriouse)
+ continue;
+ }
+ if (prio >= priouse)
+ continue;
+ priouse = prio;
+ iuse = i;
}
- printf(") ");
- html_print(r[i].desc);
- puts("
You specified an invalid manual file.
"); + return; + } -static void -format_insecure(const char *file) -{ - pid_t pid; - char cmd[MAXPATHLEN]; + puts(""); - strlcpy(cmd, "man=", MAXPATHLEN); - strlcat(cmd, progname, MAXPATHLEN); - strlcat(cmd, "/search?expr=%N&sec=%S", MAXPATHLEN); + while (NULL != (p = fgetln(f, &len))) { + bold = italic = 0; + for (i = 0; i < (int)len - 1; i++) { + /* + * This means that the catpage is out of state. + * Ignore it and keep going (although the + * catpage is bogus). + */ - /* Get ready to call the child mandoc(1) process. */ + if ('\b' == p[i] || '\n' == p[i]) + continue; - if (-1 == (pid = fork())) - exit(EXIT_FAILURE); + /* + * Print a regular character. + * Close out any bold/italic scopes. + * If we're in back-space mode, make sure we'll + * have something to enter when we backspace. + */ - if (pid > 0) { - waitpid(pid, NULL, 0); - return; - } + if ('\b' != p[i + 1]) { + if (italic) + printf(""); + if (bold) + printf(""); + italic = bold = 0; + html_putchar(p[i]); + continue; + } else if (i + 2 >= (int)len) + continue; - dup2(STDOUT_FILENO, STDERR_FILENO); + /* Italic mode. */ - puts("Content-Type: text/html; charset=utf-8\n"); + if ('_' == p[i]) { + if (bold) + printf(""); + if ( ! italic) + printf(""); + bold = 0; + italic = 1; + i += 2; + html_putchar(p[i]); + continue; + } - fflush(stdout); + /* + * Handle funny behaviour troff-isms. + * These grok'd from the original man2html.c. + */ - execlp("mandoc", "mandoc", "-T", - "html", "-O", cmd, file, (char *)NULL); + if (('+' == p[i] && 'o' == p[i + 2]) || + ('o' == p[i] && '+' == p[i + 2]) || + ('|' == p[i] && '=' == p[i + 2]) || + ('=' == p[i] && '|' == p[i + 2]) || + ('*' == p[i] && '=' == p[i + 2]) || + ('=' == p[i] && '*' == p[i + 2]) || + ('*' == p[i] && '|' == p[i + 2]) || + ('|' == p[i] && '*' == p[i + 2])) { + if (italic) + printf(""); + if (bold) + printf(""); + italic = bold = 0; + putchar('*'); + i += 2; + continue; + } else if (('|' == p[i] && '-' == p[i + 2]) || + ('-' == p[i] && '|' == p[i + 1]) || + ('+' == p[i] && '-' == p[i + 1]) || + ('-' == p[i] && '+' == p[i + 1]) || + ('+' == p[i] && '|' == p[i + 1]) || + ('|' == p[i] && '+' == p[i + 1])) { + if (italic) + printf(""); + if (bold) + printf(""); + italic = bold = 0; + putchar('+'); + i += 2; + continue; + } + + /* Bold mode. */ + + if (italic) + printf(""); + if ( ! bold) + printf(""); + bold = 1; + italic = 0; + i += 2; + html_putchar(p[i]); + } + + /* + * Clean up the last character. + * We can get to a newline; don't print that. + */ + + if (italic) + printf(""); + if (bold) + printf(""); + + if (i == (int)len - 1 && '\n' != p[i]) + html_putchar(p[i]); + + putchar('\n'); + } + + puts("\n" + "
You specified an invalid manual file.
"); return; } - resp_begin_http(200, NULL); + mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL, + req->q.manpath); + rc = mparse_readfd(mp, fd, file); + close(fd); - do { - ssz = read(fd, buf, BUFSIZ); - if (ssz > 0) - write(STDOUT_FILENO, buf, ssz); - } while (ssz > 0); + if (rc >= MANDOCLEVEL_FATAL) { + fprintf(stderr, "fatal mandoc error: %s/%s\n", + req->q.manpath, file); + pg_error_internal(); + return; + } - close(fd); + usepath = strcmp(req->q.manpath, req->p[0]); + mandoc_asprintf(&opts, + "fragment,man=%s?query=%%N&sec=%%S%s%s%s%s", + scriptname, + req->q.arch ? "&arch=" : "", + req->q.arch ? req->q.arch : "", + usepath ? "&manpath=" : "", + usepath ? req->q.manpath : ""); + + mparse_result(mp, &mdoc, &man, NULL); + if (NULL == man && NULL == mdoc) { + fprintf(stderr, "fatal mandoc error: %s/%s\n", + req->q.manpath, file); + pg_error_internal(); + mparse_free(mp); + return; + } + + vp = html_alloc(opts); + + if (NULL != mdoc) + html_mdoc(vp, mdoc); + else + html_man(vp, man); + + html_free(vp); + mparse_free(mp); + free(opts); } static void -pg_show(const struct manpaths *ps, const struct req *req, char *path) +resp_show(const struct req *req, const char *file) { - char *sub; - char file[MAXPATHLEN]; - int rc; - unsigned int vol, rec; - DB *db; - DBT key, val; - if (NULL == path) { - resp_badmanual(); + if ('.' == file[0] && '/' == file[1]) + file += 2; + + if ('c' == *file) + catman(req, file); + else + format(req, file); +} + +static void +pg_show(struct req *req, const char *fullpath) +{ + char *manpath; + const char *file; + + if ((file = strchr(fullpath, '/')) == NULL) { + pg_error_badrequest( + "You did not specify a page to show."); return; - } else if (NULL == (sub = strrchr(path, '/'))) { - resp_badmanual(); - return; - } else - *sub++ = '\0'; + } + manpath = mandoc_strndup(fullpath, file - fullpath); + file++; - if ( ! (atou(path, &vol) && atou(sub, &rec))) { - resp_badmanual(); + if ( ! validate_manpath(req, manpath)) { + pg_error_badrequest( + "You specified an invalid manpath."); + free(manpath); return; - } else if (vol >= (unsigned int)ps->sz) { - resp_badmanual(); - return; } - strlcpy(file, ps->paths[vol], MAXPATHLEN); - strlcat(file, "/mandoc.index", MAXPATHLEN); + /* + * Begin by chdir()ing into the manpath. + * This way we can pick up the database files, which are + * relative to the manpath root. + */ - /* Open the index recno(3) database. */ - - db = dbopen(file, O_RDONLY, 0, DB_RECNO, NULL); - if (NULL == db) { - resp_baddb(); + if (chdir(manpath) == -1) { + fprintf(stderr, "chdir %s: %s\n", + manpath, strerror(errno)); + pg_error_internal(); + free(manpath); return; } - key.data = &rec; - key.size = 4; + if (strcmp(manpath, "mandoc")) { + free(req->q.manpath); + req->q.manpath = manpath; + } else + free(manpath); - if (0 != (rc = (*db->get)(db, &key, &val, 0))) { - rc < 0 ? resp_baddb() : resp_badmanual(); - (*db->close)(db); + if ( ! validate_filename(file)) { + pg_error_badrequest( + "You specified an invalid manual file."); return; - } + } - /* Extra filename: the first nil-terminated entry. */ - - (*db->close)(db); - - strlcpy(file, ps->paths[vol], MAXPATHLEN); - strlcat(file, "/", MAXPATHLEN); - strlcat(file, (char *)val.data, MAXPATHLEN); - - if ( ! insecure) { - strlcat(file, ".html", MAXPATHLEN); - format_secure(file); - } else - format_insecure(file); + resp_begin_html(200, NULL); + resp_searchform(req); + resp_show(req, file); + resp_end_html(); } static void -pg_search(const struct manpaths *ps, const struct req *req, char *path) +pg_search(const struct req *req) { - size_t tt; - int i, sz, rc; - const char *ep, *start; - char **cp; - struct opts opt; - struct expr *expr; + struct mansearch search; + struct manpaths paths; + struct manpage *res; + char **cp; + const char *ep, *start; + size_t ressz; + int i, sz; - expr = NULL; - cp = NULL; - ep = NULL; - sz = 0; + /* + * Begin by chdir()ing into the root of the manpath. + * This way we can pick up the database files, which are + * relative to the manpath root. + */ - memset(&opt, 0, sizeof(struct opts)); + if (-1 == (chdir(req->q.manpath))) { + fprintf(stderr, "chdir %s: %s\n", + req->q.manpath, strerror(errno)); + pg_error_internal(); + return; + } - for (sz = i = 0; i < (int)req->fieldsz; i++) - if (0 == strcmp(req->fields[i].key, "expr")) - ep = req->fields[i].val; - else if (0 == strcmp(req->fields[i].key, "sec")) - opt.cat = req->fields[i].val; - else if (0 == strcmp(req->fields[i].key, "arch")) - opt.arch = req->fields[i].val; + search.arch = req->q.arch; + search.sec = req->q.sec; + search.outkey = "Nd"; + search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR; + paths.sz = 1; + paths.paths = mandoc_malloc(sizeof(char *)); + paths.paths[0] = mandoc_strdup("."); + /* - * Poor man's tokenisation. - * Just break apart by spaces. + * Poor man's tokenisation: just break apart by spaces. * Yes, this is half-ass. But it works for now. */ + ep = req->q.query; while (ep && isspace((unsigned char)*ep)) ep++; + sz = 0; + cp = NULL; while (ep && '\0' != *ep) { - cp = mandoc_realloc(cp, (sz + 1) * sizeof(char *)); + cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *)); start = ep; while ('\0' != *ep && ! isspace((unsigned char)*ep)) ep++; @@ -594,124 +1003,148 @@ pg_search(const struct manpaths *ps, const struct req ep++; } - rc = -1; + if (0 == mansearch(&search, &paths, sz, cp, &res, &ressz)) + pg_noresult(req, "You entered an invalid query."); + else if (0 == ressz) + pg_noresult(req, "No results found."); + else + pg_searchres(req, res, ressz); - /* - * Pump down into apropos backend. - * The resp_search() function is called with the results. - */ - - if (NULL != (expr = exprcomp(sz, cp, &tt))) - rc = apropos_search - (ps->sz, ps->paths, &opt, - expr, tt, (void *)req, resp_search); - - /* ...unless errors occured. */ - - if (0 == rc) - resp_baddb(); - else if (-1 == rc) - resp_badexpr(req); - for (i = 0; i < sz; i++) free(cp[i]); - free(cp); - exprfree(expr); + + for (i = 0; i < (int)ressz; i++) { + free(res[i].file); + free(res[i].names); + free(res[i].output); + } + free(res); + + free(paths.paths[0]); + free(paths.paths); } int main(void) { - int i; struct req req; - char *p, *path, *subpath; - struct manpaths paths; + const char *path; + const char *querystring; + int i; - /* HTTP init: read and parse the query string. */ + /* Scan our run-time environment. */ - progname = getenv("SCRIPT_NAME"); - if (NULL == progname) - progname = ""; + if (NULL == (scriptname = getenv("SCRIPT_NAME"))) + scriptname = ""; - cache = getenv("CACHE_DIR"); - if (NULL == cache) - cache = "/cache/man.cgi"; - - if (NULL == getenv("INSECURE")) { - insecure = 0; - if (-1 == chdir(cache)) { - resp_bad(); - return(EXIT_FAILURE); - } + if ( ! validate_urifrag(scriptname)) { + fprintf(stderr, "unsafe SCRIPT_NAME \"%s\"\n", + scriptname); + pg_error_internal(); + return(EXIT_FAILURE); } - host = getenv("HTTP_HOST"); - if (NULL == host) - host = "localhost"; + /* + * First we change directory into the MAN_DIR so that + * subsequent scanning for manpath directories is rooted + * relative to the same position. + */ + if (-1 == chdir(MAN_DIR)) { + fprintf(stderr, "MAN_DIR: %s: %s\n", + MAN_DIR, strerror(errno)); + pg_error_internal(); + return(EXIT_FAILURE); + } + memset(&req, 0, sizeof(struct req)); + pathgen(&req); - if (NULL != (p = getenv("QUERY_STRING"))) - kval_parse(&req.fields, &req.fieldsz, p); + /* Next parse out the query string. */ - /* Resolve leading subpath component. */ + if (NULL != (querystring = getenv("QUERY_STRING"))) + http_parse(&req, querystring); - subpath = path = NULL; - req.page = PAGE__MAX; + if ( ! (NULL == req.q.manpath || + validate_manpath(&req, req.q.manpath))) { + pg_error_badrequest( + "You specified an invalid manpath."); + return(EXIT_FAILURE); + } - if (NULL == (path = getenv("PATH_INFO")) || '\0' == *path) - req.page = PAGE_INDEX; + if ( ! (NULL == req.q.arch || validate_urifrag(req.q.arch))) { + pg_error_badrequest( + "You specified an invalid architecture."); + return(EXIT_FAILURE); + } - if (NULL != path && '/' == *path && '\0' == *++path) - req.page = PAGE_INDEX; + /* Dispatch to the three different pages. */ - /* Strip file suffix. */ + path = getenv("PATH_INFO"); + if (NULL == path) + path = ""; + else if ('/' == *path) + path++; - if (NULL != path && NULL != (p = strrchr(path, '.'))) - if (NULL != p && NULL == strchr(p, '/')) - *p++ = '\0'; + if ('\0' != *path) + pg_show(&req, path); + else if (NULL != req.q.query) + pg_search(&req); + else + pg_index(&req); - /* Resolve subpath component. */ + free(req.q.manpath); + free(req.q.arch); + free(req.q.sec); + free(req.q.query); + for (i = 0; i < (int)req.psz; i++) + free(req.p[i]); + free(req.p); + return(EXIT_SUCCESS); +} - if (NULL != path && NULL != (subpath = strchr(path, '/'))) - *subpath++ = '\0'; +/* + * Scan for indexable paths. + */ +static void +pathgen(struct req *req) +{ + FILE *fp; + char *dp; + size_t dpsz; - /* Map path into one we recognise. */ + if (NULL == (fp = fopen("manpath.conf", "r"))) { + fprintf(stderr, "%s/manpath.conf: %s\n", + MAN_DIR, strerror(errno)); + pg_error_internal(); + exit(EXIT_FAILURE); + } - if (NULL != path && '\0' != *path) - for (i = 0; i < (int)PAGE__MAX; i++) - if (0 == strcmp(pages[i], path)) { - req.page = (enum page)i; - break; - } - - /* Initialise MANPATH. */ - - memset(&paths, 0, sizeof(struct manpaths)); - if ( ! insecure) - manpath_manconf("etc/man.conf", &paths); - else - manpath_parse(&paths, NULL, NULL); - - /* Route pages. */ - - switch (req.page) { - case (PAGE_INDEX): - pg_index(&paths, &req, subpath); - break; - case (PAGE_SEARCH): - pg_search(&paths, &req, subpath); - break; - case (PAGE_SHOW): - pg_show(&paths, &req, subpath); - break; - default: - break; + while (NULL != (dp = fgetln(fp, &dpsz))) { + if ('\n' == dp[dpsz - 1]) + dpsz--; + req->p = mandoc_realloc(req->p, + (req->psz + 1) * sizeof(char *)); + dp = mandoc_strndup(dp, dpsz); + if ( ! validate_urifrag(dp)) { + fprintf(stderr, "%s/manpath.conf contains " + "unsafe path \"%s\"\n", MAN_DIR, dp); + pg_error_internal(); + exit(EXIT_FAILURE); + } + if (NULL != strchr(dp, '/')) { + fprintf(stderr, "%s/manpath.conf contains " + "path with slash \"%s\"\n", MAN_DIR, dp); + pg_error_internal(); + exit(EXIT_FAILURE); + } + req->p[req->psz++] = dp; } - manpath_free(&paths); - kval_free(req.fields, req.fieldsz); - - return(EXIT_SUCCESS); + if ( req->p == NULL ) { + fprintf(stderr, "%s/manpath.conf is empty\n", MAN_DIR); + pg_error_internal(); + exit(EXIT_FAILURE); + } }