⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cgi.c

📁 goahead,一个非常好用的web服务器,支持html和cgi,可以运行在多个平台之上.
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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 = &mp;
	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;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -