===================================================================
RCS file: /cvs/mandoc/cgi.c,v
retrieving revision 1.8
retrieving revision 1.80
diff -u -p -r1.8 -r1.80
--- mandoc/cgi.c 2011/11/27 11:46:44 1.8
+++ mandoc/cgi.c 2014/07/22 18:14:13 1.80
@@ -1,6 +1,7 @@
-/* $Id: cgi.c,v 1.8 2011/11/27 11:46:44 kristaps Exp $ */
+/* $Id: cgi.c,v 1.80 2014/07/22 18:14:13 schwarze Exp $ */
/*
- * Copyright (c) 2011 Kristaps Dzonsons Requested manual not found. Your search didn't work. \n"
+ "This web interface is documented in the "
+ "man.cgi "
+ "manual, and the "
+ "apropos "
+ "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);
+ html_printquery(req);
+ printf("\">");
+ html_print(r[i].names);
+ printf("\n"
+ " \n"
+ "");
+ html_print(r[i].output);
+ puts(" \n"
+ "
");
+ iuse = 0;
+ priouse = 10;
+ 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 (prio >= priouse)
+ continue;
+ priouse = prio;
+ iuse = i;
}
- printf(") ");
- html_print(r[i].desc);
- puts("
You specified an invalid manual file.
"); + return; + } + + puts(""); + + 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). + */ + + if ('\b' == p[i] || '\n' == p[i]) + continue; + + /* + * 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 ('\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; + + /* Italic mode. */ + + if ('_' == p[i]) { + if (bold) + printf(""); + if ( ! italic) + printf(""); + bold = 0; + italic = 1; + i += 2; + html_putchar(p[i]); + continue; + } + + /* + * Handle funny behaviour troff-isms. + * These grok'd from the original man2html.c. + */ + + 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; } - mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL); + mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL, + req->q.manpath); rc = mparse_readfd(mp, fd, file); close(fd); if (rc >= MANDOCLEVEL_FATAL) { - resp_baddb(); + fprintf(stderr, "fatal mandoc error: %s/%s\n", + req->q.manpath, file); + pg_error_internal(); return; } - mparse_result(mp, &mdoc, &man); - vp = html_alloc(NULL); + snprintf(opts, sizeof(opts), "fragment,man=%s?" + "manpath=%s&query=%%N&sec=%%S&arch=%s", + scriptname, req->q.manpath, + req->q.arch ? req->q.arch : ""); - if (NULL != mdoc) { - resp_begin_http(200, NULL); + 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 if (NULL != man) { - resp_begin_http(200, NULL); + else html_man(vp, man); - } else - resp_baddb(); html_free(vp); mparse_free(mp); } static void -pg_show(const struct manpaths *ps, const struct req *req, char *path) +resp_show(const struct req *req, const char *file) { + + 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 *path) +{ char *sub; - char file[MAXPATHLEN]; - int rc; - unsigned int vol, rec; - DB *db; - DBT key, val; - if (NULL == path) { - resp_badmanual(); + if (NULL == path || NULL == (sub = strchr(path, '/'))) { + pg_error_badrequest( + "You did not specify a page to show."); return; - } else if (NULL == (sub = strrchr(path, '/'))) { - resp_badmanual(); - return; - } else - *sub++ = '\0'; + } + *sub++ = '\0'; - if ( ! (atou(path, &vol) && atou(sub, &rec))) { - resp_badmanual(); + if ( ! validate_manpath(req, path)) { + pg_error_badrequest( + "You specified an invalid 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 (-1 == chdir(path)) { + fprintf(stderr, "chdir %s: %s\n", + path, strerror(errno)); + pg_error_internal(); return; } - key.data = &rec; - key.size = 4; - - if (0 != (rc = (*db->get)(db, &key, &val, 0))) { - rc < 0 ? resp_baddb() : resp_badmanual(); - (*db->close)(db); + if ( ! validate_filename(sub)) { + pg_error_badrequest( + "You specified an invalid manual file."); return; - } + } - /* Extra filename: the first nil-terminated entry. */ + if (strcmp(path, "mandoc")) + req->q.manpath = path; - strlcpy(file, ps->paths[vol], MAXPATHLEN); - strlcat(file, "/", MAXPATHLEN); - strlcat(file, (char *)val.data, MAXPATHLEN); - - (*db->close)(db); - - format(file); + resp_begin_html(200, NULL); + resp_searchform(req); + resp_show(req, sub); + 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.deftype = req->q.equal ? TYPE_Nm : (TYPE_Nm | TYPE_Nd); + search.flags = req->q.equal ? MANSEARCH_MAN : 0; + 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.expr; 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++; @@ -578,118 +939,151 @@ pg_search(const struct manpaths *ps, const struct req ep++; } - rc = -1; + if (0 == mansearch(&search, &paths, sz, cp, "Nd", &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; + 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 (-1 == chdir(cache)) { - resp_bad(); + 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 ( ! 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.expr) + pg_search(&req); + else + pg_index(&req); - /* Resolve subpath component. */ + 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'; +static int +cmp(const void *p1, const void *p2) +{ - /* Map path into one we recognise. */ + return(strcasecmp(((const struct manpage *)p1)->names, + ((const struct manpage *)p2)->names)); +} - 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; - } +/* + * Scan for indexable paths. + */ +static void +pathgen(struct req *req) +{ + FILE *fp; + char *dp; + size_t dpsz; - /* Initialise MANPATH. */ + if (NULL == (fp = fopen("manpath.conf", "r"))) { + fprintf(stderr, "%s/manpath.conf: %s\n", + MAN_DIR, strerror(errno)); + pg_error_internal(); + exit(EXIT_FAILURE); + } - memset(&paths, 0, sizeof(struct manpaths)); - manpath_manconf("etc/man.conf", &paths); - - /* 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); + } }