/*-------------------------------------------------------------*/ /* File: cgi-lib.c * * CGI query processing routines in C (patterned after the * cgi-lib.pl Perl routines). * * Author: Naba Barkakati */ /*--------------------------------------------------------------- * Copyright (c) 1996 Naba Barkakati (LNB Software, Inc.) * * Permission to use, copy, modify, and distribute this software * for any purpose and without any fee is hereby granted, * provided his notice is included in its entirety in the * documentation and in the source files. * * For more information about this software, consult: * * - "UNIX Webmaster Bible" published by IDG Books Worldwide * (Fall 1996) * * - LNB Software, Inc. Web site at http://www.lnbsoft.com/ * * This software and any related documentation is provided "as is" * without any warranty of any kind, either express or implied, * including, without limitation, the implied warranties of * merchantibility or fitness for a particular purpose. The entire * risk arising out of use or performance of the software remains * with you. * * In no event shall Naba Barkakati, LNB Software, or their agents * be liable for any cost, loss, or damage incurred due to the * use, malfunction, or misuse of the software or the inaccuracy * of the documentation associated with the software. *--------------------------------------------------------------- */ #include #include #include #include #include "cgi-lib.h" static char *FieldSep = "&"; static char *ValueSep = "="; /*-------------------------------------------------------------*/ /* R e a d P a r s e * * Read and parse the query string. This is the heart of this * library of routines. Define a FieldArray structure and pass * a pointer to that structure as the first argument. For example, * FieldArray fa; * ReadParse(&fa); * On return, the fa->count is the number of fields in the query and * fa->fields[] is the array of fields. * * Returns nonzero if everything goes well; zero in case of error. */ int ReadParse(FieldArray *fa) { if(fa == NULL) return 0; /* Initialize the FieldArray structure */ fa->count = 0; fa->fields = NULL; fa->query = NULL; if(MethGet()) { /* Method is GET; retrieve query from QUERY_STRING environment * variable */ char *p_env = getenv("QUERY_STRING"); if(p_env == NULL) return 0; /* Copy the environ variable's value into another string */ if((fa->query = strdup(p_env)) != NULL) return ParseQuery(fa); else { Cleanup(fa); return 0; } } if(MethPost()) { /* Method is POST; read query from stdin; read number of bytes * specified in CONTENT_LENGTH environment variable. */ char *p_env = getenv("CONTENT_LENGTH"); int numbytes; if(p_env == NULL) return 0; numbytes = atoi(p_env); /* Allocate memory to hold the query */ fa->query = malloc(numbytes + 1); if(fa->query != NULL) { int n = fread(fa->query, sizeof(char), numbytes, stdin); fa->query[n] = '\0'; return ParseQuery(fa); } else { Cleanup(fa); return 0; } } /* Unknown method */ return 0; } /*-------------------------------------------------------------*/ /* P a r s e Q u e r y * * Parses a query string "in-place" using strtok and returns * results in an FieldArray structure (through an array of * pointers). Returns non-zero if all goes well. */ int ParseQuery(FieldArray *fa) { int i, nf = 1; if(fa->query == NULL) return 0; /* Count the ampersands in the query to find out number of fields */ for(i = 0; fa->query[i] != '\0'; i++) { if(fa->query[i] == '&') nf++; } fa->count = nf; /* Allocate that many array of Field structures */ if((fa->fields = (Field*)calloc(nf, sizeof(Field))) == NULL) { Cleanup(fa); return 0; } /* Split up the fields by looking for the ampersands */ fa->fields[0].name = strtok(fa->query, FieldSep); /* Now get the rest of the fields */ for(i = 1; i < nf; i++) { fa->fields[i].name = strtok(NULL, FieldSep); } /* Now, split each field into name and value at the = sign */ for(i = 0; i < nf; i++) { fa->fields[i].name = strtok(fa->fields[i].name, ValueSep); fa->fields[i].value = strtok(NULL, ValueSep); } /* Translate plus signs to spaces and %xx to single characters */ /* First work through the names */ for(i = 0; i < nf; i++) { int j; /* First convert + to space */ for(j = 0; fa->fields[i].name[j] != '\0'; j++) { if(fa->fields[i].name[j] == '+') fa->fields[i].name[j] = ' '; } /* Next convert each %xx to a single character */ for(j = 0; fa->fields[i].name[j] != '\0'; j++) { if(fa->fields[i].name[j] == '%') { int k; fa->fields[i].name[j] = 16 * HexNum(fa->fields[i].name[j+1]) + HexNum(fa->fields[i].name[j+2]); /* Move rest of string two spaces to the left */ for(k = j+3; fa->fields[i].name[k] != '\0'; k++) fa->fields[i].name[k-2] = fa->fields[i].name[k]; /* Place a null character to mark end of string */ fa->fields[i].name[k-2] = '\0'; } } } /* Now process the values */ for(i = 0; i < nf; i++) { int j; /* Skip fields with missing values (the value is NULL) */ if(fa->fields[i].value == NULL) continue; /* First convert + to space */ for(j = 0; fa->fields[i].value[j] != '\0'; j++) { if(fa->fields[i].value[j] == '+') fa->fields[i].value[j] = ' '; } /* Next convert each %xx to a single character */ for(j = 0; fa->fields[i].value[j] != '\0'; j++) { if(fa->fields[i].value[j] == '%') { int k; fa->fields[i].value[j] = 16 * HexNum(fa->fields[i].value[j+1]) + HexNum(fa->fields[i].value[j+2]); /* Move rest of string two spaces to the left */ for(k = j+3; fa->fields[i].value[k] != '\0'; k++) fa->fields[i].value[k-2] = fa->fields[i].value[k]; /* Place a null character to mark end of string */ fa->fields[i].value[k-2] = '\0'; } } } return 1; } /*-------------------------------------------------------------*/ /* C l e a n u p * Cleans up all allocated memory in the FieldArray structure fa-> */ void Cleanup(FieldArray *fa) { if(fa == NULL) return; if(fa->query != NULL) free(fa->query); if(fa->fields != NULL) free(fa->fields); } /*-------------------------------------------------------------*/ /* M e t h G e t * Returns nonzero if method is GET. */ int MethGet(void) { char *p_env = getenv("REQUEST_METHOD"); if(p_env == NULL) return 0; return(strcasecmp(p_env, "get") == 0); } /*-------------------------------------------------------------*/ /* M e t h P o s t * Returns nonzero if method is POST. */ int MethPost(void) { char *p_env = getenv("REQUEST_METHOD"); if(p_env == NULL) return 0; return(strcasecmp(p_env, "post") == 0); } /*-------------------------------------------------------------*/ /* G e t V a l u e * * Return the value of a field (specified by name), if found. * Else, return NULL. */ const char *GetValue(const FieldArray *fa, const char *name) { int i; for(i = 0; i < fa->count; i++) { if(strcmp(name, fa->fields[i].name) == 0) return fa->fields[i].value; } return NULL; } /*-------------------------------------------------------------*/ /* P r i n t V a r i a b l e s * * Prints the name and value of the fields (like the subroutine * with the same name in cgi-lib.pl Perl library). */ void PrintVariables(const FieldArray *fa) { int i; printf("
\n"); for(i = 0; i < fa->count; i++) { printf("
%s
\n", fa->fields[i].name); printf("
%s
\n", fa->fields[i].value); } printf("
\n"); } /*-------------------------------------------------------------*/ /* P r i n t H e a d e r * * Print the MIME header */ void PrintHeader(void) { printf("Content-type: text/html\n\n"); } /*-------------------------------------------------------------*/ /* H t m l T o p * * Print the top part of the HTML document */ void HtmlTop(const char *title) { printf("\n\n%s\n", title); printf("\n

%s

\n", title); } /*-------------------------------------------------------------*/ /* H t m l B o t * * Print the bottom part of the HTML document */ void HtmlBot(void) { printf("\n\n"); } /*---------------------------------------------------------------*/ /* H e x N u m * * Returns the decimal value corresponding to a hexadecimal * digit. */ int HexNum(int c) { if(isdigit(c)) { return c - '0'; } else { int cl = toupper(c); return cl - 'A' + 10; } }