/* -----------------------*-----------------------*----------------------- * * cgi2env.c * * Process the Query_String (GET) or the standard input (POST)into strings * of the form: * xxx = yyy * where xxx is the environment variable to be set * yyy is the value of the variable. * Once the form is set, the another.cgi program can access the * variables directly rather than process the QUERY_STRING itself. * * To avoid clobbering existing environmental variable, WWW_ is * appended to xxx. Therefore instead of setting xxx to yyy, * WWW_xxx is set to yyy. * * ------------------------*----------------------*------------------------ */ #include #include #include #include #include void *malloc(), *realloc(); char *getenv(); extern char *sys_errlist[]; extern int errno; /*void cgi2env(); void http_head(); */ #define PREFIX "WWW_" #define ishex(x) (((x) >= '0' && (x) <= '9') || ((x) >= 'a' && (x) <= 'f') || \ ((x) >= 'A' && (x) <= 'F')) /* -------*-----------*-------------* * Convert two hex digits to a value. * ---------*------------*---------- */ static int htoi(s) unsigned char *s; { int value; char c; c = s[0]; if (isupper(c)) c = tolower(c); value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16; c = s[1]; if (isupper(c)) c = tolower(c); value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10; return (value); } /* end htoi */ /* -------*-----------*-------------* * Get rid of all the URL escaping in a string. Modify it in place, since * the result will always be equal in length or smaller. * ---------*------------*---------- */ static void url_unescape(str) unsigned char *str; { unsigned char *dest = str; while (str[0]) { if (str[0] == '+') dest[0] = ' '; else if (str[0] == '%' && ishex(str[1]) && ishex(str[2])) { dest[0] = (unsigned char) htoi(str + 1); str += 2; } else dest[0] = str[0]; str++; dest++; } dest[0] = '\0'; } /* end url unescape */ /* -------*-----------*-------------* * Print the HTTP header * ---------*------------*---------- */ void http_head() { puts("Content-type: text/html\n\n"); } /* end http head */ /* -------*-----------*-------------* * Print an HTML error string with the right HTTP header and exit. * ---------*------------*---------- */ static void html_perror(str) char *str; { http_head(); puts("

The following error was encountered while processing"); puts("your query."); puts("

"); printf("%s: %s\n", str, sys_errlist[errno]); puts("
"); puts("

Please try again later."); puts(""); exit(1); } /* end html perror */ /* -------*-----------*-------------* * Stuff a URL-unescaped variable, with the prefix on its name, into the * environment. Uses the "=" from the CGI arguments. Putting an "=" in * a field name is probably a bad idea. * * If the variable is already defined, append a '#' to it along with the * new value. * * If the variable name begins with an underline, strip whitespace from the * start and end and normalize end-of-line characters. * ---------*------------*---------- */ static void stuffenv(var) char *var; { char *buf, *c, *s, *t, *oldval, *newval; int despace = 0, got_cr = 0; url_unescape(var); /* Allocate enough memory for the variable name and its value. */ buf = malloc(strlen(var) + sizeof(PREFIX) + 1); if (buf == NULL) html_perror("stuffenv"); strcpy(buf, PREFIX); if (var[0] == '_') { strcpy(buf + sizeof(PREFIX) - 1, var + 1); despace = 1; } else strcpy(buf + sizeof(PREFIX) - 1, var); /* * If, for some reason, there wasn't an = in the query string, * add one so the environment will be valid. * * Also, change periods to spaces so folks can get at "image" * input fields from the shell, which has trouble with periods * in variable names. */ for (c = buf; *c != '\0'; c++) { if (*c == '.') *c = '_'; if (*c == '=') break; } if (*c == '\0') c[1] = '\0'; /* * Do whitespace stripping, if applicable. Since this can only ever * shorten the value, it's safe to do in place. */ if (despace && c[1]) { for (s = c + 1; *s && isspace(*s); s++) ; t = c + 1; while (*s) { if (*s == '\r') { got_cr = 1; s++; continue; } if (got_cr) { if (*s != '\n') *t++ = '\n'; got_cr = 0; } *t++ = *s++; } /* Strip trailing whitespace if we copied anything. */ while (t > c && isspace(*--t)) ; t[1] = '\0'; } /* * Check for the presence of the variable. */ *c = '\0'; if (oldval = getenv(buf)) { newval = malloc(strlen(oldval) + strlen(buf) + strlen(c+1) + 2); if (newval == NULL) html_perror("stuffenv: append"); *c = '='; strcpy(newval, buf); strcat(newval, "#"); strcat(newval, oldval); free(buf); } else { *c = '='; newval = buf; } putenv(newval); } /* end stuffenv */ /* -------*-----------*-------------* * Scan a query string, stuffing variables into the environment. This * should ideally just use strtok(), but that's not available everywhere. * ---------*------------*---------- */ static void scanquery(q) char *q; { char *next = q; do { while (*next && *next != '&') next++; if (! *next) next = NULL; else *next = '\0'; stuffenv(q); if (next) *next++ = '&'; q = next; } while (q != NULL); } /* end scanquery */ /* -------*-----------*-------------* * Read a POST query from standard input into a dynamic buffer. Terminate * it with a null character. * ---------*------------*---------- */ static char * postread() { char *buf = NULL; int size = 0, sofar = 0, got; buf = getenv("CONTENT_TYPE"); if (buf == NULL || strcmp(buf, "application/x-www-form-urlencoded")) { http_head(); puts("

No content type was passed to cgi2env."); exit(1); } buf = getenv("CONTENT_LENGTH"); if (buf == NULL) { http_head(); puts("

The server did not tell cgi2env how long the request"); puts("was."); exit(1); } size = atoi(buf); buf = malloc(size + 1); if (buf == NULL) html_perror("postread"); do { got = fread(buf + sofar, 1, size - sofar, stdin); sofar += got; } while (got && sofar < size); buf[sofar] = '\0'; return (buf); } /* end post read */ /* -------*-----------*-------------* * Run a shell script. We use this instead of the OS's "#!" mechanism * because that mechanism doesn't work too well on SVR3-based systems. * ---------*------------*---------- */ void runscript(shell, script) char *shell, *script; { char *argvec[4], *space; int pos; if (shell[0] != '/') return; pos = strlen(shell) - 1; if (shell[pos] == '\n') shell[pos] = '\0'; argvec[0] = shell; /* * See if there's an argument string. strchr() isn't available * everywhere, so do it ourselves. */ for (space = shell; *space; space++) if (*space == ' ') break; if (space[0] && space[1]) { *space = '\0'; argvec[1] = space + 1; argvec[2] = script; argvec[3] = NULL; } else { argvec[1] = script; argvec[2] = NULL; } execv(shell, argvec); /* Fall back to main() on error. */ } /* end run script */ #ifndef NO_MAIN /* -------*-----------*-------------* * Figure out which part of the path information refers to a backend program. * This might be more than one path element if the backend is in a * subdirectory of the main script directory. * ---------*------------*---------- */ int find_program(scriptdir, pathinfo) char *scriptdir; char *pathinfo; { struct stat st; int sdlen = strlen(scriptdir); int proglen = 0; char path[1000]; strncpy(path, scriptdir, sizeof(path)); while (pathinfo[proglen]) { path[proglen + sdlen] = pathinfo[proglen]; proglen++; if (pathinfo[proglen] == '/' || pathinfo[proglen] == '\0') { path[proglen + sdlen] = '\0'; if (stat(path, &st) || !S_ISDIR(st.st_mode)) return (proglen); } } return (proglen); /* whole path, if it's all directories */ } #endif /* -------*-----------*-------------* * Main program, optionally callable as a library function. * ---------*------------*---------- */ void cgi2env() { char *query, *program, *pathinfo, *newpathinfo, *method; char *scriptname, *newscriptname; char *ptrans, *ptend; char *argvec[2], shellname[200]; int proglen; FILE *fp; /* * First, get the query string, wherever it is, and stick its * component parts into the environment. Allow combination * GET and POST queries, even though that's a bit strange. */ query = getenv("QUERY_STRING"); /* printf("input messsage: %s\n",query); */ if (query != NULL && strlen(query)) { char *dupquery; /* Ultrix doesn't have strdup(), so we do this the long way. */ dupquery = malloc(sizeof(char)*(strlen(query) + 1)); if (dupquery) { strcpy(dupquery, query); scanquery(dupquery); } } method = getenv("REQUEST_METHOD"); if (method != NULL && ! strcmp(method, "POST")) { query = postread(); if (query[0] != '\0') scanquery(query); } if (query == NULL) { puts("

The 'cgi2env' program couldn't find a query to"); puts("process.\n"); exit(1); } }