=================================================================== RCS file: /cvs/mandoc/cgi.c,v retrieving revision 1.82 retrieving revision 1.98 diff -u -p -r1.82 -r1.98 --- mandoc/cgi.c 2014/07/24 20:30:45 1.82 +++ mandoc/cgi.c 2014/09/27 11:17:36 1.98 @@ -1,4 +1,4 @@ -/* $Id: cgi.c,v 1.82 2014/07/24 20:30:45 schwarze Exp $ */ +/* $Id: cgi.c,v 1.98 2014/09/27 11:17:36 kristaps Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2014 Ingo Schwarze @@ -15,14 +15,16 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include +#include + #include #include #include #include +#include #include #include #include @@ -39,10 +41,10 @@ * A query as passed to the search function. */ struct query { - const char *manpath; /* desired manual directory */ - const char *arch; /* architecture */ - const char *sec; /* manual section */ - const char *expr; /* unparsed expression string */ + char *manpath; /* desired manual directory */ + char *arch; /* architecture */ + char *sec; /* manual section */ + char *query; /* unparsed query expression */ int equal; /* match whole names, not substrings */ }; @@ -55,13 +57,12 @@ struct req { static void catman(const struct req *, const char *); static void format(const struct req *, const char *); static void html_print(const char *); -static void html_printquery(const struct req *); static void html_putchar(char); static int http_decode(char *); -static void http_parse(struct req *, char *); +static void http_parse(struct req *, const char *); static void http_print(const char *); static void http_putchar(char); -static void http_printquery(const struct req *); +static void http_printquery(const struct req *, const char *); static void pathgen(struct req *); static void pg_error_badrequest(const char *); static void pg_error_internal(void); @@ -76,6 +77,10 @@ static void resp_begin_http(int, const char *); static void resp_end_html(void); static void resp_searchform(const struct req *); static void resp_show(const struct req *, const char *); +static void set_query_attr(char **, char **); +static int validate_filename(const char *); +static int validate_manpath(const struct req *, const char *); +static int validate_urifrag(const char *); static const char *scriptname; /* CGI script name */ @@ -87,14 +92,14 @@ static const char *const sec_names[] = { "All Sections", "1 - General Commands", "2 - System Calls", - "3 - Subroutines", - "3p - Perl Subroutines", - "4 - Special Files", + "3 - Library Functions", + "3p - Perl Library", + "4 - Device Drivers", "5 - File Formats", "6 - Games", - "7 - Macros and Conventions", - "8 - Maintenance Commands", - "9 - Kernel Interface" + "7 - Miscellaneous Information", + "8 - System Manager\'s Manual", + "9 - Kernel Developer\'s Manual" }; static const int sec_MAX = sizeof(sec_names) / sizeof(char *); @@ -141,54 +146,31 @@ html_putchar(char c) } static void -http_printquery(const struct req *req) +http_printquery(const struct req *req, const char *sep) { - if (NULL != req->q.manpath) { - printf("&manpath="); - http_print(req->q.manpath); + if (NULL != req->q.query) { + printf("query="); + http_print(req->q.query); } + if (0 == req->q.equal) + printf("%sapropos=1", sep); if (NULL != req->q.sec) { - printf("&sec="); + printf("%ssec=", sep); http_print(req->q.sec); } if (NULL != req->q.arch) { - printf("&arch="); + printf("%sarch=", sep); http_print(req->q.arch); } - if (NULL != req->q.expr) { - printf("&query="); - http_print(req->q.expr); + if (NULL != req->q.manpath && + strcmp(req->q.manpath, req->p[0])) { + printf("%smanpath=", sep); + http_print(req->q.manpath); } - if (0 == req->q.equal) - printf("&apropos=1"); } static void -html_printquery(const struct req *req) -{ - - if (NULL != req->q.manpath) { - printf("&manpath="); - html_print(req->q.manpath); - } - if (NULL != req->q.sec) { - printf("&sec="); - html_print(req->q.sec); - } - if (NULL != req->q.arch) { - printf("&arch="); - html_print(req->q.arch); - } - if (NULL != req->q.expr) { - printf("&query="); - html_print(req->q.expr); - } - if (0 == req->q.equal) - printf("&apropos=1"); -} - -static void http_print(const char *p) { @@ -213,65 +195,114 @@ html_print(const char *p) } /* - * Parse out key-value pairs from an HTTP request variable. - * This can be either a cookie or a POST/GET string, although man.cgi - * uses only GET for simplicity. + * Transfer the responsibility for the allocated string *val + * to the query structure. */ static void -http_parse(struct req *req, char *p) +set_query_attr(char **attr, char **val) { - char *key, *val; - memset(&req->q, 0, sizeof(struct query)); - req->q.manpath = req->p[0]; - req->q.equal = 1; + free(*attr); + if (**val == '\0') { + *attr = NULL; + free(*val); + } else + *attr = *val; + *val = NULL; +} - while ('\0' != *p) { - key = p; - val = NULL; +/* + * Parse the QUERY_STRING for key-value pairs + * and store the values into the query structure. + */ +static void +http_parse(struct req *req, const char *qs) +{ + char *key, *val; + size_t keysz, valsz; - p += (int)strcspn(p, ";&"); - if ('\0' != *p) - *p++ = '\0'; - if (NULL != (val = strchr(key, '='))) - *val++ = '\0'; + req->q.manpath = NULL; + req->q.arch = NULL; + req->q.sec = NULL; + req->q.query = NULL; + req->q.equal = 1; - if ('\0' == *key || NULL == val || '\0' == *val) - continue; + key = val = NULL; + while (*qs != '\0') { - /* Just abort handling. */ + /* Parse one key. */ - if ( ! http_decode(key)) - break; - if (NULL != val && ! http_decode(val)) - break; + keysz = strcspn(qs, "=;&"); + key = mandoc_strndup(qs, keysz); + qs += keysz; + if (*qs != '=') + goto next; - if (0 == strcmp(key, "query")) - req->q.expr = val; - else if (0 == strcmp(key, "manpath")) { + /* Parse one value. */ + + valsz = strcspn(++qs, ";&"); + val = mandoc_strndup(qs, valsz); + qs += valsz; + + /* Decode and catch encoding errors. */ + + if ( ! (http_decode(key) && http_decode(val))) + goto next; + + /* Handle key-value pairs. */ + + if ( ! strcmp(key, "query")) + set_query_attr(&req->q.query, &val); + + else if ( ! strcmp(key, "apropos")) + req->q.equal = !strcmp(val, "0"); + + else if ( ! strcmp(key, "manpath")) { #ifdef COMPAT_OLDURI - if (0 == strncmp(val, "OpenBSD ", 8)) { + if ( ! strncmp(val, "OpenBSD ", 8)) { val[7] = '-'; if ('C' == val[8]) val[8] = 'c'; } #endif - req->q.manpath = val; - } else if (0 == strcmp(key, "apropos")) - req->q.equal = !strcmp(val, "0"); - else if (0 == strcmp(key, "sec")) { - if (strcmp(val, "0")) - req->q.sec = val; + set_query_attr(&req->q.manpath, &val); + } + + else if ( ! (strcmp(key, "sec") #ifdef COMPAT_OLDURI - } else if (0 == strcmp(key, "sektion")) { - if (strcmp(val, "0")) - req->q.sec = val; + && strcmp(key, "sektion") #endif - } else if (0 == strcmp(key, "arch")) { - if (strcmp(val, "default")) - req->q.arch = val; + )) { + if ( ! strcmp(val, "0")) + *val = '\0'; + set_query_attr(&req->q.sec, &val); } + + else if ( ! strcmp(key, "arch")) { + if ( ! strcmp(val, "default")) + *val = '\0'; + set_query_attr(&req->q.arch, &val); + } + + /* + * The key must be freed in any case. + * The val may have been handed over to the query + * structure, in which case it is now NULL. + */ +next: + free(key); + key = NULL; + free(val); + val = NULL; + + if (*qs != '\0') + qs++; } + + /* Fall back to the default manpath. */ + + if (req->q.manpath == NULL) + req->q.manpath = mandoc_strdup(req->p[0]); } static void @@ -345,13 +376,10 @@ resp_begin_html(int code, const char *msg) resp_begin_http(code, msg); - printf("\n" + printf("\n" "\n" "\n" - "\n" + "\n" "\n" "\n" "q.expr) - html_print(req->q.expr); + if (NULL != req->q.query) + html_print(req->q.query); puts("\" SIZE=\"40\">"); /* Write submission and reset buttons. */ @@ -402,19 +430,19 @@ resp_searchform(const struct req *req) printf( "\n" "q.equal) - printf("CHECKED "); + printf("CHECKED=\"checked\" "); printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n" "\n"); /* Write section selector. */ - printf( "\n" + puts( "\n" ""); @@ -424,13 +452,13 @@ resp_searchform(const struct req *req) printf( ""); @@ -443,7 +471,7 @@ resp_searchform(const struct req *req) printf("