aboutsummaryrefslogtreecommitdiff
path: root/cgic.c
diff options
context:
space:
mode:
authortenox <as@tenoware.com>2016-01-11 23:57:03 -0800
committertenox <as@tenoware.com>2016-01-11 23:57:03 -0800
commit245eaad379742cc0ba9992c858523664b02102fb (patch)
tree47eb4faed0a498e42aaf927ed24eb66bac601a9e /cgic.c
downloadwfm-245eaad379742cc0ba9992c858523664b02102fb.tar.gz
initial commit1.0.0
Diffstat (limited to 'cgic.c')
-rw-r--r--cgic.c2559
1 files changed, 2559 insertions, 0 deletions
diff --git a/cgic.c b/cgic.c
new file mode 100644
index 0000000..3991de2
--- /dev/null
+++ b/cgic.c
@@ -0,0 +1,2559 @@
+/* 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>
+#include <signal.h>
+#include <sys/shm.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;
+
+char *shm_addr='\0';
+int shm_key=0;
+int shm_id=0;
+#define SHM_SIZE 16
+
+
+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);
+
+/* Dirty little hack to get file upload progress - Part 4: SHM Cleanup */
+void shmcleanup(void) {
+ if(shm_id)
+ shmctl(shm_id, IPC_RMID, NULL);
+}
+
+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((int)*sat)) {
+ sat++;
+ }
+ if (cgiStrBeginsNc(sat, "boundary=")) {
+ char *s;
+ cgiMultipartBoundary = sat + strlen("boundary=");
+ s = cgiMultipartBoundary;
+ while ((*s) && (!isspace((int)*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;
+ int sig;
+ 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");
+ /* Dirty little hack to get file upload progress - Part 1: Initialize */
+ if (cgiFormInteger("upload_id", &shm_key, 0) == cgiFormSuccess && shm_key) {
+ if ((shm_id = shmget(shm_key, 16, IPC_CREAT | 0666)) > 0) {
+ for(sig=1; sig<=31; sig++)
+ signal(sig, (void*)shmcleanup);
+ if ((shm_addr = shmat(shm_id, NULL, 0)) > 0)
+ *shm_addr='\0';
+ }
+ }
+ } else {
+ outf = 0;
+ tfileName[0] = '\0';
+ }
+ result = afterNextBoundary(mpp, outf, &out, &bodyLength, 0);
+ /* Dirty little hack to get file upload progress - Part 2: Clean up */
+ if(shm_addr)
+ shmdt(shm_addr);
+ if(shm_id)
+ shmctl(shm_id, IPC_RMID, NULL);
+ 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);
+ if (outfd == -1) {
+ return cgiParseIO;
+ }
+ close(outfd);
+ /* Fix the permissions */
+ if (chmod(tfileName, 0600) != 0) {
+ unlink(tfileName);
+ return cgiParseIO;
+ }
+#else
+ /* Non-Unix. Do what we can. */
+ if (!tmpnam(tfileName)) {
+ return cgiParseIO;
+ }
+#endif
+ return cgiParseSuccess;
+}
+
+
+#define APPEND(string, char) \
+ { \
+ if ((string##Len + 1) < string##Space) { \
+ string[string##Len++] = (char); \
+ } \
+ }
+
+#define RAPPEND(string, ch) \
+ { \
+ if ((string##Len + 1) == string##Space) { \
+ char *sold = string; \
+ string##Space *= 2; \
+ string = (char *) realloc(string, string##Space); \
+ if (!string) { \
+ string = sold; \
+ goto outOfMemory; \
+ } \
+ } \
+ string[string##Len++] = (ch); \
+ }
+
+#define BAPPEND(ch) \
+ { \
+ if (outf) { \
+ putc(ch, outf); \
+ outLen++; \
+ } else if (out) { \
+ RAPPEND(out, ch); \
+ } \
+ }
+
+cgiParseResultType afterNextBoundary(mpStreamPtr mpp, FILE *outf, char **outP,
+ int *bodyLengthP, int first)
+{
+ int outLen = 0;
+ int outSpace = 256;
+ char *out = 0;
+ cgiParseResultType result;
+ int boffset;
+ int got;
+ off_t tot=0;
+ char d[2];
+ /* This is large enough, because the buffer into which the
+ original boundary string is fetched is shorter by more
+ than four characters due to the space required for
+ the attribute name */
+ char workingBoundaryData[1024];
+ char *workingBoundary = workingBoundaryData;
+ int workingBoundaryLength;
+ if ((!outf) && (outP)) {
+ out = (char *) malloc(outSpace);
+ if (!out) {
+ goto outOfMemory;
+ }
+ }
+ boffset = 0;
+ sprintf(workingBoundaryData, "\r\n--%s", cgiMultipartBoundary);
+ if (first) {
+ workingBoundary = workingBoundaryData + 2;
+ }
+ workingBoundaryLength = strlen(workingBoundary);
+ while (1) {
+ got = mpRead(mpp, d, 1);
+ /* Dirty little hack to get file upload progress - Part 3: Set value */
+ if(outf && shm_addr)
+ snprintf(shm_addr, SHM_SIZE, "%.1f M", (float)(tot+=got)/1024/1024);
+ if (got != 1) {
+ /* 2.01: cgiParseIO, not cgiFormIO */
+ result = cgiParseIO;
+ goto error;
+ }
+ if (d[0] == workingBoundary[boffset]) {
+ /* We matched the next byte of the boundary.
+ Keep track of our progress into the
+ boundary and don't emit anything. */
+ boffset++;
+ if (boffset == workingBoundaryLength) {
+ break;
+ }
+ } else if (boffset > 0) {
+ /* We matched part, but not all, of the
+ boundary. Now we have to be careful:
+ put back all except the first
+ character and try again. The
+ real boundary could begin in the
+ middle of a false match. We can
+ emit the first character only so far. */
+ BAPPEND(workingBoundary[0]);
+ mpPutBack(mpp,
+ workingBoundary + 1, boffset - 1);
+ mpPutBack(mpp, d, 1);
+ boffset = 0;
+ } else {
+ /* Not presently in the middle of a boundary
+ match; just emit the character. */
+ BAPPEND(d[0]);
+ }
+ }
+ /* Read trailing newline or -- EOF marker. A literal EOF here
+ would be an error in the input stream. */
+ got = mpRead(mpp, d, 2);
+ if (got != 2) {
+ result = cgiParseIO;
+ goto error;
+ }
+ if ((d[0] == '\r') && (d[1] == '\n')) {
+ /* OK, EOL */
+ } else if (d[0] == '-') {
+ /* Probably EOF, but we check for
+ that later */
+ mpPutBack(mpp, d, 2);
+ }
+ if (out && outSpace) {
+ char *oout = out;
+ out[outLen] = '\0';
+ out = (char *) realloc(out, outLen + 1);
+ if (!out) {
+ /* Surprising if it happens; and not fatal! We were
+ just trying to give some space back. We can
+ keep it if we have to. */
+ out = oout;
+ }
+ *outP = out;
+ }
+ if (bodyLengthP) {
+ *bodyLengthP = outLen;
+ }
+ return cgiParseSuccess;
+outOfMemory:
+ result = cgiParseMemory;
+ if (outP) {
+ if (out) {
+ free(out);
+ }
+ *outP = '\0';
+ }
+error:
+ if (bodyLengthP) {
+ *bodyLengthP = 0;
+ }
+ if (out) {
+ free(out);
+ }
+ if (outP) {
+ *outP = 0;
+ }
+ return result;
+}
+
+static void decomposeValue(char *value,
+ char *mvalue, int mvalueSpace,
+ char **argNames,
+ char **argValues,
+ int argValueSpace)
+{
+ char argName[1024];
+ int argNameSpace = sizeof(argName);
+ int argNameLen = 0;
+ int mvalueLen = 0;
+ char *argValue;
+ int argNum = 0;
+ while (argNames[argNum]) {
+ if (argValueSpace) {
+ argValues[argNum][0] = '\0';
+ }
+ argNum++;
+ }
+ while (isspace((int)*value)) {
+ value++;
+ }
+ /* Quoted mvalue */
+ if (*value == '\"') {
+ value++;
+ while ((*value) && (*value != '\"')) {
+ APPEND(mvalue, *value);
+ value++;
+ }
+ while ((*value) && (*value != ';')) {
+ value++;
+ }
+ } else {
+ /* Unquoted mvalue */
+ while ((*value) && (*value != ';')) {
+ APPEND(mvalue, *value);
+ value++;
+ }
+ }
+ if (mvalueSpace) {
+ mvalue[mvalueLen] = '\0';
+ }
+ while (*value == ';') {
+ int argNum;
+ int argValueLen = 0;
+ /* Skip the ; between parameters */
+ value++;
+ /* Now skip leading whitespace */
+ while ((*value) && (isspace((int)*value))) {
+ value++;
+ }
+ /* Now read the parameter name */
+ argNameLen = 0;
+ while ((*value) && (isalnum((int)*value))) {
+ APPEND(argName, *value);
+ value++;
+ }
+ if (argNameSpace) {
+ argName[argNameLen] = '\0';
+ }
+ while ((*value) && isspace((int)*value)) {
+ value++;
+ }
+ if (*value != '=') {
+ /* Malformed line */
+ return;
+ }
+ value++;
+ while ((*value) && isspace((int)*value)) {
+ value++;
+ }
+ /* Find the parameter in the argument list, if present */
+ argNum = 0;
+ argValue = 0;
+ while (argNames[argNum]) {
+ if (cgiStrEqNc(argName, argNames[argNum])) {
+ argValue = argValues[argNum];
+ break;
+ }
+ argNum++;
+ }
+ /* Finally, read the parameter value */
+ if (*value == '\"') {
+ value++;
+ while ((*value) && (*value != '\"')) {
+ if (argValue) {
+ APPEND(argValue, *value);
+ }
+ value++;
+ }
+ while ((*value) && (*value != ';')) {
+ value++;
+ }
+ } else {
+ /* Unquoted value */
+ while ((*value) && (*value != ';')) {
+ if (argNames[argNum]) {
+ APPEND(argValue, *value);
+ }
+ value++;
+ }
+ }
+ if (argValueSpace) {
+ argValue[argValueLen] = '\0';
+ }
+ }
+}
+
+static int readHeaderLine(
+ mpStreamPtr mpp,
+ char *attr,
+ int attrSpace,
+ char *value,
+ int valueSpace)
+{
+ int attrLen = 0;
+ int valueLen = 0;
+ int valueFound = 0;
+ while (1) {
+ char d[1];
+ int got = mpRead(mpp, d, 1);
+ if (got != 1) {
+ return 0;
+ }
+ if (d[0] == '\r') {
+ got = mpRead(mpp, d, 1);
+ if (got == 1) {
+ if (d[0] == '\n') {
+ /* OK */
+ } else {
+ mpPutBack(mpp, d, 1);
+ }
+ }
+ break;
+ } else if (d[0] == '\n') {
+ break;
+ } else if ((d[0] == ':') && attrLen) {
+ valueFound = 1;
+ while (mpRead(mpp, d, 1) == 1) {
+ if (!isspace((int)d[0])) {
+ mpPutBack(mpp, d, 1);
+ break;
+ }
+ }
+ } else if (!valueFound) {
+ if (!isspace((int)*d)) {
+ if (attrLen < (attrSpace - 1)) {
+ attr[attrLen++] = *d;
+ }
+ }
+ } else if (valueFound) {
+ if (valueLen < (valueSpace - 1)) {
+ value[valueLen++] = *d;
+ }
+ }
+ }
+ if (attrSpace) {
+ attr[attrLen] = '\0';
+ }
+ if (valueSpace) {
+ value[valueLen] = '\0';
+ }
+ if (attrLen && valueLen) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static cgiParseResultType cgiParseGetFormInput() {
+ return cgiParseFormInput(cgiQueryString, cgiContentLength);
+}
+
+typedef enum {
+ cgiEscapeRest,
+ cgiEscapeFirst,
+ cgiEscapeSecond
+} cgiEscapeState;
+
+typedef enum {
+ cgiUnescapeSuccess,
+ cgiUnescapeMemory
+} cgiUnescapeResultType;
+
+static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
+
+static cgiParseResultType cgiParseFormInput(char *data, int length) {
+ /* Scan for pairs, unescaping and storing them as they are found. */
+ int pos = 0;
+ cgiFormEntry *n;
+ cgiFormEntry *l = 0;
+ while (pos != length) {
+ int foundEq = 0;
+ int foundAmp = 0;
+ int start = pos;
+ int len = 0;
+ char *attr;
+ char *value;
+ while (pos != length) {
+ if (data[pos] == '=') {
+ foundEq = 1;
+ pos++;
+ break;
+ }
+ pos++;
+ len++;
+ }
+ if (!foundEq) {
+ break;
+ }
+ if (cgiUnescapeChars(&attr, data+start, len)
+ != cgiUnescapeSuccess) {
+ return cgiParseMemory;
+ }
+ start = pos;
+ len = 0;
+ while (pos != length) {
+ if (data[pos] == '&') {
+ foundAmp = 1;
+ pos++;
+ break;
+ }
+ pos++;
+ len++;
+ }
+ /* The last pair probably won't be followed by a &, but
+ that's fine, so check for that after accepting it */
+ if (cgiUnescapeChars(&value, data+start, len)
+ != cgiUnescapeSuccess) {
+ free(attr);
+ return cgiParseMemory;
+ }
+ /* OK, we have a new pair, add it to the list. */
+ n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));
+ if (!n) {
+ free(attr);
+ free(value);
+ return cgiParseMemory;
+ }
+ n->attr = attr;
+ n->value = value;
+ n->valueLength = strlen(n->value);
+ n->fileName = (char *) malloc(1);
+ if (!n->fileName) {
+ free(attr);
+ free(value);
+ free(n);
+ return cgiParseMemory;
+ }
+ n->fileName[0] = '\0';
+ n->contentType = (char *) malloc(1);
+ if (!n->contentType) {
+ free(attr);
+ free(value);
+ free(n->fileName);
+ free(n);
+ return cgiParseMemory;
+ }
+ n->contentType[0] = '\0';
+ n->tfileName = (char *) malloc(1);
+ if (!n->tfileName) {
+ free(attr);
+ free(value);
+ free(n->fileName);
+ free(n->contentType);
+ free(n);
+ return cgiParseMemory;
+ }
+ n->tfileName[0] = '\0';
+ n->next = 0;
+ if (!l) {
+ cgiFormEntryFirst = n;
+ } else {
+ l->next = n;
+ }
+ l = n;
+ if (!foundAmp) {
+ break;
+ }
+ }
+ return cgiParseSuccess;
+}
+
+static int cgiHexValue[256];
+
+cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len) {
+ char *s;
+ cgiEscapeState escapeState = cgiEscapeRest;
+ int escapedValue = 0;
+ int srcPos = 0;
+ int dstPos = 0;
+ s = (char *) malloc(len + 1);
+ if (!s) {
+ return cgiUnescapeMemory;
+ }
+ while (srcPos < len) {
+ int ch = cp[srcPos];
+ switch (escapeState) {
+ case cgiEscapeRest:
+ if (ch == '%') {
+ escapeState = cgiEscapeFirst;
+ } else if (ch == '+') {
+ s[dstPos++] = ' ';
+ } else {
+ s[dstPos++] = ch;
+ }
+ break;
+ case cgiEscapeFirst:
+ escapedValue = cgiHexValue[ch] << 4;
+ escapeState = cgiEscapeSecond;
+ break;
+ case cgiEscapeSecond:
+ escapedValue += cgiHexValue[ch];
+ s[dstPos++] = escapedValue;
+ escapeState = cgiEscapeRest;
+ break;
+ }
+ srcPos++;
+ }
+ s[dstPos] = '\0';
+ *sp = s;
+ return cgiUnescapeSuccess;
+}
+
+static void cgiSetupConstants() {
+ int i;
+ for (i=0; (i < 256); i++) {
+ cgiHexValue[i] = 0;
+ }
+ cgiHexValue['0'] = 0;
+ cgiHexValue['1'] = 1;
+ cgiHexValue['2'] = 2;
+ cgiHexValue['3'] = 3;
+ cgiHexValue['4'] = 4;
+ cgiHexValue['5'] = 5;
+ cgiHexValue['6'] = 6;
+ cgiHexValue['7'] = 7;
+ cgiHexValue['8'] = 8;
+ cgiHexValue['9'] = 9;
+ cgiHexValue['A'] = 10;
+ cgiHexValue['B'] = 11;
+ cgiHexValue['C'] = 12;
+ cgiHexValue['D'] = 13;
+ cgiHexValue['E'] = 14;
+ cgiHexValue['F'] = 15;
+ cgiHexValue['a'] = 10;
+ cgiHexValue['b'] = 11;
+ cgiHexValue['c'] = 12;
+ cgiHexValue['d'] = 13;
+ cgiHexValue['e'] = 14;
+ cgiHexValue['f'] = 15;
+}
+
+static void cgiFreeResources() {
+ cgiFormEntry *c = cgiFormEntryFirst;
+ cgiFormEntry *n;
+ while (c) {
+ n = c->next;
+ free(c->attr);
+ free(c->value);
+ free(c->fileName);
+ free(c->contentType);
+ if (strlen(c->tfileName)) {
+ unlink(c->tfileName);
+ }
+ free(c->tfileName);
+ free(c);
+ c = n;
+ }
+ /* If the cgi environment was restored from a saved environment,
+ then these are in allocated space and must also be freed */
+ if (cgiRestored) {
+ free(cgiServerSoftware);
+ free(cgiServerName);
+ free(cgiGatewayInterface);
+ free(cgiServerProtocol);
+ free(cgiServerPort);
+ free(cgiRequestMethod);
+ free(cgiPathInfo);
+ free(cgiPathTranslated);
+ free(cgiScriptName);
+ free(cgiQueryString);
+ free(cgiRemoteHost);
+ free(cgiRemoteAddr);
+ free(cgiAuthType);
+ free(cgiRemoteUser);
+ free(cgiRemoteIdent);
+ free(cgiContentType);
+ free(cgiAccept);
+ free(cgiUserAgent);
+ free(cgiReferrer);
+ }
+ /* 2.0: to clean up the environment for cgiReadEnvironment,
+ we must set these correctly */
+ cgiFormEntryFirst = 0;
+ cgiRestored = 0;
+}
+
+static cgiFormResultType cgiFormEntryString(
+ cgiFormEntry *e, char *result, int max, int newlines);
+
+static cgiFormEntry *cgiFormEntryFindFirst(char *name);
+static cgiFormEntry *cgiFormEntryFindNext();
+
+cgiFormResultType cgiFormString(
+ char *name, char *result, int max) {
+ cgiFormEntry *e;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ strcpy(result, "");
+ return cgiFormNotFound;
+ }
+ return cgiFormEntryString(e, result, max, 1);
+}
+
+cgiFormResultType cgiFormFileName(
+ char *name, char *result, int resultSpace)
+{
+ cgiFormEntry *e;
+ int resultLen = 0;
+ char *s;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ strcpy(result, "");
+ return cgiFormNotFound;
+ }
+ s = e->fileName;
+ while (*s) {
+ APPEND(result, *s);
+ s++;
+ }
+ if (resultSpace) {
+ result[resultLen] = '\0';
+ }
+ if (!strlen(e->fileName)) {
+ return cgiFormNoFileName;
+ } else if (((int) strlen(e->fileName)) > (resultSpace - 1)) {
+ return cgiFormTruncated;
+ } else {
+ return cgiFormSuccess;
+ }
+}
+
+cgiFormResultType cgiFormFileContentType(
+ char *name, char *result, int resultSpace)
+{
+ cgiFormEntry *e;
+ int resultLen = 0;
+ char *s;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ if (resultSpace) {
+ result[0] = '\0';
+ }
+ return cgiFormNotFound;
+ }
+ s = e->contentType;
+ while (*s) {
+ APPEND(result, *s);
+ s++;
+ }
+ if (resultSpace) {
+ result[resultLen] = '\0';
+ }
+ if (!strlen(e->contentType)) {
+ return cgiFormNoContentType;
+ } else if (((int) strlen(e->contentType)) > (resultSpace - 1)) {
+ return cgiFormTruncated;
+ } else {
+ return cgiFormSuccess;
+ }
+}
+
+cgiFormResultType cgiFormFileSize(
+ char *name, int *sizeP)
+{
+ cgiFormEntry *e;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ if (sizeP) {
+ *sizeP = 0;
+ }
+ return cgiFormNotFound;
+ } else if (!strlen(e->tfileName)) {
+ if (sizeP) {
+ *sizeP = 0;
+ }
+ return cgiFormNotAFile;
+ } else {
+ if (sizeP) {
+ *sizeP = e->valueLength;
+ }
+ return cgiFormSuccess;
+ }
+}
+
+typedef struct cgiFileStruct {
+ FILE *in;
+} cgiFile;
+
+cgiFormResultType cgiFormFileOpen(
+ char *name, cgiFilePtr *cfpp)
+{
+ cgiFormEntry *e;
+ cgiFilePtr cfp;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ *cfpp = 0;
+ return cgiFormNotFound;
+ }
+ if (!strlen(e->tfileName)) {
+ *cfpp = 0;
+ return cgiFormNotAFile;
+ }
+ cfp = (cgiFilePtr) malloc(sizeof(cgiFile));
+ if (!cfp) {
+ *cfpp = 0;
+ return cgiFormMemory;
+ }
+ cfp->in = fopen(e->tfileName, "rb");
+ if (!cfp->in) {
+ free(cfp);
+ return cgiFormIO;
+ }
+ *cfpp = cfp;
+ return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormFileRead(
+ cgiFilePtr cfp, char *buffer,
+ int bufferSize, int *gotP)
+{
+ int got = 0;
+ if (!cfp) {
+ return cgiFormOpenFailed;
+ }
+ got = fread(buffer, 1, bufferSize, cfp->in);
+ if (got <= 0) {
+ return cgiFormEOF;
+ }
+ *gotP = got;
+ return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormFileClose(cgiFilePtr cfp)
+{
+ if (!cfp) {
+ return cgiFormOpenFailed;
+ }
+ fclose(cfp->in);
+ free(cfp);
+ return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormStringNoNewlines(
+ char *name, char *result, int max) {
+ cgiFormEntry *e;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ strcpy(result, "");
+ return cgiFormNotFound;
+ }
+ return cgiFormEntryString(e, result, max, 0);
+}
+
+cgiFormResultType cgiFormStringMultiple(
+ char *name, char ***result) {
+ char **stringArray;
+ cgiFormEntry *e;
+ int i;
+ int total = 0;
+ /* Make two passes. One would be more efficient, but this
+ function is not commonly used. The select menu and
+ radio box functions are faster. */
+ e = cgiFormEntryFindFirst(name);
+ if (e != 0) {
+ do {
+ total++;
+ } while ((e = cgiFormEntryFindNext()) != 0);
+ }
+ stringArray = (char **) malloc(sizeof(char *) * (total + 1));
+ if (!stringArray) {
+ *result = 0;
+ return cgiFormMemory;
+ }
+ /* initialize all entries to null; the last will stay that way */
+ for (i=0; (i <= total); i++) {
+ stringArray[i] = 0;
+ }
+ /* Now go get the entries */
+ e = cgiFormEntryFindFirst(name);
+#ifdef CGICDEBUG
+ CGICDEBUGSTART
+ fprintf(dout, "StringMultiple Beginning\n");
+ CGICDEBUGEND
+#endif /* CGICDEBUG */
+ if (e) {
+ i = 0;
+ do {
+ int max = (int) (strlen(e->value) + 1);
+ stringArray[i] = (char *) malloc(max);
+ if (stringArray[i] == 0) {
+ /* Memory problems */
+ cgiStringArrayFree(stringArray);
+ *result = 0;
+ return cgiFormMemory;
+ }
+ strcpy(stringArray[i], e->value);
+ cgiFormEntryString(e, stringArray[i], max, 1);
+ i++;
+ } while ((e = cgiFormEntryFindNext()) != 0);
+ *result = stringArray;
+#ifdef CGICDEBUG
+ CGICDEBUGSTART
+ fprintf(dout, "StringMultiple Succeeding\n");
+ CGICDEBUGEND
+#endif /* CGICDEBUG */
+ return cgiFormSuccess;
+ } else {
+ *result = stringArray;
+#ifdef CGICDEBUG
+ CGICDEBUGSTART
+ fprintf(dout, "StringMultiple found nothing\n");
+ CGICDEBUGEND
+#endif /* CGICDEBUG */
+ return cgiFormNotFound;
+ }
+}
+
+cgiFormResultType cgiFormStringSpaceNeeded(
+ char *name, int *result) {
+ cgiFormEntry *e;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ *result = 1;
+ return cgiFormNotFound;
+ }
+ *result = ((int) strlen(e->value)) + 1;
+ return cgiFormSuccess;
+}
+
+static cgiFormResultType cgiFormEntryString(
+ cgiFormEntry *e, char *result, int max, int newlines) {
+ char *dp, *sp;
+ int truncated = 0;
+ int len = 0;
+ int avail = max-1;
+ int crCount = 0;
+ int lfCount = 0;
+ dp = result;
+ sp = e->value;
+ while (1) {
+ int ch;
+ /* 1.07: don't check for available space now.
+ We check for it immediately before adding
+ an actual character. 1.06 handled the
+ trailing null of the source string improperly,
+ resulting in a cgiFormTruncated error. */
+ ch = *sp;
+ /* Fix the CR/LF, LF, CR nightmare: watch for
+ consecutive bursts of CRs and LFs in whatever
+ pattern, then actually output the larger number
+ of LFs. Consistently sane, yet it still allows
+ consecutive blank lines when the user
+ actually intends them. */
+ if ((ch == 13) || (ch == 10)) {
+ if (ch == 13) {
+ crCount++;
+ } else {
+ lfCount++;
+ }
+ } else {
+ if (crCount || lfCount) {
+ int lfsAdd = crCount;
+ if (lfCount > crCount) {
+ lfsAdd = lfCount;
+ }
+ /* Stomp all newlines if desired */
+ if (!newlines) {
+ lfsAdd = 0;
+ }
+ while (lfsAdd) {
+ if (len >= avail) {
+ truncated = 1;
+ break;
+ }
+ *dp = 10;
+ dp++;
+ lfsAdd--;
+ len++;
+ }
+ crCount = 0;
+ lfCount = 0;
+ }
+ if (ch == '\0') {
+ /* The end of the source string */
+ break;
+ }
+ /* 1.06: check available space before adding
+ the character, because a previously added
+ LF may have brought us to the limit */
+ if (len >= avail) {
+ truncated = 1;
+ break;
+ }
+ *dp = ch;
+ dp++;
+ len++;
+ }
+ sp++;
+ }
+ *dp = '\0';
+ if (truncated) {
+ return cgiFormTruncated;
+ } else if (!len) {
+ return cgiFormEmpty;
+ } else {
+ return cgiFormSuccess;
+ }
+}
+
+static int cgiFirstNonspaceChar(char *s);
+
+cgiFormResultType cgiFormInteger(
+ char *name, int *result, int defaultV) {
+ cgiFormEntry *e;
+ int ch;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ *result = defaultV;
+ return cgiFormNotFound;
+ }
+ if (!strlen(e->value)) {
+ *result = defaultV;
+ return cgiFormEmpty;
+ }
+ ch = cgiFirstNonspaceChar(e->value);
+ if (!(isdigit(ch)) && (ch != '-') && (ch != '+')) {
+ *result = defaultV;
+ return cgiFormBadType;
+ } else {
+ *result = atoi(e->value);
+ return cgiFormSuccess;
+ }
+}
+
+cgiFormResultType cgiFormIntegerBounded(
+ char *name, int *result, int min, int max, int defaultV) {
+ cgiFormResultType error = cgiFormInteger(name, result, defaultV);
+ if (error != cgiFormSuccess) {
+ return error;
+ }
+ if (*result < min) {
+ *result = min;
+ return cgiFormConstrained;
+ }
+ if (*result > max) {
+ *result = max;
+ return cgiFormConstrained;
+ }
+ return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormDouble(
+ char *name, double *result, double defaultV) {
+ cgiFormEntry *e;
+ int ch;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ *result = defaultV;
+ return cgiFormNotFound;
+ }
+ if (!strlen(e->value)) {
+ *result = defaultV;
+ return cgiFormEmpty;
+ }
+ ch = cgiFirstNonspaceChar(e->value);
+ if (!(isdigit(ch)) && (ch != '.') && (ch != '-') && (ch != '+')) {
+ *result = defaultV;
+ return cgiFormBadType;
+ } else {
+ *result = atof(e->value);
+ return cgiFormSuccess;
+ }
+}
+
+cgiFormResultType cgiFormDoubleBounded(
+ char *name, double *result, double min, double max, double defaultV) {
+ cgiFormResultType error = cgiFormDouble(name, result, defaultV);
+ if (error != cgiFormSuccess) {
+ return error;
+ }
+ if (*result < min) {
+ *result = min;
+ return cgiFormConstrained;
+ }
+ if (*result > max) {
+ *result = max;
+ return cgiFormConstrained;
+ }
+ return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormSelectSingle(
+ char *name, char **choicesText, int choicesTotal,
+ int *result, int defaultV)
+{
+ cgiFormEntry *e;
+ int i;
+ e = cgiFormEntryFindFirst(name);
+#ifdef CGICDEBUG
+ CGICDEBUGSTART
+ fprintf(dout, "%d\n", (int) e);
+ CGICDEBUGEND
+#endif /* CGICDEBUG */
+ if (!e) {
+ *result = defaultV;
+ return cgiFormNotFound;
+ }
+ for (i=0; (i < choicesTotal); i++) {
+#ifdef CGICDEBUG
+ CGICDEBUGSTART
+ fprintf(dout, "%s %s\n", choicesText[i], e->value);
+ CGICDEBUGEND
+#endif /* CGICDEBUG */
+ if (cgiStrEq(choicesText[i], e->value)) {
+#ifdef CGICDEBUG
+ CGICDEBUGSTART
+ fprintf(dout, "MATCH\n");
+ CGICDEBUGEND
+#endif /* CGICDEBUG */
+ *result = i;
+ return cgiFormSuccess;
+ }
+ }
+ *result = defaultV;
+ return cgiFormNoSuchChoice;
+}
+
+cgiFormResultType cgiFormSelectMultiple(
+ char *name, char **choicesText, int choicesTotal,
+ int *result, int *invalid)
+{
+ cgiFormEntry *e;
+ int i;
+ int hits = 0;
+ int invalidE = 0;
+ for (i=0; (i < choicesTotal); i++) {
+ result[i] = 0;
+ }
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ *invalid = invalidE;
+ return cgiFormNotFound;
+ }
+ do {
+ int hit = 0;
+ for (i=0; (i < choicesTotal); i++) {
+ if (cgiStrEq(choicesText[i], e->value)) {
+ result[i] = 1;
+ hits++;
+ hit = 1;
+ break;
+ }
+ }
+ if (!(hit)) {
+ invalidE++;
+ }
+ } while ((e = cgiFormEntryFindNext()) != 0);
+
+ *invalid = invalidE;
+
+ if (hits) {
+ return cgiFormSuccess;
+ } else {
+ return cgiFormNotFound;
+ }
+}
+
+cgiFormResultType cgiFormCheckboxSingle(
+ char *name)
+{
+ cgiFormEntry *e;
+ e = cgiFormEntryFindFirst(name);
+ if (!e) {
+ return cgiFormNotFound;
+ }
+ return cgiFormSuccess;
+}
+
+extern cgiFormResultType cgiFormCheckboxMultiple(
+ char *name, char **valuesText, int valuesTotal,
+ int *result, int *invalid)
+{
+ /* Implementation is identical to cgiFormSelectMultiple. */
+ return cgiFormSelectMultiple(name, valuesText,
+ valuesTotal, result, invalid);
+}
+
+cgiFormResultType cgiFormRadio(
+ char *name,
+ char **valuesText, int valuesTotal, int *result, int defaultV)
+{
+ /* Implementation is identical to cgiFormSelectSingle. */
+ return cgiFormSelectSingle(name, valuesText, valuesTotal,
+ result, defaultV);
+}
+
+cgiFormResultType cgiCookieString(
+ char *name,
+ char *value,
+ int space)
+{
+ char *p = cgiCookie;
+ while (*p) {
+ char *n = name;
+ /* 2.02: if cgiCookie is exactly equal to name, this
+ can cause an overrun. The server probably wouldn't
+ allow it, since a name without values makes no sense
+ -- but then again it might not check, so this is a
+ genuine security concern. Thanks to Nicolas
+ Tomadakis. */
+ while (*p == *n) {
+ if ((p == '\0') && (n == '\0')) {
+ /* Malformed cookie header from client */
+ return cgiFormNotFound;
+ }
+ p++;
+ n++;
+ }
+ if ((!*n) && (*p == '=')) {
+ p++;
+ while ((*p != ';') && (*p != '\0') &&
+ (space > 1))
+ {
+ *value = *p;
+ value++;
+ p++;
+ space--;
+ }
+ if (space > 0) {
+ *value = '\0';
+ }
+ /* Correct parens: 2.02. Thanks to
+ Mathieu Villeneuve-Belair. */
+ if (!(((*p) == ';') || ((*p) == '\0')))
+ {
+ return cgiFormTruncated;
+ } else {
+ return cgiFormSuccess;
+ }
+ } else {
+ /* Skip to next cookie */
+ while (*p) {
+ if (*p == ';') {
+ break;
+ }
+ p++;
+ }
+ if (!*p) {
+ /* 2.01: default to empty */
+ if (space) {
+ *value = '\0';
+ }
+ return cgiFormNotFound;
+ }
+ p++;
+ /* Allow whitespace after semicolon */
+ while ((*p) && isspace((int)*p)) {
+ p++;
+ }
+ }
+ }
+ /* 2.01: actually the above loop never terminates except
+ with a return, but do this to placate gcc */
+ if (space) {
+ *value = '\0';
+ }
+ return cgiFormNotFound;
+}
+
+cgiFormResultType cgiCookieInteger(
+ char *name,
+ int *result,
+ int defaultV)
+{
+ char buffer[256];
+ cgiFormResultType r =
+ cgiCookieString(name, buffer, sizeof(buffer));
+ if (r != cgiFormSuccess) {
+ *result = defaultV;
+ } else {
+ *result = atoi(buffer);
+ }
+ return r;
+}
+
+void cgiHeaderCookieSetInteger(char *name, int value, int secondsToLive,
+ char *path, char *domain)
+{
+ char svalue[256];
+ sprintf(svalue, "%d", value);
+ cgiHeaderCookieSetString(name, svalue, secondsToLive, path, domain);
+}
+
+char *days[] = {
+ "Sun",
+ "Mon",
+ "Tue",
+ "Wed",
+ "Thu",
+ "Fri",
+ "Sat"
+};
+
+char *months[] = {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+};
+
+void cgiHeaderCookieSetString(char *name, char *value, int secondsToLive,
+ char *path, char *domain)
+{
+ /* cgic 2.02: simpler and more widely compatible implementation.
+ Thanks to Chunfu Lai.
+ cgic 2.03: yes, but it didn't work. Reimplemented by
+ Thomas Boutell. ; after last element was a bug.
+ Examples of real world cookies that really work:
+ Set-Cookie: MSNADS=UM=; domain=.slate.com;
+ expires=Tue, 26-Apr-2022 19:00:00 GMT; path=/
+ Set-Cookie: MC1=V=3&ID=b5bc08af2b8a43ff85fcb5efd8b238f0;
+ domain=.slate.com; expires=Mon, 04-Oct-2021 19:00:00 GMT; path=/
+ */
+ time_t now;
+ time_t then;
+ struct tm *gt;
+ time(&now);
+ then = now + secondsToLive;
+ gt = gmtime(&then);
+ fprintf(cgiOut,
+ "Set-Cookie: %s=%s; domain=%s; expires=%s, %02d-%s-%04d %02d:%02d:%02d GMT; path=%s\r\n",
+ name, value, domain,
+ days[gt->tm_wday],
+ gt->tm_mday,
+ months[gt->tm_mon],
+ gt->tm_year + 1900,
+ gt->tm_hour,
+ gt->tm_min,
+ gt->tm_sec,
+ path);
+}
+
+void cgiHeaderLocation(char *redirectUrl) {
+ fprintf(cgiOut, "Location: %s\r\n\r\n", redirectUrl);
+}
+
+void cgiHeaderStatus(int status, char *statusMessage) {
+ fprintf(cgiOut, "Status: %d %s\r\n\r\n", status, statusMessage);
+}
+
+void cgiHeaderContentType(char *mimeType) {
+ fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType);
+}
+
+static int cgiWriteString(FILE *out, char *s);
+
+static int cgiWriteInt(FILE *out, int i);
+
+#define CGIC_VERSION "2.0"
+
+cgiEnvironmentResultType cgiWriteEnvironment(char *filename) {
+ FILE *out;
+ cgiFormEntry *e;
+ /* Be sure to open in binary mode */
+ out = fopen(filename, "wb");
+ if (!out) {
+ /* Can't create file */
+ return cgiEnvironmentIO;
+ }
+ if (!cgiWriteString(out, "CGIC2.0")) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiServerSoftware)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiServerName)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiGatewayInterface)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiServerProtocol)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiServerPort)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiRequestMethod)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiPathInfo)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiPathTranslated)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiScriptName)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiQueryString)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiRemoteHost)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiRemoteAddr)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiAuthType)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiRemoteUser)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiRemoteIdent)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiContentType)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiAccept)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiUserAgent)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiReferrer)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, cgiCookie)) {
+ goto error;
+ }
+ if (!cgiWriteInt(out, cgiContentLength)) {
+ goto error;
+ }
+ e = cgiFormEntryFirst;
+ while (e) {
+ cgiFilePtr fp;
+ if (!cgiWriteString(out, e->attr)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, e->value)) {
+ goto error;
+ }
+ /* New 2.0 fields and file uploads */
+ if (!cgiWriteString(out, e->fileName)) {
+ goto error;
+ }
+ if (!cgiWriteString(out, e->contentType)) {
+ goto error;
+ }
+ if (!cgiWriteInt(out, e->valueLength)) {
+ goto error;
+ }
+ if (cgiFormFileOpen(e->attr, &fp) == cgiFormSuccess) {
+ char buffer[1024];
+ int got;
+ if (!cgiWriteInt(out, 1)) {
+ cgiFormFileClose(fp);
+ goto error;
+ }
+ while (cgiFormFileRead(fp, buffer,
+ sizeof(buffer), &got) == cgiFormSuccess)
+ {
+ if (((int) fwrite(buffer, 1, got, out)) != got) {
+ cgiFormFileClose(fp);
+ goto error;
+ }
+ }
+ if (cgiFormFileClose(fp) != cgiFormSuccess) {
+ goto error;
+ }
+ } else {
+ if (!cgiWriteInt(out, 0)) {
+ goto error;
+ }
+ }
+ e = e->next;
+ }
+ fclose(out);
+ return cgiEnvironmentSuccess;
+error:
+ fclose(out);
+ /* If this function is not defined in your system,
+ you must substitute the appropriate
+ file-deletion function. */
+ unlink(filename);
+ return cgiEnvironmentIO;
+}
+
+static int cgiWriteString(FILE *out, char *s) {
+ int len = (int) strlen(s);
+ cgiWriteInt(out, len);
+ if (((int) fwrite(s, 1, len, out)) != len) {
+ return 0;
+ }
+ return 1;
+}
+
+static int cgiWriteInt(FILE *out, int i) {
+ if (!fwrite(&i, sizeof(int), 1, out)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int cgiReadString(FILE *out, char **s);
+
+static int cgiReadInt(FILE *out, int *i);
+
+cgiEnvironmentResultType cgiReadEnvironment(char *filename) {
+ FILE *in;
+ cgiFormEntry *e = 0, *p;
+ char *version;
+ /* Prevent compiler warnings */
+ cgiEnvironmentResultType result = cgiEnvironmentIO;
+ /* Free any existing data first */
+ cgiFreeResources();
+ /* Be sure to open in binary mode */
+ in = fopen(filename, "rb");
+ if (!in) {
+ /* Can't access file */
+ return cgiEnvironmentIO;
+ }
+ if (!cgiReadString(in, &version)) {
+ goto error;
+ }
+ if (strcmp(version, "CGIC" CGIC_VERSION)) {
+ /* 2.02: Merezko Oleg */
+ free(version);
+ return cgiEnvironmentWrongVersion;
+ }
+ /* 2.02: Merezko Oleg */
+ free(version);
+ if (!cgiReadString(in, &cgiServerSoftware)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiServerName)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiGatewayInterface)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiServerProtocol)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiServerPort)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiRequestMethod)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiPathInfo)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiPathTranslated)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiScriptName)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiQueryString)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiRemoteHost)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiRemoteAddr)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiAuthType)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiRemoteUser)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiRemoteIdent)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiContentType)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiAccept)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiUserAgent)) {
+ goto error;
+ }
+ if (!cgiReadString(in, &cgiReferrer)) {
+ goto error;
+ }
+ /* 2.0 */
+ if (!cgiReadString(in, &cgiCookie)) {
+ goto error;
+ }
+ if (!cgiReadInt(in, &cgiContentLength)) {
+ goto error;
+ }
+ p = 0;
+ while (1) {
+ int fileFlag;
+ e = (cgiFormEntry *) calloc(1, sizeof(cgiFormEntry));
+ if (!e) {
+ cgiFreeResources();
+ fclose(in);
+ return cgiEnvironmentMemory;
+ }
+ memset(e, 0, sizeof(cgiFormEntry));
+ if (!cgiReadString(in, &e->attr)) {
+ /* This means we've reached the end of the list. */
+ /* 2.02: thanks to Merezko Oleg */
+ free(e);
+ break;
+ }
+ if (!cgiReadString(in, &e->value)) {
+ goto outOfMemory;
+ }
+ if (!cgiReadString(in, &e->fileName)) {
+ goto outOfMemory;
+ }
+ if (!cgiReadString(in, &e->contentType)) {
+ goto outOfMemory;
+ }
+ if (!cgiReadInt(in, &e->valueLength)) {
+ goto outOfMemory;
+ }
+ if (!cgiReadInt(in, &fileFlag)) {
+ goto outOfMemory;
+ }
+ if (fileFlag) {
+ char buffer[1024];
+ FILE *out;
+ char tfileName[1024];
+ int got;
+ int len = e->valueLength;
+ if (getTempFileName(tfileName)
+ != cgiParseSuccess)
+ {
+ result = cgiEnvironmentIO;
+ goto error;
+ }
+ out = fopen(tfileName, "w+b");
+ if (!out) {
+ result = cgiEnvironmentIO;
+ goto error;
+ }
+ while (len > 0) {
+ /* 2.01: try is a bad variable name in
+ C++, and it wasn't being used
+ properly either */
+ int tryr = len;
+ if (tryr > ((int) sizeof(buffer))) {
+ tryr = sizeof(buffer);
+ }
+ got = fread(buffer, 1, tryr, in);
+ if (got <= 0) {
+ result = cgiEnvironmentIO;
+ fclose(out);
+ unlink(tfileName);
+ goto error;
+ }
+ if (((int) fwrite(buffer, 1, got, out)) != got) {
+ result = cgiEnvironmentIO;
+ fclose(out);
+ unlink(tfileName);
+ goto error;
+ }
+ len -= got;
+ }
+ /* cgic 2.05: should be fclose not rewind */
+ fclose(out);
+ e->tfileName = (char *) malloc((int) strlen(tfileName) + 1);
+ if (!e->tfileName) {
+ result = cgiEnvironmentMemory;
+ unlink(tfileName);
+ goto error;
+ }
+ strcpy(e->tfileName, tfileName);
+ } else {
+ e->tfileName = (char *) malloc(1);
+ if (!e->tfileName) {
+ result = cgiEnvironmentMemory;
+ goto error;
+ }
+ }
+ e->next = 0;
+ if (p) {
+ p->next = e;
+ } else {
+ cgiFormEntryFirst = e;
+ }
+ p = e;
+ }
+ fclose(in);
+ cgiRestored = 1;
+ return cgiEnvironmentSuccess;
+outOfMemory:
+ result = cgiEnvironmentMemory;
+error:
+ cgiFreeResources();
+ fclose(in);
+ if (e) {
+ if (e->attr) {
+ free(e->attr);
+ }
+ if (e->value) {
+ free(e->value);
+ }
+ if (e->fileName) {
+ free(e->fileName);
+ }
+ if (e->contentType) {
+ free(e->contentType);
+ }
+ if (e->tfileName) {
+ free(e->tfileName);
+ }
+ free(e);
+ }
+ return result;
+}
+
+static int cgiReadString(FILE *in, char **s) {
+ int len;
+ /* 2.0 fix: test cgiReadInt for failure! */
+ if (!cgiReadInt(in, &len)) {
+ return 0;
+ }
+ *s = (char *) malloc(len + 1);
+ if (!(*s)) {
+ return 0;
+ }
+ if (((int) fread(*s, 1, len, in)) != len) {
+ return 0;
+ }
+ (*s)[len] = '\0';
+ return 1;
+}
+
+static int cgiReadInt(FILE *out, int *i) {
+ if (!fread(i, sizeof(int), 1, out)) {
+ return 0;
+ }
+ return 1;
+}
+
+static int cgiStrEqNc(char *s1, char *s2) {
+ while(1) {
+ if (!(*s1)) {
+ if (!(*s2)) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else if (!(*s2)) {
+ return 0;
+ }
+ if (isalpha((int)*s1)) {
+ if (tolower(*s1) != tolower(*s2)) {
+ return 0;
+ }
+ } else if ((*s1) != (*s2)) {
+ return 0;
+ }
+ s1++;
+ s2++;
+ }
+}
+
+static int cgiStrBeginsNc(char *s1, char *s2) {
+ while(1) {
+ if (!(*s2)) {
+ return 1;
+ } else if (!(*s1)) {
+ return 0;
+ }
+ if (isalpha((int)*s1)) {
+ if (tolower(*s1) != tolower(*s2)) {
+ return 0;
+ }
+ } else if ((*s1) != (*s2)) {
+ return 0;
+ }
+ s1++;
+ s2++;
+ }
+}
+
+static char *cgiFindTarget = 0;
+static cgiFormEntry *cgiFindPos = 0;
+
+static cgiFormEntry *cgiFormEntryFindFirst(char *name) {
+ cgiFindTarget = name;
+ cgiFindPos = cgiFormEntryFirst;
+ return cgiFormEntryFindNext();
+}
+
+static cgiFormEntry *cgiFormEntryFindNext() {
+ while (cgiFindPos) {
+ cgiFormEntry *c = cgiFindPos;
+ cgiFindPos = c->next;
+ if (!strcmp(c -> attr, cgiFindTarget)) {
+ return c;
+ }
+ }
+ return 0;
+}
+
+static int cgiFirstNonspaceChar(char *s) {
+ int len = strspn(s, " \n\r\t");
+ return s[len];
+}
+
+void cgiStringArrayFree(char **stringArray) {
+ char *p;
+ char **arrayItself = stringArray;
+ p = *stringArray;
+ while (p) {
+ free(p);
+ stringArray++;
+ p = *stringArray;
+ }
+ /* 2.0: free the array itself! */
+ free(arrayItself);
+}
+
+cgiFormResultType cgiCookies(char ***result) {
+ char **stringArray;
+ int i;
+ int total = 0;
+ char *p;
+ char *n;
+ p = cgiCookie;
+ while (*p) {
+ if (*p == '=') {
+ total++;
+ }
+ p++;
+ }
+ stringArray = (char **) malloc(sizeof(char *) * (total + 1));
+ if (!stringArray) {
+ *result = 0;
+ return cgiFormMemory;
+ }
+ /* initialize all entries to null; the last will stay that way */
+ for (i=0; (i <= total); i++) {
+ stringArray[i] = 0;
+ }
+ i = 0;
+ p = cgiCookie;
+ while (*p) {
+ while (*p && isspace((int)*p)) {
+ p++;
+ }
+ n = p;
+ while (*p && (*p != '=')) {
+ p++;
+ }
+ if (p != n) {
+ stringArray[i] = (char *) malloc((p - n) + 1);
+ if (!stringArray[i]) {
+ cgiStringArrayFree(stringArray);
+ *result = 0;
+ return cgiFormMemory;
+ }
+ memcpy(stringArray[i], n, p - n);
+ stringArray[i][p - n] = '\0';
+ i++;
+ }
+ while (*p && (*p != ';')) {
+ p++;
+ }
+ if (!*p) {
+ break;
+ }
+ if (*p == ';') {
+ p++;
+ }
+ }
+ *result = stringArray;
+ return cgiFormSuccess;
+}
+
+cgiFormResultType cgiFormEntries(char ***result) {
+ char **stringArray;
+ cgiFormEntry *e, *pe;
+ int i;
+ int total = 0;
+ e = cgiFormEntryFirst;
+ while (e) {
+ /* Don't count a field name more than once if
+ multiple values happen to be present for it */
+ pe = cgiFormEntryFirst;
+ while (pe != e) {
+ if (!strcmp(e->attr, pe->attr)) {
+ goto skipSecondValue;
+ }
+ pe = pe->next;
+ }
+ total++;
+skipSecondValue:
+ e = e->next;
+ }
+ stringArray = (char **) malloc(sizeof(char *) * (total + 1));
+ if (!stringArray) {
+ *result = 0;
+ return cgiFormMemory;
+ }
+ /* initialize all entries to null; the last will stay that way */
+ for (i=0; (i <= total); i++) {
+ stringArray[i] = 0;
+ }
+ /* Now go get the entries */
+ e = cgiFormEntryFirst;
+ i = 0;
+ while (e) {
+ int space;
+ /* Don't return a field name more than once if
+ multiple values happen to be present for it */
+ pe = cgiFormEntryFirst;
+ while (pe != e) {
+ if (!strcmp(e->attr, pe->attr)) {
+ goto skipSecondValue2;
+ }
+ pe = pe->next;
+ }
+ space = (int) strlen(e->attr) + 1;
+ stringArray[i] = (char *) malloc(space);
+ if (stringArray[i] == 0) {
+ /* Memory problems */
+ cgiStringArrayFree(stringArray);
+ *result = 0;
+ return cgiFormMemory;
+ }
+ strcpy(stringArray[i], e->attr);
+ i++;
+skipSecondValue2:
+ e = e->next;
+ }
+ *result = stringArray;
+ return cgiFormSuccess;
+}
+
+#define TRYPUTC(ch) \
+ { \
+ if (putc((ch), cgiOut) == EOF) { \
+ return cgiFormIO; \
+ } \
+ }
+
+cgiFormResultType cgiHtmlEscapeData(char *data, int len)
+{
+ while (len--) {
+ if (*data == '<') {
+ TRYPUTC('&');
+ TRYPUTC('l');
+ TRYPUTC('t');
+ TRYPUTC(';');
+ } else if (*data == '&') {
+ TRYPUTC('&');
+ TRYPUTC('a');
+ TRYPUTC('m');
+ TRYPUTC('p');
+ TRYPUTC(';');
+ } else if (*data == '>') {
+ TRYPUTC('&');
+ TRYPUTC('g');
+ TRYPUTC('t');
+ TRYPUTC(';');
+ } else {
+ TRYPUTC(*data);
+ }
+ data++;
+ }
+ return cgiFormSuccess;
+}
+
+cgiFormResultType cgiHtmlEscape(char *s)
+{
+ return cgiHtmlEscapeData(s, (int) strlen(s));
+}
+
+/* Output data with the " character HTML-escaped, and no
+ other characters escaped. This is useful when outputting
+ the contents of a tag attribute such as 'href' or 'src'.
+ 'data' is not null-terminated; 'len' is the number of
+ bytes in 'data'. Returns cgiFormIO in the event
+ of error, cgiFormSuccess otherwise. */
+cgiFormResultType cgiValueEscapeData(char *data, int len)
+{
+ while (len--) {
+ if (*data == '\"') {
+ TRYPUTC('&');
+ TRYPUTC('#');
+ TRYPUTC('3');
+ TRYPUTC('4');
+ TRYPUTC(';');
+ } else {
+ TRYPUTC(*data);
+ }
+ data++;
+ }
+ return cgiFormSuccess;
+}
+
+cgiFormResultType cgiValueEscape(char *s)
+{
+ return cgiValueEscapeData(s, (int) strlen(s));
+}
+
+