/* PROGRAM cgiparse.c ** PROGRAM FOR CGI/1.0 SCRIPTS TO PARSE QUERY_STRING ** ENVIRONTMENT VARIABLE PASSED FROM THE SERVER, AND ** TO READ CONTENT_LENGTH CHARACTERS FROM STDIN. ** ** USAGE: ** See usage() function, or start with no parameters. ** ** AUTHORS: ** AL Ari Luotonen luotonen@dxcern.cern.ch ** ** HISTORY: ** 9 Jan 94 AL Written on a melancholic Sunday evening. ** ** BUGS: ** */ #include #include #include #include "HTUtils.h" #include "HTParse.h" PRIVATE char * progname; PRIVATE BOOL parse_keywords = NO; PRIVATE BOOL parse_form = NO; PRIVATE char * parse_value = NULL; PRIVATE BOOL echo = NO; PRIVATE BOOL init = NO; PRIVATE char * separator = NULL; PRIVATE char * prefix = NULL; PRIVATE BOOL count = NO; PRIVATE int nth = 0; PRIVATE BOOL quiet = NO; #ifdef VMS #include "HTStyle.h" PUBLIC char *HTAppName = "CGIParse"; PUBLIC char *HTAppVersion = "Unknown"; PUBLIC HTStyleSheet *styleSheet; #endif /* VMS */ PRIVATE void fatal2 ARGS3(int, exit_status, char *, part_1, char *, part_2) { if (!quiet) fprintf(stderr, "%s: Error: %s %s\n", progname, part_1 ? part_1 : "", part_2 ? part_2 : ""); exit(exit_status); } PRIVATE void fatal ARGS2(int, exit_status, char *, err_msg) { fatal2(exit_status, err_msg, ""); } PRIVATE void usage NOARGS { fprintf(stderr, "\n\t\t\t\t\t%s Version 1.1 CERN 1994\n\n%s\n%s\n%s\n\n", progname, "Program for CGI/1.0 scripts, to parse QUERY_STRING environment variable", "(method GET) or standard input (method POST). Can also be used to just", "read CONTENT_LENGTH characters from stdin."); fprintf(stderr, "Usage:\n %s %s\n %s %s\n %s %s\n %s %s\n %s %s\n\n", progname, "-keywords", progname, "-form [-sep ] -prefix ", progname, "-value [-sep ]", progname, "-read", progname, "-init"); fprintf(stderr, "Options:\n%s\n%s\n\n", " -sep xx with -form Specify multi-value separator, default \", \"", " with -value The same, but default is newline"); fprintf(stderr, "%s\n\n", " -prefix xx only with -form Set the shell variable suffix, default is FORM_"); fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n\n", " -count with -keywords Output the number of keywords", " with -form Give the number of unique form fields", " (multiple values counted as one)", " with -value xx Give the number of multiple values of", " field xx"); fprintf(stderr, "%s\n%s\n%s\n%s\n\n", " - e.g. -2", " with -keyword Give n'th keyword", " with -form Give all values of n'th field", " with -value xx Give n'th of multi-values of field xx"); fprintf(stderr, "%s\n%s\n\n", " -quiet Suppress error messages (non-zero exit", " status still indicates error)"); fprintf(stderr, "%s\n\n", "Options have one-character equivalents: -k -f -v -r -i -s -p -c -q"); fprintf(stderr, "Exit statuses:\n %s\n %s\n %s\n %s\n %s\n\n", "0 Success", "1 Illegal arguments", "2 Environment not set correctly", "3 Failed to get the information (no such field, QUERY_STRING", " contains keywords when form field values requested, etc)"); exit(1); } PRIVATE void plusses_to_spaces ARGS1(char *, s) { if (!s) return; for(; *s; s++) if (*s == '+') *s=' '; } PRIVATE BOOL valid_varname ARGS1(char *, varname) { char *cur; if (!varname || !*varname) return NO; for(cur=varname; *cur; cur++) { if ((*cur < 'a' || *cur > 'z') && (*cur < 'A' || *cur > 'Z') && (*cur < '0' || *cur > '9') && *cur != '_') return NO; } return YES; } PRIVATE char * sh_escape ARGS1(char *, arg) { static char *escaped = NULL; static int allocated = 0; int needed; char *cur; char *dest; if (!arg) return ""; for(cur=arg,needed=3; *cur; cur++,needed++) { #ifdef VMS if (*cur == '\"') needed += 1; #else if (*cur == '\'') needed += 3; #endif } if (allocated < needed) { allocated = needed; if (escaped) free(escaped); escaped = (char*)malloc(needed); if (!escaped) outofmem(__FILE__, "escaped-malloc"); } dest = escaped; #ifdef VMS *(dest++) = '\"'; /* Open quotes */ #else *(dest++) = '\''; /* Open quotes */ #endif for(cur=arg; *cur; cur++) { #ifdef VMS if (*cur == '\"') { *(dest++) = '\"'; /* Create double quotes */ *(dest++) = '\"'; } #else if (*cur == '\'') { *(dest++) = '\''; /* First close current quotes */ *(dest++) = '\\'; /* Escape quote with backslash */ *(dest++) = '\''; /* Quote itself */ *(dest++) = '\''; /* Open quotes again */ } #endif else *(dest++) = *cur; /* Just copy the character */ } #ifdef VMS *(dest++) = '\"'; /* Close quotes */ #else *(dest++) = '\''; /* Close quotes */ #endif *dest = 0; /* Terminate string */ return escaped; } PRIVATE int already_in_array ARGS2(char **, array, char *, field) { int i; if (!array || !field) return -1; for (i=0; array[i]; i+=2) { if (!strcmp(array[i], field)) return i+1; } return -1; } PUBLIC int main ARGS2(int, argc, char **, argv) { char * query_string = getenv("QUERY_STRING"); char * request_method = getenv("REQUEST_METHOD"); char * content_length = getenv("CONTENT_LENGTH"); int content_len = 0; BOOL form_request = NO; char * cur; int i; progname = argv[0]; /* ** Parse command line options */ if (argc < 2) usage(); for (i=1; i= argc) usage(); parse_value = argv[i]; } else if (!strncmp(argv[i], "-read", 2)) echo = YES; else if (!strncmp(argv[i], "-init", 2)) init = YES; else if (!strncmp(argv[i], "-separator",2)) { if (++i >= argc) usage(); separator = argv[i]; } else if (!strncmp(argv[i], "-prefix", 2)) { if (++i >= argc) usage(); prefix = argv[i]; } else if (!strncmp(argv[i], "-count", 2)) count = YES; else if (!strncmp(argv[i], "-quiet", 2)) quiet = YES; else if (argv[i][0] == '-' && isdigit(argv[i][1])) { char *num = argv[i]+1; nth = atoi(num); if (nth <= 0) usage(); } else usage(); } /* ** Checks */ if ((parse_keywords && (parse_form || parse_value || echo || init)) || (parse_form && (parse_value || echo || init)) || (parse_value && (echo || init)) || (echo && init)) fatal2(1, "Only one of -keywords, -form, -value, -read and -init", "can be specified at a time"); if (content_length) content_len = atoi(content_length); if (!request_method) request_method = "UNDEFINED"; if (echo) if (!strcmp(request_method, "GET")) fatal(3, "Won't read from stdin with method GET"); else if (!strcmp(request_method, "HEAD")) fatal(3, "Won't read from stdin with method HEAD"); if (!content_length) { if (!query_string || echo) fatal(2, "Both QUERY_STRING and CONTENT_LENGTH not defined"); } else if (echo) { int cnt, ch; for (cnt=content_len; cnt>0 && (ch=getchar())!=EOF; cnt--) putchar(ch); exit(0); } else if (!query_string || !*query_string) { int cnt, ch; char * cur; cur = query_string = (char*)malloc(content_len + 1); if (!query_string) outofmem(__FILE__, "query_string-malloc"); for(cnt=content_len; cnt>0 && (ch=getchar())!=EOF; cnt--,cur++) *cur = (char)ch; *cur = 0; if (init) { printf("QUERY_STRING='%s'; export QUERY_STRING\n", query_string); exit(0); } } else if (init) exit(0); if (strchr(query_string, '=')) form_request = YES; if (form_request && parse_keywords) fatal2(3, (!strcmp(request_method, "POST") ? "Body" : "QUERY_STRING"), "contains fomr request -- not search keywords"); if (!form_request && !parse_keywords && *query_string) fatal2(3, (!strcmp(request_method, "POST") ? "Body" : "QUERY_STRING"), "contains search keywords -- not a form request"); if (parse_keywords) { char ** keywordvec; int cnt = 1; /* Last keyword not ended with a plus */ for (cur=query_string; *cur; cur++) { if ('+' == *cur) cnt++; } keywordvec = (char**)calloc(cnt, sizeof(char*)); if (!keywordvec) outofmem(__FILE__, "keywordvec-calloc"); for(cur=query_string, i=0; cur; i++) { char *end = strchr(cur, '+'); if (end) *(end++) = 0; HTUnEscape(cur); keywordvec[i] = cur; cur = end; } if (count) printf("%d\n", cnt); else if (nth) { if (nth <= cnt) printf("%s\n", keywordvec[nth-1]); else fatal(3, "Not so many keywords"); } else for(i=0; i 0) { char *str=(char*)malloc(strlen(formvec[pos]) + strlen(end1) + 1 + (separator? strlen(separator):2)); if (!str) outofmem(__FILE__, "multi-value-malloc"); sprintf(str, "%s%s%s", formvec[pos], (separator ? separator : ", "), end1); formvec[pos] = str; /* ** Previous formvec[pos] can't be free()d, because ** it may be part of the QUERY_STRING env.var. ** It doesn't matter anyway in this kind of small ** program that exits sooner than you can blink. */ } else { formvec[i++] = cur; formvec[i++] = end1; formvec[i] = NULL; cnt++; } } cur = end2; } if (count) printf("%d\n", cnt); else if (nth) { if (nth <= cnt) { if (valid_varname(formvec[(nth-1)*2])) printf("%s%s=%s", (prefix ? prefix : "FORM_"), formvec[(nth-1)*2], sh_escape(formvec[nth*2-1])); else fatal(3,"Invalid form field name form shell var.name"); } else fatal(3, "Not so many unique form fields"); } else { for(i=0; formvec[i]; i+=2) { if (valid_varname(formvec[i])) printf("%s%s%s=%s", (i ? "; " : ""), (prefix ? prefix : "FORM_"), formvec[i], sh_escape(formvec[i+1])); } printf("\n"); } exit(0); } else if (parse_value) { int found = 0; for(cur=query_string; cur; ) { char *end1 = strchr(cur, '='); char *end2 = (end1 ? strchr(end1, '&') : NULL); if (end1) { *(end1++) = 0; if (end2) *(end2++) = 0; HTUnEscape(cur); if (!strcmp(cur, parse_value)) { found++; if (!count && (!nth || nth==found)) { plusses_to_spaces(end1); HTUnEscape(end1); if (separator) { if (found > 1) printf("%s", separator); printf("%s", end1); } else printf("%s\n", end1); if (nth) exit(0); } } } cur = end2; } if (count) printf("%d\n", found); else if (!found) fatal(3, "No matching field name found"); else { if (separator) printf("\n"); exit(0); } } else fatal(1, "Internal error -- seems like I have noting to do??"); return 0; /* Exit program -- never reached */ }