?? cgic.c
字號:
/* cgicTempDir is the only setting you are likely to need to change in this file. *//* Used only in Unix environments, in conjunction with mkstemp(). Elsewhere (Windows), temporary files go where the tmpnam() function suggests. If this behavior does not work for you, modify the getTempFileName() function to suit your needs. */#define cgicTempDir "/tmp"#if CGICDEBUG#define CGICDEBUGSTART \ { \ FILE *dout; \ dout = fopen("/home/boutell/public_html/debug", "a"); \ #define CGICDEBUGEND \ fclose(dout); \ }#else /* CGICDEBUG */#define CGICDEBUGSTART#define CGICDEBUGEND#endif /* CGICDEBUG */#include <stdio.h>#include <string.h>#include <ctype.h>#include <stdlib.h>#include <time.h>#include <sys/types.h>#include <sys/stat.h>#ifdef WIN32#include <io.h>/* cgic 2.01 */#include <fcntl.h>#else#include <unistd.h>#endif /* WIN32 */#include "cgic.h"#define cgiStrEq(a, b) (!strcmp((a), (b)))char *cgiServerSoftware;char *cgiServerName;char *cgiGatewayInterface;char *cgiServerProtocol;char *cgiServerPort;char *cgiRequestMethod;char *cgiPathInfo;char *cgiPathTranslated;char *cgiScriptName;char *cgiQueryString;char *cgiRemoteHost;char *cgiRemoteAddr;char *cgiAuthType;char *cgiRemoteUser;char *cgiRemoteIdent;char cgiContentTypeData[1024];char *cgiContentType = cgiContentTypeData;char *cgiMultipartBoundary;char *cgiCookie;int cgiContentLength;char *cgiAccept;char *cgiUserAgent;char *cgiReferrer;FILE *cgiIn;FILE *cgiOut;/* True if CGI environment was restored from a file. */static int cgiRestored = 0;static void cgiGetenv(char **s, char *var);typedef enum { cgiParseSuccess, cgiParseMemory, cgiParseIO} cgiParseResultType;/* One form entry, consisting of an attribute-value pair, and an optional filename and content type. All of these are guaranteed to be valid null-terminated strings, which will be of length zero in the event that the field is not present, with the exception of tfileName which will be null when 'in' is null. DO NOT MODIFY THESE VALUES. Make local copies if modifications are desired. */typedef struct cgiFormEntryStruct { char *attr; /* value is populated for regular form fields only. For file uploads, it points to an empty string, and file upload data should be read from the file tfileName. */ char *value; /* When fileName is not an empty string, tfileName is not null, and 'value' points to an empty string. */ /* Valid for both files and regular fields; does not include terminating null of regular fields. */ int valueLength; char *fileName; char *contentType; /* Temporary file name for working storage of file uploads. */ char *tfileName; struct cgiFormEntryStruct *next;} cgiFormEntry;/* The first form entry. */static cgiFormEntry *cgiFormEntryFirst;static cgiParseResultType cgiParseGetFormInput();static cgiParseResultType cgiParsePostFormInput();static cgiParseResultType cgiParsePostMultipartInput();static cgiParseResultType cgiParseFormInput(char *data, int length);static void cgiSetupConstants();static void cgiFreeResources();static int cgiStrEqNc(char *s1, char *s2);static int cgiStrBeginsNc(char *s1, char *s2);int main(int argc, char *argv[]) { int result; char *cgiContentLengthString; char *e; cgiSetupConstants(); cgiGetenv(&cgiServerSoftware, "SERVER_SOFTWARE"); cgiGetenv(&cgiServerName, "SERVER_NAME"); cgiGetenv(&cgiGatewayInterface, "GATEWAY_INTERFACE"); cgiGetenv(&cgiServerProtocol, "SERVER_PROTOCOL"); cgiGetenv(&cgiServerPort, "SERVER_PORT"); cgiGetenv(&cgiRequestMethod, "REQUEST_METHOD"); cgiGetenv(&cgiPathInfo, "PATH_INFO"); cgiGetenv(&cgiPathTranslated, "PATH_TRANSLATED"); cgiGetenv(&cgiScriptName, "SCRIPT_NAME"); cgiGetenv(&cgiQueryString, "QUERY_STRING"); cgiGetenv(&cgiRemoteHost, "REMOTE_HOST"); cgiGetenv(&cgiRemoteAddr, "REMOTE_ADDR"); cgiGetenv(&cgiAuthType, "AUTH_TYPE"); cgiGetenv(&cgiRemoteUser, "REMOTE_USER"); cgiGetenv(&cgiRemoteIdent, "REMOTE_IDENT"); /* 2.0: the content type string needs to be parsed and modified, so copy it to a buffer. */ e = getenv("CONTENT_TYPE"); if (e) { if (strlen(e) < sizeof(cgiContentTypeData)) { strcpy(cgiContentType, e); } else { /* Truncate safely in the event of what is almost certainly a hack attempt */ strncpy(cgiContentType, e, sizeof(cgiContentTypeData)); cgiContentType[sizeof(cgiContentTypeData) - 1] = '\0'; } } else { cgiContentType[0] = '\0'; } /* Never null */ cgiMultipartBoundary = ""; /* 2.0: parse semicolon-separated additional parameters of the content type. The one we're interested in is 'boundary'. We discard the rest to make cgiContentType more useful to the typical programmer. */ if (strchr(cgiContentType, ';')) { char *sat = strchr(cgiContentType, ';'); while (sat) { *sat = '\0'; sat++; while (isspace(*sat)) { sat++; } if (cgiStrBeginsNc(sat, "boundary=")) { char *s; cgiMultipartBoundary = sat + strlen("boundary="); s = cgiMultipartBoundary; while ((*s) && (!isspace(*s))) { s++; } *s = '\0'; break; } else { sat = strchr(sat, ';'); } } } cgiGetenv(&cgiContentLengthString, "CONTENT_LENGTH"); cgiContentLength = atoi(cgiContentLengthString); cgiGetenv(&cgiAccept, "HTTP_ACCEPT"); cgiGetenv(&cgiUserAgent, "HTTP_USER_AGENT"); cgiGetenv(&cgiReferrer, "HTTP_REFERER"); cgiGetenv(&cgiCookie, "HTTP_COOKIE");#ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "%d\n", cgiContentLength); fprintf(dout, "%s\n", cgiRequestMethod); fprintf(dout, "%s\n", cgiContentType); CGICDEBUGEND #endif /* CGICDEBUG */#ifdef WIN32 /* 1.07: Must set stdin and stdout to binary mode */ /* 2.0: this is particularly crucial now and must not be removed */ _setmode( _fileno( stdin ), _O_BINARY ); _setmode( _fileno( stdout ), _O_BINARY );#endif /* WIN32 */ cgiFormEntryFirst = 0; cgiIn = stdin; cgiOut = stdout; cgiRestored = 0; /* These five lines keep compilers from producing warnings that argc and argv are unused. They have no actual function. */ if (argc) { if (argv[0]) { cgiRestored = 0; } } if (cgiStrEqNc(cgiRequestMethod, "post")) {#ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "POST recognized\n"); CGICDEBUGEND#endif /* CGICDEBUG */ if (cgiStrEqNc(cgiContentType, "application/x-www-form-urlencoded")) { #ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "Calling PostFormInput\n"); CGICDEBUGEND #endif /* CGICDEBUG */ if (cgiParsePostFormInput() != cgiParseSuccess) {#ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "PostFormInput failed\n"); CGICDEBUGEND #endif /* CGICDEBUG */ cgiFreeResources(); return -1; } #ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "PostFormInput succeeded\n"); CGICDEBUGEND #endif /* CGICDEBUG */ } else if (cgiStrEqNc(cgiContentType, "multipart/form-data")) {#ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "Calling PostMultipartInput\n"); CGICDEBUGEND #endif /* CGICDEBUG */ if (cgiParsePostMultipartInput() != cgiParseSuccess) {#ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "PostMultipartInput failed\n"); CGICDEBUGEND #endif /* CGICDEBUG */ cgiFreeResources(); return -1; } #ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "PostMultipartInput succeeded\n"); CGICDEBUGEND #endif /* CGICDEBUG */ } } else if (cgiStrEqNc(cgiRequestMethod, "get")) { /* The spec says this should be taken care of by the server, but... it isn't */ cgiContentLength = strlen(cgiQueryString); if (cgiParseGetFormInput() != cgiParseSuccess) {#ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "GetFormInput failed\n"); CGICDEBUGEND #endif /* CGICDEBUG */ cgiFreeResources(); return -1; } else { #ifdef CGICDEBUG CGICDEBUGSTART fprintf(dout, "GetFormInput succeeded\n"); CGICDEBUGEND #endif /* CGICDEBUG */ } } result = cgiMain(); cgiFreeResources(); return result;}static void cgiGetenv(char **s, char *var){ *s = getenv(var); if (!(*s)) { *s = ""; }}static cgiParseResultType cgiParsePostFormInput() { char *input; cgiParseResultType result; if (!cgiContentLength) { return cgiParseSuccess; } input = (char *) malloc(cgiContentLength); if (!input) { return cgiParseMemory; } if (((int) fread(input, 1, cgiContentLength, cgiIn)) != cgiContentLength) { return cgiParseIO; } result = cgiParseFormInput(input, cgiContentLength); free(input); return result;}/* 2.0: A virtual datastream supporting putback of enough characters to handle multipart boundaries easily. A simple memset(&mp, 0, sizeof(mp)) is suitable initialization. */typedef struct { /* Buffer for putting characters back */ char putback[1024]; /* Position in putback from which next character will be read. If readPos == writePos, then next character should come from cgiIn. */ int readPos; /* Position in putback to which next character will be put back. If writePos catches up to readPos, as opposed to the other way around, the stream no longer functions properly. Calling code must guarantee that no more than sizeof(putback) bytes are put back at any given time. */ int writePos; /* Offset in the virtual datastream; can be compared to cgiContentLength */ int offset;} mpStream, *mpStreamPtr;int mpRead(mpStreamPtr mpp, char *buffer, int len){ int ilen = len; int got = 0; while (len) { if (mpp->readPos != mpp->writePos) { *buffer++ = mpp->putback[mpp->readPos++]; mpp->readPos %= sizeof(mpp->putback); got++; len--; } else { break; } } /* Refuse to read past the declared length in order to avoid deadlock */ if (len > (cgiContentLength - mpp->offset)) { len = cgiContentLength - mpp->offset; } if (len) { int fgot = fread(buffer, 1, len, cgiIn); if (fgot >= 0) { mpp->offset += (got + fgot); return got + fgot; } else if (got > 0) { mpp->offset += got; return got; } else { /* EOF or error */ return fgot; } } else if (got) { return got; } else if (ilen) { return EOF; } else { /* 2.01 */ return 0; }}void mpPutBack(mpStreamPtr mpp, char *data, int len){ mpp->offset -= len; while (len) { mpp->putback[mpp->writePos++] = *data++; mpp->writePos %= sizeof(mpp->putback); len--; }}/* This function copies the body to outf if it is not null, otherwise to a newly allocated character buffer at *outP, which will be null terminated; if both outf and outP are null the body is not stored. If bodyLengthP is not null, the size of the body in bytes is stored to *bodyLengthP, not including any terminating null added to *outP. If 'first' is nonzero, a preceding newline is not expected before the boundary. If 'first' is zero, a preceding newline is expected. Upon return mpp is positioned after the boundary and its trailing newline, if any; if the boundary is followed by -- the next two characters read after this function returns will be --. Upon error, if outP is not null, *outP is a null pointer; *bodyLengthP is set to zero. Returns cgiParseSuccess, cgiParseMemory or cgiParseIO. */static cgiParseResultType afterNextBoundary(mpStreamPtr mpp, FILE *outf, char **outP, int *bodyLengthP, int first );static int readHeaderLine( mpStreamPtr mpp, char *attr, int attrSpace, char *value, int valueSpace);static void decomposeValue(char *value, char *mvalue, int mvalueSpace, char **argNames, char **argValues, int argValueSpace);/* tfileName must be 1024 bytes to ensure adequacy on win32 (1024 exceeds the maximum path length and certainly exceeds observed behavior of _tmpnam). May as well also be 1024 bytes on Unix, although actual length is strlen(cgiTempDir) + a short unique pattern. */ static cgiParseResultType getTempFileName(char *tfileName);static cgiParseResultType cgiParsePostMultipartInput() { cgiParseResultType result; cgiFormEntry *n = 0, *l = 0; int got; FILE *outf = 0; char *out = 0; char tfileName[1024]; mpStream mp; mpStreamPtr mpp = ∓ memset(&mp, 0, sizeof(mp)); if (!cgiContentLength) { return cgiParseSuccess; } /* Read first boundary, including trailing newline */ result = afterNextBoundary(mpp, 0, 0, 0, 1); if (result == cgiParseIO) { /* An empty submission is not necessarily an error */ return cgiParseSuccess; } else if (result != cgiParseSuccess) { return result; } while (1) { char d[1024]; char fvalue[1024]; char fname[1024]; int bodyLength = 0; char ffileName[1024]; char fcontentType[1024]; char attr[1024]; char value[1024]; fvalue[0] = 0; fname[0] = 0; ffileName[0] = 0; fcontentType[0] = 0; out = 0; outf = 0; /* Check for EOF */ got = mpRead(mpp, d, 2); if (got < 2) { /* Crude EOF */ break; } if ((d[0] == '-') && (d[1] == '-')) { /* Graceful EOF */ break; } mpPutBack(mpp, d, 2); /* Read header lines until end of header */ while (readHeaderLine( mpp, attr, sizeof(attr), value, sizeof(value))) { char *argNames[3]; char *argValues[2]; /* Content-Disposition: form-data; name="test"; filename="googley.gif" */ if (cgiStrEqNc(attr, "Content-Disposition")) { argNames[0] = "name"; argNames[1] = "filename"; argNames[2] = 0; argValues[0] = fname; argValues[1] = ffileName; decomposeValue(value, fvalue, sizeof(fvalue), argNames, argValues, 1024); } else if (cgiStrEqNc(attr, "Content-Type")) { argNames[0] = 0; decomposeValue(value, fcontentType, sizeof(fcontentType), argNames, 0, 0); } } if (!cgiStrEqNc(fvalue, "form-data")) { /* Not form data */ continue; } /* Body is everything from here until the next boundary. So, set it aside and move past boundary. If a filename was submitted as part of the disposition header, store to a temporary file. Otherwise, store to a memory buffer (it is presumably a regular form field). */ if (strlen(ffileName)) { if (getTempFileName(tfileName) != cgiParseSuccess) { return cgiParseIO; } outf = fopen(tfileName, "w+b"); } else { outf = 0; tfileName[0] = '\0'; } result = afterNextBoundary(mpp, outf, &out, &bodyLength, 0); if (result != cgiParseSuccess) { /* Lack of a boundary here is an error. */ if (outf) { fclose(outf); unlink(tfileName); } if (out) { free(out); } return result; } /* OK, we have a new pair, add it to the list. */ n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry)); if (!n) { goto outOfMemory; } memset(n, 0, sizeof(cgiFormEntry)); /* 2.01: one of numerous new casts required to please C++ compilers */ n->attr = (char *) malloc(strlen(fname) + 1); if (!n->attr) { goto outOfMemory; } strcpy(n->attr, fname); if (out) { n->value = out; out = 0; } else if (outf) { n->value = (char *) malloc(1); if (!n->value) { goto outOfMemory; } n->value[0] = '\0'; fclose(outf); } n->valueLength = bodyLength; n->next = 0; if (!l) { cgiFormEntryFirst = n; } else { l->next = n; } n->fileName = (char *) malloc(strlen(ffileName) + 1); if (!n->fileName) { goto outOfMemory; } strcpy(n->fileName, ffileName); n->contentType = (char *) malloc(strlen(fcontentType) + 1); if (!n->contentType) { goto outOfMemory; } strcpy(n->contentType, fcontentType); n->tfileName = (char *) malloc(strlen(tfileName) + 1); if (!n->tfileName) { goto outOfMemory; } strcpy(n->tfileName, tfileName); l = n; } return cgiParseSuccess;outOfMemory: if (n) { if (n->attr) { free(n->attr); } if (n->value) { free(n->value); } if (n->fileName) { free(n->fileName); } if (n->tfileName) { free(n->tfileName); } if (n->contentType) { free(n->contentType); } free(n); } if (out) { free(out); } if (outf) { fclose(outf); unlink(tfileName); } return cgiParseMemory;}static cgiParseResultType getTempFileName(char *tfileName){#ifndef WIN32 /* Unix. Use the robust 'mkstemp' function to create a temporary file that is truly unique, with permissions that are truly safe. The fopen-for-write destroys any bogus information written by potential hackers during the brief window between the file's creation and the chmod call (glibc 2.0.6 and lower might otherwise have allowed this). */ int outfd; strcpy(tfileName, cgicTempDir "/cgicXXXXXX"); outfd = mkstemp(tfileName);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -