#ifndef _H_CGI #define _H_CGI /**************************************** * * Common Gateway Interface Library * * Copyright 1995, 1998 Adam D. Kessler * http://www.infogarden.com/c/cgi/cgi.h * *****************************************/ #include #include #include /************************************************************/ #define LF 10 #define CR 13 #define TRUE 1 #define FALSE 0 /************************************************************/ typedef struct { char *name; char *value; } nv_pair; /************************************************************/ /* Parse CGI Input into Name Value Pairs (NVPs) */ int readParse(void); /************************************************************/ /* write out hidden input fields for use in passing through */ /* nvps in a cgi-bin. */ int cgi_passthrough(char **); /************************************************************/ /* Parse CGI Input Mid-Level Functions called by readParse(). */ int readParse_get(void); int readParse_post(void); int readParse_manual(char *); /* experimental */ /************************************************************/ /* Write to cgi.log for debugging */ void cgi_log(char *); void cgi_log_int(int); /************************************************************/ /* Low Level Word Manipulation */ char *makeword(char *line, char stop); char *fmakeword(FILE *f, char stop, int *len); char x2c(char *what); void unescape_url(char *url); void plustospace(char *str); char *safe_getenv(char *); /************************************************************/ /* Associative Arrays are stored in arrays of Name Value Pairs. Although they can be directly accessed by their numeric index, nvp_array[0..n], this access method is used mostly by Name Value Pair Lookup Functions, like nvp below. */ #define MAX_NVPAIRS 10000 nv_pair nvp_array[MAX_NVPAIRS]; int num_of_nvps=0; /************************************************************/ /* This is the NVP Lookup Function called nvp. This is implemented as a Linear Search Function. Its prototype is */ char *nvp(char *); /************************************************************/ /************************************************************/ /* END OF FUNCTION PROTOTYPING AND GLOBAL DECLARATIONS. */ /************************************************************/ /************************************************************/ /* START OF CGI API */ /************************************************************/ int cgi_passthrough(char *strarray[]) { int i=0; char buffer[256]; while (strlen(strarray[i])>0) { sprintf(buffer, "passthrough field %d: \n", i,strarray[i],nvp(strarray[i])); cgi_log(buffer); sprintf(buffer,"\n", strarray[i],nvp(strarray[i])); printf("%s",buffer); i++; } } /************************************************************/ /* This routine takes in a pointer to a string like "xxx,yyy,zzz" and a one char stop character like ',' and returns a new string with the value "xxx". As a side-effect it copies the unprocessed portion of the incoming string back to line[0], presumably in case the caller wants to keep calling till he's done. Greedy, fellow. */ char *makeword(char *line, char stop) { int x=0,y=0; /* A spot of memory is allocated */ char *word = (char *) malloc(sizeof(char) * (strlen(line) + 1)); if ((line==NULL)||(stop=='\0')) return(""); if (word==NULL) return(""); /* Copy one character at a time up to the stop-bit */ for(x=0;((line[x]) && (line[x] != stop));x++) word[x] = line[x]; /* Stick a '\0' to make it a string */ word[x] = '\0'; /* but at this location back in line there could have been a stop char (eg: comma,dot,ampersand). In fact, the only way that line[x] cannot be the stop bit is if it was in fact already '\0', meaning that it was a string (although we cannot safely assume that it was a legal string.) Either legal or illegal, we've created enough of our own string memory to hold it, so we can simply copy it blindly. We need to know this because we are about to copy all of the line we were passed, but didn't process, back to the beginning of the line!! */ if (line[x]) ++x; /* at this point line[x] refers to either a '\0' or the beginning of the unprocessed string (we were stopped cold in our tracks by stop.) */ /* create an index for the beginning of the line we got */ /* because we'll send the unprocessed string back to there. */ y=0; /* copy one character at a time from the unprocessed portion of the input string, or a '\0', back to its own beginning. Notice that if it stops immediately because of a '\0' that the input line becomes an empty string. */ while (line[y++]=line[x++]); /* give them what they wanted. */ return(word); } /************************************************************/ /* This is a function similar to makeword, in that it takes a line and a stop char for processing (ie, lopping off the first word up to, but not including, the stop char) but it does not return a pointer to a newly allocated address space. Instead it requires that you pass in a pointer to an address space large enough to hold the processed word chunk. NB: The unprocessed portion of the inputted line is copied back to the beginning of the line, so that the processed word, and delimeter, are over written. This allows the function to be called repetitively. */ void getword(char *word, char *line, char stop) { int x = 0,y; for (x=0;((line[x]) && (line[x] != stop));x++) word[x] = line[x]; word[x] = '\0'; if(line[x]) ++x; y=0; while(line[y++] = line[x++]); } /************************************************************/ /* fmakeword() - read a word from a file stream Arguments: fp file pointer to a stream of data stop stop bit (delimiter) cl pointer to maximum number of bytes to read Returns: A pointer to a newly allocated counted string that contains the first word of data from the file stream up to but not including the stop-bit actually encountered. Description: This is a routine to pull the next word in from a file stream. Send it a file-pointer, a stop char, and a maximum number of bytes to read, and it will return a counted string of data. The reading stops after n characters are read in, a stop char is encountered, or an end of file condition occurs. */ char *fmakeword(FILE *f, char stop, int *n) { int wsize; char *word; int ll; wsize = 102400; ll=0; if (f==NULL) return(""); if (stop==0) return(""); if ((*n)==0) return(""); /* allocate address space for a word we are to return */ word = (char *) malloc(sizeof(char) * (wsize + 1)); /* check your work */ if (word==NULL) {return("");} while(TRUE) { /* Take one down and pass it around, Take another byte from the input stream. */ word[ll] = (char)fgetc(f); /* Here is a check to see if we hit the penultimate byte in the word we malloced. If so, then we take the opportunity to place a '\0' in the ultimate byte, and then reallocate for a larger chunk of address space. */ if(ll==wsize) { word[ll+1] = '\0'; wsize+=102400; word = (char *)realloc(word,sizeof(char)*(wsize+1)); if (word==NULL) {return("");} } /* We are also tasked with decrementing our own limit, which when zero is a stop condition for us. We were given a pointer to an integer in our parent routine's integer, so we can modify it. */ --(*n); /* stop conditions */ if((word[ll] == stop) || (feof(f)) || (!(*n))) { /* There are three stop conditions for this loop: 1) word[ll] is the stop bit (eg comma or amp) 2) end of filestream 3) the counter, which we have a pointer to and are supposed to decrement with each byte, has reached zero. */ /* we need to add a '\0' to end the string, but we don't want the stop bit dangling at the end of the string we pass back, so if we hit the stop bit we'll leave the pointer at it so that it gets overwritten by '\0', otherwise we'll move past the last place written and stick in the '\0' there. */ if(word[ll] != stop) ll++; word[ll] = '\0'; /* give them what they want */ return(word); } /* no stop condition was met, so we increment our local counter and go get another byte from the stream. */ ++ll; } } /************************************************************/ char x2c(char *what) { register char digit; digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0')); digit *= 16; digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0')); return(digit); } /************************************************************/ void unescape_url(char *url) { register int x,y; for(x=0,y=0;url[y];++x,++y) { if((url[x] = url[y]) == '%') { url[x] = x2c(&url[y+1]); y+=2; } } url[x] = '\0'; } /************************************************************/ /* take a string and replace all of the plus signs by blanks */ void plustospace(char *str) { register int x; for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' '; } /************************************************************/ /* What is the index into a string which indicates the first occurance of a given stop character? */ int ind(char *s, char stop) { register int x; for(x=0;s[x];x++) if(s[x] == stop) return(x); return(-1); } /************************************************************/ /* What is the index into a string which indicates the last occurance of a given stop character? */ int rind(char *string, char stop) { register int x; for(x=strlen(string) - 1; x != -1; x--) if(string[x] == stop) return(x); /* returns -1 if not found */ return(-1); } /************************************************************/ int getline(char *s, int n, FILE *f) { register int i=0; while(TRUE) { s[i] = (char)fgetc(f); if(s[i] == CR) s[i] = fgetc(f); if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) { s[i] = '\0'; return (feof(f) ? 1 : 0); } ++i; } } /************************************************************/ void send_fd(FILE *f, FILE *fd) { int num_chars=0; char c; while (TRUE) { c = fgetc(f); if(feof(f)) return; fputc(c,fd); } } /************************************************************/ void escape_shell_cmd(char *cmd) { register int x,y,l; l=strlen(cmd); for(x=0;cmd[x];x++) { if(ind("&;`'\"|*?~<>^()[]{}$\\",cmd[x]) != -1){ for(y=l+1;y>x;y--) cmd[y] = cmd[y-1]; l++; /* length has been increased */ cmd[x] = '\\'; x++; /* skip the character */ } } } /************************************************************/ int readParse(void) { char *env; num_of_nvps=-1; env=safe_getenv("REQUEST_METHOD"); if (!strcmp(env,"GET")) { return(readParse_get()); } else if (!strcmp(env,"POST")) { return(readParse_post()); } } /************************************************************/ int readParse_get(void) { int m=-1,x=-1; char *url_data; url_data = safe_getenv("QUERY_STRING"); for(x=0;url_data[0] != '\0';x++) { m=x; nvp_array[x].value = (char *) malloc(sizeof(char) * (strlen(url_data) + 1)); memset(nvp_array[x].value,'\0',sizeof(nvp_array[x].value)); if (nvp_array[x].value==NULL) { cgi_log("readParse_get: could not allocate memory for value.\n"); return(FALSE); } getword(nvp_array[x].value,url_data,'&'); plustospace(nvp_array[x].value); unescape_url(nvp_array[x].value); nvp_array[x].name = (char *) malloc(sizeof(char) * (strlen(nvp_array[x].value) + 1)); memset(nvp_array[x].name,'\0',sizeof(nvp_array[x].name)); if (nvp_array[x].name==NULL) { cgi_log("readParse_get: could not allocate memory for name.\n"); return(FALSE); } getword(nvp_array[x].name,nvp_array[x].value,'='); } num_of_nvps=m+1; return(TRUE); } /************************************************************/ int readParse_post(void) { int m=-1,x=-1; int cl; char *env; env=safe_getenv("CONTENT_LENGTH"); cl = atoi(env); for (x=0; cl && (!feof(stdin)); x++) { m=x; nvp_array[x].value = fmakeword(stdin,'&',&cl); plustospace(nvp_array[x].value); unescape_url(nvp_array[x].value); nvp_array[x].name = makeword(nvp_array[x].value,'='); } num_of_nvps=m+1; } /************************************************************/ int readParse_manual(char *url_data) { int m=-1,x=-1; if (url_data == NULL) { cgi_log("readParse_manual: had NULL URL input\n"); return(FALSE); } for(x=0; url_data[0] != '\0'; x++) { m=x; nvp_array[x].value = (char *) malloc(sizeof(char) * (strlen(url_data) + 1)); if (nvp_array[x].value==NULL) { cgi_log("readParse_manual: could not allocate memory for value.\n"); return(FALSE); } getword(nvp_array[x].value,url_data,'&'); plustospace(nvp_array[x].value); unescape_url(nvp_array[x].value); nvp_array[x].name = (char *) malloc(sizeof(char) * (strlen(nvp_array[x].value) + 1)); if (nvp_array[x].name==NULL) { cgi_log("readParse_manual: could not allocate memory for name.\n"); return(FALSE); } /* At this point we have a string like "xxx=yyy" and after this call we have name="xxx" and value="yyy" */ getword(nvp_array[x].name,nvp_array[x].value,'='); } num_of_nvps=m+1; return(TRUE); } /************************************************************/ char *nvp(char *name) { int i; if (name==NULL) return(""); if (num_of_nvps<0) return(""); for (i=0;i