1 /* cgicTempDir is the only setting you are likely to need
2 to change in this file. */
4 /* Used only in Unix environments, in conjunction with mkstemp().
5 Elsewhere (Windows), temporary files go where the tmpnam()
6 function suggests. If this behavior does not work for you,
7 modify the getTempFileName() function to suit your needs. */
9 #define cgicTempDir "/tmp"
12 #define CGICDEBUGSTART \
15 dout = fopen("/home/boutell/public_html/debug", "a"); \
17 #define CGICDEBUGEND \
21 #define CGICDEBUGSTART
23 #endif /* CGICDEBUG */
30 #include <sys/types.h>
44 #define cgiStrEq(a, b) (!strcmp((a), (b)))
46 char *cgiServerSoftware;
48 char *cgiGatewayInterface;
49 char *cgiServerProtocol;
51 char *cgiRequestMethod;
53 char *cgiPathTranslated;
61 char cgiContentTypeData[1024];
62 char *cgiContentType = cgiContentTypeData;
63 char *cgiMultipartBoundary;
73 /* True if CGI environment was restored from a file. */
74 static int cgiRestored = 0;
76 static void cgiGetenv(char **s, char *var);
84 /* One form entry, consisting of an attribute-value pair,
85 and an optional filename and content type. All of
86 these are guaranteed to be valid null-terminated strings,
87 which will be of length zero in the event that the
88 field is not present, with the exception of tfileName
89 which will be null when 'in' is null. DO NOT MODIFY THESE
90 VALUES. Make local copies if modifications are desired. */
92 typedef struct cgiFormEntryStruct {
94 /* value is populated for regular form fields only.
95 For file uploads, it points to an empty string, and file
96 upload data should be read from the file tfileName. */
98 /* When fileName is not an empty string, tfileName is not null,
99 and 'value' points to an empty string. */
100 /* Valid for both files and regular fields; does not include
101 terminating null of regular fields. */
105 /* Temporary file name for working storage of file uploads. */
107 struct cgiFormEntryStruct *next;
110 /* The first form entry. */
111 static cgiFormEntry *cgiFormEntryFirst;
113 static cgiParseResultType cgiParseGetFormInput();
114 static cgiParseResultType cgiParsePostFormInput();
115 static cgiParseResultType cgiParsePostMultipartInput();
116 static cgiParseResultType cgiParseFormInput(char *data, int length);
117 static void cgiSetupConstants();
118 static void cgiFreeResources();
119 static int cgiStrEqNc(char *s1, char *s2);
120 static int cgiStrBeginsNc(char *s1, char *s2);
122 int main(int argc, char *argv[]) {
124 char *cgiContentLengthString;
127 cgiGetenv(&cgiServerSoftware, "SERVER_SOFTWARE");
128 cgiGetenv(&cgiServerName, "SERVER_NAME");
129 cgiGetenv(&cgiGatewayInterface, "GATEWAY_INTERFACE");
130 cgiGetenv(&cgiServerProtocol, "SERVER_PROTOCOL");
131 cgiGetenv(&cgiServerPort, "SERVER_PORT");
132 cgiGetenv(&cgiRequestMethod, "REQUEST_METHOD");
133 cgiGetenv(&cgiPathInfo, "PATH_INFO");
134 cgiGetenv(&cgiPathTranslated, "PATH_TRANSLATED");
135 cgiGetenv(&cgiScriptName, "SCRIPT_NAME");
136 cgiGetenv(&cgiQueryString, "QUERY_STRING");
137 cgiGetenv(&cgiRemoteHost, "REMOTE_HOST");
138 cgiGetenv(&cgiRemoteAddr, "REMOTE_ADDR");
139 cgiGetenv(&cgiAuthType, "AUTH_TYPE");
140 cgiGetenv(&cgiRemoteUser, "REMOTE_USER");
141 cgiGetenv(&cgiRemoteIdent, "REMOTE_IDENT");
142 /* 2.0: the content type string needs to be parsed and modified, so
143 copy it to a buffer. */
144 e = getenv("CONTENT_TYPE");
146 if (strlen(e) < sizeof(cgiContentTypeData)) {
147 strcpy(cgiContentType, e);
149 /* Truncate safely in the event of what is almost certainly
151 strncpy(cgiContentType, e, sizeof(cgiContentTypeData));
152 cgiContentType[sizeof(cgiContentTypeData) - 1] = '\0';
155 cgiContentType[0] = '\0';
158 cgiMultipartBoundary = "";
159 /* 2.0: parse semicolon-separated additional parameters of the
160 content type. The one we're interested in is 'boundary'.
161 We discard the rest to make cgiContentType more useful
162 to the typical programmer. */
163 if (strchr(cgiContentType, ';')) {
164 char *sat = strchr(cgiContentType, ';');
168 while (isspace(*sat)) {
171 if (cgiStrBeginsNc(sat, "boundary=")) {
173 cgiMultipartBoundary = sat + strlen("boundary=");
174 s = cgiMultipartBoundary;
175 while ((*s) && (!isspace(*s))) {
181 sat = strchr(sat, ';');
185 cgiGetenv(&cgiContentLengthString, "CONTENT_LENGTH");
186 cgiContentLength = atoi(cgiContentLengthString);
187 cgiGetenv(&cgiAccept, "HTTP_ACCEPT");
188 cgiGetenv(&cgiUserAgent, "HTTP_USER_AGENT");
189 cgiGetenv(&cgiReferrer, "HTTP_REFERER");
190 cgiGetenv(&cgiCookie, "HTTP_COOKIE");
193 fprintf(dout, "%d\n", cgiContentLength);
194 fprintf(dout, "%s\n", cgiRequestMethod);
195 fprintf(dout, "%s\n", cgiContentType);
197 #endif /* CGICDEBUG */
199 /* 1.07: Must set stdin and stdout to binary mode */
200 /* 2.0: this is particularly crucial now and must not be removed */
201 _setmode( _fileno( stdin ), _O_BINARY );
202 _setmode( _fileno( stdout ), _O_BINARY );
204 cgiFormEntryFirst = 0;
210 /* These five lines keep compilers from
211 producing warnings that argc and argv
212 are unused. They have no actual function. */
220 if (cgiStrEqNc(cgiRequestMethod, "post")) {
223 fprintf(dout, "POST recognized\n");
225 #endif /* CGICDEBUG */
226 if (cgiStrEqNc(cgiContentType, "application/x-www-form-urlencoded")) {
229 fprintf(dout, "Calling PostFormInput\n");
231 #endif /* CGICDEBUG */
232 if (cgiParsePostFormInput() != cgiParseSuccess) {
235 fprintf(dout, "PostFormInput failed\n");
237 #endif /* CGICDEBUG */
243 fprintf(dout, "PostFormInput succeeded\n");
245 #endif /* CGICDEBUG */
246 } else if (cgiStrEqNc(cgiContentType, "multipart/form-data")) {
249 fprintf(dout, "Calling PostMultipartInput\n");
251 #endif /* CGICDEBUG */
252 if (cgiParsePostMultipartInput() != cgiParseSuccess) {
255 fprintf(dout, "PostMultipartInput failed\n");
257 #endif /* CGICDEBUG */
263 fprintf(dout, "PostMultipartInput succeeded\n");
265 #endif /* CGICDEBUG */
267 } else if (cgiStrEqNc(cgiRequestMethod, "get")) {
268 /* The spec says this should be taken care of by
269 the server, but... it isn't */
270 cgiContentLength = strlen(cgiQueryString);
271 if (cgiParseGetFormInput() != cgiParseSuccess) {
274 fprintf(dout, "GetFormInput failed\n");
276 #endif /* CGICDEBUG */
282 fprintf(dout, "GetFormInput succeeded\n");
284 #endif /* CGICDEBUG */
292 static void cgiGetenv(char **s, char *var){
299 static cgiParseResultType cgiParsePostFormInput() {
301 cgiParseResultType result;
302 if (!cgiContentLength) {
303 return cgiParseSuccess;
305 input = (char *) malloc(cgiContentLength);
307 return cgiParseMemory;
309 if (((int) fread(input, 1, cgiContentLength, cgiIn))
314 result = cgiParseFormInput(input, cgiContentLength);
319 /* 2.0: A virtual datastream supporting putback of
320 enough characters to handle multipart boundaries easily.
321 A simple memset(&mp, 0, sizeof(mp)) is suitable initialization. */
324 /* Buffer for putting characters back */
326 /* Position in putback from which next character will be read.
327 If readPos == writePos, then next character should
330 /* Position in putback to which next character will be put back.
331 If writePos catches up to readPos, as opposed to the other
332 way around, the stream no longer functions properly.
333 Calling code must guarantee that no more than
334 sizeof(putback) bytes are put back at any given time. */
336 /* Offset in the virtual datastream; can be compared
337 to cgiContentLength */
339 } mpStream, *mpStreamPtr;
341 int mpRead(mpStreamPtr mpp, char *buffer, int len)
346 if (mpp->readPos != mpp->writePos) {
347 *buffer++ = mpp->putback[mpp->readPos++];
348 mpp->readPos %= sizeof(mpp->putback);
355 /* Refuse to read past the declared length in order to
357 if (len > (cgiContentLength - mpp->offset)) {
358 len = cgiContentLength - mpp->offset;
361 int fgot = fread(buffer, 1, len, cgiIn);
363 mpp->offset += (got + fgot);
365 } else if (got > 0) {
382 void mpPutBack(mpStreamPtr mpp, char *data, int len)
386 mpp->putback[mpp->writePos++] = *data++;
387 mpp->writePos %= sizeof(mpp->putback);
392 /* This function copies the body to outf if it is not null, otherwise to
393 a newly allocated character buffer at *outP, which will be null
394 terminated; if both outf and outP are null the body is not stored.
395 If bodyLengthP is not null, the size of the body in bytes is stored
396 to *bodyLengthP, not including any terminating null added to *outP.
397 If 'first' is nonzero, a preceding newline is not expected before
398 the boundary. If 'first' is zero, a preceding newline is expected.
399 Upon return mpp is positioned after the boundary and its trailing
400 newline, if any; if the boundary is followed by -- the next two
401 characters read after this function returns will be --. Upon error,
402 if outP is not null, *outP is a null pointer; *bodyLengthP
403 is set to zero. Returns cgiParseSuccess, cgiParseMemory
406 static cgiParseResultType afterNextBoundary(mpStreamPtr mpp,
413 static int readHeaderLine(
420 static void decomposeValue(char *value,
421 char *mvalue, int mvalueSpace,
426 /* tfileName must be 1024 bytes to ensure adequacy on
427 win32 (1024 exceeds the maximum path length and
428 certainly exceeds observed behavior of _tmpnam).
429 May as well also be 1024 bytes on Unix, although actual
430 length is strlen(cgiTempDir) + a short unique pattern. */
432 static cgiParseResultType getTempFileName(char *tfileName);
434 static cgiParseResultType cgiParsePostMultipartInput() {
435 cgiParseResultType result;
436 cgiFormEntry *n = 0, *l = 0;
440 char tfileName[1024];
442 mpStreamPtr mpp = ∓
443 memset(&mp, 0, sizeof(mp));
444 if (!cgiContentLength) {
445 return cgiParseSuccess;
447 /* Read first boundary, including trailing newline */
448 result = afterNextBoundary(mpp, 0, 0, 0, 1);
449 if (result == cgiParseIO) {
450 /* An empty submission is not necessarily an error */
451 return cgiParseSuccess;
452 } else if (result != cgiParseSuccess) {
460 char ffileName[1024];
461 char fcontentType[1024];
471 got = mpRead(mpp, d, 2);
476 if ((d[0] == '-') && (d[1] == '-')) {
480 mpPutBack(mpp, d, 2);
481 /* Read header lines until end of header */
482 while (readHeaderLine(
483 mpp, attr, sizeof(attr), value, sizeof(value)))
487 /* Content-Disposition: form-data;
488 name="test"; filename="googley.gif" */
489 if (cgiStrEqNc(attr, "Content-Disposition")) {
490 argNames[0] = "name";
491 argNames[1] = "filename";
493 argValues[0] = fname;
494 argValues[1] = ffileName;
495 decomposeValue(value,
496 fvalue, sizeof(fvalue),
500 } else if (cgiStrEqNc(attr, "Content-Type")) {
502 decomposeValue(value,
503 fcontentType, sizeof(fcontentType),
509 if (!cgiStrEqNc(fvalue, "form-data")) {
513 /* Body is everything from here until the next
514 boundary. So, set it aside and move past boundary.
515 If a filename was submitted as part of the
516 disposition header, store to a temporary file.
517 Otherwise, store to a memory buffer (it is
518 presumably a regular form field). */
519 if (strlen(ffileName)) {
520 if (getTempFileName(tfileName) != cgiParseSuccess) {
523 outf = fopen(tfileName, "w+b");
528 result = afterNextBoundary(mpp, outf, &out, &bodyLength, 0);
529 if (result != cgiParseSuccess) {
530 /* Lack of a boundary here is an error. */
540 /* OK, we have a new pair, add it to the list. */
541 n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));
545 memset(n, 0, sizeof(cgiFormEntry));
546 /* 2.01: one of numerous new casts required
547 to please C++ compilers */
548 n->attr = (char *) malloc(strlen(fname) + 1);
552 strcpy(n->attr, fname);
557 n->value = (char *) malloc(1);
564 n->valueLength = bodyLength;
567 cgiFormEntryFirst = n;
571 n->fileName = (char *) malloc(strlen(ffileName) + 1);
575 strcpy(n->fileName, ffileName);
576 n->contentType = (char *) malloc(strlen(fcontentType) + 1);
577 if (!n->contentType) {
580 strcpy(n->contentType, fcontentType);
581 n->tfileName = (char *) malloc(strlen(tfileName) + 1);
585 strcpy(n->tfileName, tfileName);
589 return cgiParseSuccess;
604 if (n->contentType) {
605 free(n->contentType);
616 return cgiParseMemory;
619 static cgiParseResultType getTempFileName(char *tfileName)
622 /* Unix. Use the robust 'mkstemp' function to create
623 a temporary file that is truly unique, with
624 permissions that are truly safe. The
625 fopen-for-write destroys any bogus information
626 written by potential hackers during the brief
627 window between the file's creation and the
628 chmod call (glibc 2.0.6 and lower might
629 otherwise have allowed this). */
631 strcpy(tfileName, cgicTempDir "/cgicXXXXXX");
632 outfd = mkstemp(tfileName);
637 /* Fix the permissions */
638 if (chmod(tfileName, 0600) != 0) {
643 /* Non-Unix. Do what we can. */
644 if (!tmpnam(tfileName)) {
648 return cgiParseSuccess;
652 #define APPEND(string, char) \
654 if ((string##Len + 1) < string##Space) { \
655 string[string##Len++] = (char); \
659 #define RAPPEND(string, ch) \
661 if ((string##Len + 1) == string##Space) { \
662 char *sold = string; \
663 string##Space *= 2; \
664 string = (char *) realloc(string, string##Space); \
670 string[string##Len++] = (ch); \
673 #define BAPPEND(ch) \
683 cgiParseResultType afterNextBoundary(mpStreamPtr mpp, FILE *outf, char **outP,
684 int *bodyLengthP, int first)
689 cgiParseResultType result;
693 /* This is large enough, because the buffer into which the
694 original boundary string is fetched is shorter by more
695 than four characters due to the space required for
696 the attribute name */
697 char workingBoundaryData[1024];
698 char *workingBoundary = workingBoundaryData;
699 int workingBoundaryLength;
700 if ((!outf) && (outP)) {
701 out = (char *) malloc(outSpace);
707 sprintf(workingBoundaryData, "\r\n--%s", cgiMultipartBoundary);
709 workingBoundary = workingBoundaryData + 2;
711 workingBoundaryLength = strlen(workingBoundary);
713 got = mpRead(mpp, d, 1);
715 /* 2.01: cgiParseIO, not cgiFormIO */
719 if (d[0] == workingBoundary[boffset]) {
720 /* We matched the next byte of the boundary.
721 Keep track of our progress into the
722 boundary and don't emit anything. */
724 if (boffset == workingBoundaryLength) {
727 } else if (boffset > 0) {
728 /* We matched part, but not all, of the
729 boundary. Now we have to be careful:
730 put back all except the first
731 character and try again. The
732 real boundary could begin in the
733 middle of a false match. We can
734 emit the first character only so far. */
735 BAPPEND(workingBoundary[0]);
737 workingBoundary + 1, boffset - 1);
738 mpPutBack(mpp, d, 1);
741 /* Not presently in the middle of a boundary
742 match; just emit the character. */
746 /* Read trailing newline or -- EOF marker. A literal EOF here
747 would be an error in the input stream. */
748 got = mpRead(mpp, d, 2);
753 if ((d[0] == '\r') && (d[1] == '\n')) {
755 } else if (d[0] == '-') {
756 /* Probably EOF, but we check for
758 mpPutBack(mpp, d, 2);
760 if (out && outSpace) {
763 out = (char *) realloc(out, outLen + 1);
765 /* Surprising if it happens; and not fatal! We were
766 just trying to give some space back. We can
767 keep it if we have to. */
773 *bodyLengthP = outLen;
775 return cgiParseSuccess;
777 result = cgiParseMemory;
797 static void decomposeValue(char *value,
798 char *mvalue, int mvalueSpace,
804 int argNameSpace = sizeof(argName);
809 while (argNames[argNum]) {
811 argValues[argNum][0] = '\0';
815 while (isspace(*value)) {
819 if (*value == '\"') {
821 while ((*value) && (*value != '\"')) {
822 APPEND(mvalue, *value);
825 while ((*value) && (*value != ';')) {
829 /* Unquoted mvalue */
830 while ((*value) && (*value != ';')) {
831 APPEND(mvalue, *value);
836 mvalue[mvalueLen] = '\0';
838 while (*value == ';') {
841 /* Skip the ; between parameters */
843 /* Now skip leading whitespace */
844 while ((*value) && (isspace(*value))) {
847 /* Now read the parameter name */
849 while ((*value) && (isalnum(*value))) {
850 APPEND(argName, *value);
854 argName[argNameLen] = '\0';
856 while ((*value) && isspace(*value)) {
864 while ((*value) && isspace(*value)) {
867 /* Find the parameter in the argument list, if present */
870 while (argNames[argNum]) {
871 if (cgiStrEqNc(argName, argNames[argNum])) {
872 argValue = argValues[argNum];
877 /* Finally, read the parameter value */
878 if (*value == '\"') {
880 while ((*value) && (*value != '\"')) {
882 APPEND(argValue, *value);
886 while ((*value) && (*value != ';')) {
891 while ((*value) && (*value != ';')) {
892 if (argNames[argNum]) {
893 APPEND(argValue, *value);
899 argValue[argValueLen] = '\0';
904 static int readHeaderLine(
916 int got = mpRead(mpp, d, 1);
921 got = mpRead(mpp, d, 1);
926 mpPutBack(mpp, d, 1);
930 } else if (d[0] == '\n') {
932 } else if ((d[0] == ':') && attrLen) {
934 while (mpRead(mpp, d, 1) == 1) {
935 if (!isspace(d[0])) {
936 mpPutBack(mpp, d, 1);
940 } else if (!valueFound) {
942 if (attrLen < (attrSpace - 1)) {
943 attr[attrLen++] = *d;
946 } else if (valueFound) {
947 if (valueLen < (valueSpace - 1)) {
948 value[valueLen++] = *d;
953 attr[attrLen] = '\0';
956 value[valueLen] = '\0';
958 if (attrLen && valueLen) {
965 static cgiParseResultType cgiParseGetFormInput() {
966 return cgiParseFormInput(cgiQueryString, cgiContentLength);
978 } cgiUnescapeResultType;
980 static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
982 static cgiParseResultType cgiParseFormInput(char *data, int length) {
983 /* Scan for pairs, unescaping and storing them as they are found. */
987 while (pos != length) {
994 while (pos != length) {
995 if (data[pos] == '=') {
1006 if (cgiUnescapeChars(&attr, data+start, len)
1007 != cgiUnescapeSuccess) {
1008 return cgiParseMemory;
1012 while (pos != length) {
1013 if (data[pos] == '&') {
1021 /* The last pair probably won't be followed by a &, but
1022 that's fine, so check for that after accepting it */
1023 if (cgiUnescapeChars(&value, data+start, len)
1024 != cgiUnescapeSuccess) {
1026 return cgiParseMemory;
1028 /* OK, we have a new pair, add it to the list. */
1029 n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));
1033 return cgiParseMemory;
1037 n->valueLength = strlen(n->value);
1038 n->fileName = (char *) malloc(1);
1043 return cgiParseMemory;
1045 n->fileName[0] = '\0';
1046 n->contentType = (char *) malloc(1);
1047 if (!n->contentType) {
1052 return cgiParseMemory;
1054 n->contentType[0] = '\0';
1055 n->tfileName = (char *) malloc(1);
1056 if (!n->tfileName) {
1060 free(n->contentType);
1062 return cgiParseMemory;
1064 n->tfileName[0] = '\0';
1067 cgiFormEntryFirst = n;
1076 return cgiParseSuccess;
1079 static int cgiHexValue[256];
1081 cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len) {
1083 cgiEscapeState escapeState = cgiEscapeRest;
1084 int escapedValue = 0;
1087 s = (char *) malloc(len + 1);
1089 return cgiUnescapeMemory;
1091 while (srcPos < len) {
1092 int ch = cp[srcPos];
1093 switch (escapeState) {
1096 escapeState = cgiEscapeFirst;
1097 } else if (ch == '+') {
1103 case cgiEscapeFirst:
1104 escapedValue = cgiHexValue[ch] << 4;
1105 escapeState = cgiEscapeSecond;
1107 case cgiEscapeSecond:
1108 escapedValue += cgiHexValue[ch];
1109 s[dstPos++] = escapedValue;
1110 escapeState = cgiEscapeRest;
1117 return cgiUnescapeSuccess;
1120 static void cgiSetupConstants() {
1122 for (i=0; (i < 256); i++) {
1125 cgiHexValue['0'] = 0;
1126 cgiHexValue['1'] = 1;
1127 cgiHexValue['2'] = 2;
1128 cgiHexValue['3'] = 3;
1129 cgiHexValue['4'] = 4;
1130 cgiHexValue['5'] = 5;
1131 cgiHexValue['6'] = 6;
1132 cgiHexValue['7'] = 7;
1133 cgiHexValue['8'] = 8;
1134 cgiHexValue['9'] = 9;
1135 cgiHexValue['A'] = 10;
1136 cgiHexValue['B'] = 11;
1137 cgiHexValue['C'] = 12;
1138 cgiHexValue['D'] = 13;
1139 cgiHexValue['E'] = 14;
1140 cgiHexValue['F'] = 15;
1141 cgiHexValue['a'] = 10;
1142 cgiHexValue['b'] = 11;
1143 cgiHexValue['c'] = 12;
1144 cgiHexValue['d'] = 13;
1145 cgiHexValue['e'] = 14;
1146 cgiHexValue['f'] = 15;
1149 static void cgiFreeResources() {
1150 cgiFormEntry *c = cgiFormEntryFirst;
1157 free(c->contentType);
1158 if (strlen(c->tfileName)) {
1159 unlink(c->tfileName);
1165 /* If the cgi environment was restored from a saved environment,
1166 then these are in allocated space and must also be freed */
1168 free(cgiServerSoftware);
1169 free(cgiServerName);
1170 free(cgiGatewayInterface);
1171 free(cgiServerProtocol);
1172 free(cgiServerPort);
1173 free(cgiRequestMethod);
1175 free(cgiPathTranslated);
1176 free(cgiScriptName);
1177 free(cgiQueryString);
1178 free(cgiRemoteHost);
1179 free(cgiRemoteAddr);
1181 free(cgiRemoteUser);
1182 free(cgiRemoteIdent);
1183 free(cgiContentType);
1188 /* 2.0: to clean up the environment for cgiReadEnvironment,
1189 we must set these correctly */
1190 cgiFormEntryFirst = 0;
1194 static cgiFormResultType cgiFormEntryString(
1195 cgiFormEntry *e, char *result, int max, int newlines);
1197 static cgiFormEntry *cgiFormEntryFindFirst(char *name);
1198 static cgiFormEntry *cgiFormEntryFindNext();
1200 cgiFormResultType cgiFormString(
1201 char *name, char *result, int max) {
1203 e = cgiFormEntryFindFirst(name);
1206 return cgiFormNotFound;
1208 return cgiFormEntryString(e, result, max, 1);
1211 cgiFormResultType cgiFormFileName(
1212 char *name, char *result, int resultSpace)
1217 e = cgiFormEntryFindFirst(name);
1220 return cgiFormNotFound;
1228 result[resultLen] = '\0';
1230 if (!strlen(e->fileName)) {
1231 return cgiFormNoFileName;
1232 } else if (((int) strlen(e->fileName)) > (resultSpace - 1)) {
1233 return cgiFormTruncated;
1235 return cgiFormSuccess;
1239 cgiFormResultType cgiFormFileContentType(
1240 char *name, char *result, int resultSpace)
1245 e = cgiFormEntryFindFirst(name);
1250 return cgiFormNotFound;
1258 result[resultLen] = '\0';
1260 if (!strlen(e->contentType)) {
1261 return cgiFormNoContentType;
1262 } else if (((int) strlen(e->contentType)) > (resultSpace - 1)) {
1263 return cgiFormTruncated;
1265 return cgiFormSuccess;
1269 cgiFormResultType cgiFormFileSize(
1270 char *name, int *sizeP)
1273 e = cgiFormEntryFindFirst(name);
1278 return cgiFormNotFound;
1279 } else if (!strlen(e->tfileName)) {
1283 return cgiFormNotAFile;
1286 *sizeP = e->valueLength;
1288 return cgiFormSuccess;
1292 typedef struct cgiFileStruct {
1296 cgiFormResultType cgiFormFileOpen(
1297 char *name, cgiFilePtr *cfpp)
1301 e = cgiFormEntryFindFirst(name);
1304 return cgiFormNotFound;
1306 if (!strlen(e->tfileName)) {
1308 return cgiFormNotAFile;
1310 cfp = (cgiFilePtr) malloc(sizeof(cgiFile));
1313 return cgiFormMemory;
1315 cfp->in = fopen(e->tfileName, "rb");
1321 return cgiFormSuccess;
1324 cgiFormResultType cgiFormFileRead(
1325 cgiFilePtr cfp, char *buffer,
1326 int bufferSize, int *gotP)
1330 return cgiFormOpenFailed;
1332 got = fread(buffer, 1, bufferSize, cfp->in);
1337 return cgiFormSuccess;
1340 cgiFormResultType cgiFormFileClose(cgiFilePtr cfp)
1343 return cgiFormOpenFailed;
1347 return cgiFormSuccess;
1350 cgiFormResultType cgiFormStringNoNewlines(
1351 char *name, char *result, int max) {
1353 e = cgiFormEntryFindFirst(name);
1356 return cgiFormNotFound;
1358 return cgiFormEntryString(e, result, max, 0);
1361 cgiFormResultType cgiFormStringMultiple(
1362 char *name, char ***result) {
1367 /* Make two passes. One would be more efficient, but this
1368 function is not commonly used. The select menu and
1369 radio box functions are faster. */
1370 e = cgiFormEntryFindFirst(name);
1374 } while ((e = cgiFormEntryFindNext()) != 0);
1376 stringArray = (char **) malloc(sizeof(char *) * (total + 1));
1379 return cgiFormMemory;
1381 /* initialize all entries to null; the last will stay that way */
1382 for (i=0; (i <= total); i++) {
1385 /* Now go get the entries */
1386 e = cgiFormEntryFindFirst(name);
1389 fprintf(dout, "StringMultiple Beginning\n");
1391 #endif /* CGICDEBUG */
1395 int max = (int) (strlen(e->value) + 1);
1396 stringArray[i] = (char *) malloc(max);
1397 if (stringArray[i] == 0) {
1398 /* Memory problems */
1399 cgiStringArrayFree(stringArray);
1401 return cgiFormMemory;
1403 strcpy(stringArray[i], e->value);
1404 cgiFormEntryString(e, stringArray[i], max, 1);
1406 } while ((e = cgiFormEntryFindNext()) != 0);
1407 *result = stringArray;
1410 fprintf(dout, "StringMultiple Succeeding\n");
1412 #endif /* CGICDEBUG */
1413 return cgiFormSuccess;
1415 *result = stringArray;
1418 fprintf(dout, "StringMultiple found nothing\n");
1420 #endif /* CGICDEBUG */
1421 return cgiFormNotFound;
1425 cgiFormResultType cgiFormStringSpaceNeeded(
1426 char *name, int *result) {
1428 e = cgiFormEntryFindFirst(name);
1431 return cgiFormNotFound;
1433 *result = ((int) strlen(e->value)) + 1;
1434 return cgiFormSuccess;
1437 static cgiFormResultType cgiFormEntryString(
1438 cgiFormEntry *e, char *result, int max, int newlines) {
1449 /* 1.07: don't check for available space now.
1450 We check for it immediately before adding
1451 an actual character. 1.06 handled the
1452 trailing null of the source string improperly,
1453 resulting in a cgiFormTruncated error. */
1455 /* Fix the CR/LF, LF, CR nightmare: watch for
1456 consecutive bursts of CRs and LFs in whatever
1457 pattern, then actually output the larger number
1458 of LFs. Consistently sane, yet it still allows
1459 consecutive blank lines when the user
1460 actually intends them. */
1461 if ((ch == 13) || (ch == 10)) {
1468 if (crCount || lfCount) {
1469 int lfsAdd = crCount;
1470 if (lfCount > crCount) {
1473 /* Stomp all newlines if desired */
1491 /* The end of the source string */
1494 /* 1.06: check available space before adding
1495 the character, because a previously added
1496 LF may have brought us to the limit */
1509 return cgiFormTruncated;
1511 return cgiFormEmpty;
1513 return cgiFormSuccess;
1517 static int cgiFirstNonspaceChar(char *s);
1519 cgiFormResultType cgiFormInteger(
1520 char *name, int *result, int defaultV) {
1523 e = cgiFormEntryFindFirst(name);
1526 return cgiFormNotFound;
1528 if (!strlen(e->value)) {
1530 return cgiFormEmpty;
1532 ch = cgiFirstNonspaceChar(e->value);
1533 if (!(isdigit(ch)) && (ch != '-') && (ch != '+')) {
1535 return cgiFormBadType;
1537 *result = atoi(e->value);
1538 return cgiFormSuccess;
1542 cgiFormResultType cgiFormIntegerBounded(
1543 char *name, int *result, int min, int max, int defaultV) {
1544 cgiFormResultType error = cgiFormInteger(name, result, defaultV);
1545 if (error != cgiFormSuccess) {
1548 if (*result < min) {
1550 return cgiFormConstrained;
1552 if (*result > max) {
1554 return cgiFormConstrained;
1556 return cgiFormSuccess;
1559 cgiFormResultType cgiFormDouble(
1560 char *name, double *result, double defaultV) {
1563 e = cgiFormEntryFindFirst(name);
1566 return cgiFormNotFound;
1568 if (!strlen(e->value)) {
1570 return cgiFormEmpty;
1572 ch = cgiFirstNonspaceChar(e->value);
1573 if (!(isdigit(ch)) && (ch != '.') && (ch != '-') && (ch != '+')) {
1575 return cgiFormBadType;
1577 *result = atof(e->value);
1578 return cgiFormSuccess;
1582 cgiFormResultType cgiFormDoubleBounded(
1583 char *name, double *result, double min, double max, double defaultV) {
1584 cgiFormResultType error = cgiFormDouble(name, result, defaultV);
1585 if (error != cgiFormSuccess) {
1588 if (*result < min) {
1590 return cgiFormConstrained;
1592 if (*result > max) {
1594 return cgiFormConstrained;
1596 return cgiFormSuccess;
1599 cgiFormResultType cgiFormSelectSingle(
1600 char *name, char **choicesText, int choicesTotal,
1601 int *result, int defaultV)
1605 e = cgiFormEntryFindFirst(name);
1608 fprintf(dout, "%d\n", (int) e);
1610 #endif /* CGICDEBUG */
1613 return cgiFormNotFound;
1615 for (i=0; (i < choicesTotal); i++) {
1618 fprintf(dout, "%s %s\n", choicesText[i], e->value);
1620 #endif /* CGICDEBUG */
1621 if (cgiStrEq(choicesText[i], e->value)) {
1624 fprintf(dout, "MATCH\n");
1626 #endif /* CGICDEBUG */
1628 return cgiFormSuccess;
1632 return cgiFormNoSuchChoice;
1635 cgiFormResultType cgiFormSelectMultiple(
1636 char *name, char **choicesText, int choicesTotal,
1637 int *result, int *invalid)
1643 for (i=0; (i < choicesTotal); i++) {
1646 e = cgiFormEntryFindFirst(name);
1648 *invalid = invalidE;
1649 return cgiFormNotFound;
1653 for (i=0; (i < choicesTotal); i++) {
1654 if (cgiStrEq(choicesText[i], e->value)) {
1664 } while ((e = cgiFormEntryFindNext()) != 0);
1666 *invalid = invalidE;
1669 return cgiFormSuccess;
1671 return cgiFormNotFound;
1675 cgiFormResultType cgiFormCheckboxSingle(
1679 e = cgiFormEntryFindFirst(name);
1681 return cgiFormNotFound;
1683 return cgiFormSuccess;
1686 extern cgiFormResultType cgiFormCheckboxMultiple(
1687 char *name, char **valuesText, int valuesTotal,
1688 int *result, int *invalid)
1690 /* Implementation is identical to cgiFormSelectMultiple. */
1691 return cgiFormSelectMultiple(name, valuesText,
1692 valuesTotal, result, invalid);
1695 cgiFormResultType cgiFormRadio(
1697 char **valuesText, int valuesTotal, int *result, int defaultV)
1699 /* Implementation is identical to cgiFormSelectSingle. */
1700 return cgiFormSelectSingle(name, valuesText, valuesTotal,
1704 cgiFormResultType cgiCookieString(
1709 char *p = cgiCookie;
1712 /* 2.02: if cgiCookie is exactly equal to name, this
1713 can cause an overrun. The server probably wouldn't
1714 allow it, since a name without values makes no sense
1715 -- but then again it might not check, so this is a
1716 genuine security concern. Thanks to Nicolas
1719 if ((p == '\0') && (n == '\0')) {
1720 /* Malformed cookie header from client */
1721 return cgiFormNotFound;
1726 if ((!*n) && (*p == '=')) {
1728 while ((*p != ';') && (*p != '\0') &&
1739 /* Correct parens: 2.02. Thanks to
1740 Mathieu Villeneuve-Belair. */
1741 if (!(((*p) == ';') || ((*p) == '\0')))
1743 return cgiFormTruncated;
1745 return cgiFormSuccess;
1748 /* Skip to next cookie */
1756 /* 2.01: default to empty */
1760 return cgiFormNotFound;
1763 /* Allow whitespace after semicolon */
1764 while ((*p) && isspace(*p)) {
1769 /* 2.01: actually the above loop never terminates except
1770 with a return, but do this to placate gcc */
1774 return cgiFormNotFound;
1777 cgiFormResultType cgiCookieInteger(
1783 cgiFormResultType r =
1784 cgiCookieString(name, buffer, sizeof(buffer));
1785 if (r != cgiFormSuccess) {
1788 *result = atoi(buffer);
1793 void cgiHeaderCookieSetInteger(char *name, int value, int secondsToLive,
1794 char *path, char *domain)
1797 sprintf(svalue, "%d", value);
1798 cgiHeaderCookieSetString(name, svalue, secondsToLive, path, domain);
1826 void cgiHeaderCookieSetString(char *name, char *value, int secondsToLive,
1827 char *path, char *domain)
1829 /* cgic 2.02: simpler and more widely compatible implementation.
1830 Thanks to Chunfu Lai.
1831 cgic 2.03: yes, but it didn't work. Reimplemented by
1832 Thomas Boutell. ; after last element was a bug.
1833 Examples of real world cookies that really work:
1834 Set-Cookie: MSNADS=UM=; domain=.slate.com;
1835 expires=Tue, 26-Apr-2022 19:00:00 GMT; path=/
1836 Set-Cookie: MC1=V=3&ID=b5bc08af2b8a43ff85fcb5efd8b238f0;
1837 domain=.slate.com; expires=Mon, 04-Oct-2021 19:00:00 GMT; path=/
1843 then = now + secondsToLive;
1846 "Set-Cookie: %s=%s; domain=%s; expires=%s, %02d-%s-%04d %02d:%02d:%02d GMT; path=%s\r\n",
1847 name, value, domain,
1858 void cgiHeaderLocation(char *redirectUrl) {
1859 fprintf(cgiOut, "Location: %s\r\n\r\n", redirectUrl);
1862 void cgiHeaderStatus(int status, char *statusMessage) {
1863 fprintf(cgiOut, "Status: %d %s\r\n\r\n", status, statusMessage);
1866 void cgiHeaderContentType(char *mimeType) {
1867 fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType);
1870 static int cgiWriteString(FILE *out, char *s);
1872 static int cgiWriteInt(FILE *out, int i);
1874 #define CGIC_VERSION "2.0"
1876 cgiEnvironmentResultType cgiWriteEnvironment(char *filename) {
1879 /* Be sure to open in binary mode */
1880 out = fopen(filename, "wb");
1882 /* Can't create file */
1883 return cgiEnvironmentIO;
1885 if (!cgiWriteString(out, "CGIC2.0")) {
1888 if (!cgiWriteString(out, cgiServerSoftware)) {
1891 if (!cgiWriteString(out, cgiServerName)) {
1894 if (!cgiWriteString(out, cgiGatewayInterface)) {
1897 if (!cgiWriteString(out, cgiServerProtocol)) {
1900 if (!cgiWriteString(out, cgiServerPort)) {
1903 if (!cgiWriteString(out, cgiRequestMethod)) {
1906 if (!cgiWriteString(out, cgiPathInfo)) {
1909 if (!cgiWriteString(out, cgiPathTranslated)) {
1912 if (!cgiWriteString(out, cgiScriptName)) {
1915 if (!cgiWriteString(out, cgiQueryString)) {
1918 if (!cgiWriteString(out, cgiRemoteHost)) {
1921 if (!cgiWriteString(out, cgiRemoteAddr)) {
1924 if (!cgiWriteString(out, cgiAuthType)) {
1927 if (!cgiWriteString(out, cgiRemoteUser)) {
1930 if (!cgiWriteString(out, cgiRemoteIdent)) {
1933 if (!cgiWriteString(out, cgiContentType)) {
1936 if (!cgiWriteString(out, cgiAccept)) {
1939 if (!cgiWriteString(out, cgiUserAgent)) {
1942 if (!cgiWriteString(out, cgiReferrer)) {
1945 if (!cgiWriteString(out, cgiCookie)) {
1948 if (!cgiWriteInt(out, cgiContentLength)) {
1951 e = cgiFormEntryFirst;
1954 if (!cgiWriteString(out, e->attr)) {
1957 if (!cgiWriteString(out, e->value)) {
1960 /* New 2.0 fields and file uploads */
1961 if (!cgiWriteString(out, e->fileName)) {
1964 if (!cgiWriteString(out, e->contentType)) {
1967 if (!cgiWriteInt(out, e->valueLength)) {
1970 if (cgiFormFileOpen(e->attr, &fp) == cgiFormSuccess) {
1973 if (!cgiWriteInt(out, 1)) {
1974 cgiFormFileClose(fp);
1977 while (cgiFormFileRead(fp, buffer,
1978 sizeof(buffer), &got) == cgiFormSuccess)
1980 if (((int) fwrite(buffer, 1, got, out)) != got) {
1981 cgiFormFileClose(fp);
1985 if (cgiFormFileClose(fp) != cgiFormSuccess) {
1989 if (!cgiWriteInt(out, 0)) {
1996 return cgiEnvironmentSuccess;
1999 /* If this function is not defined in your system,
2000 you must substitute the appropriate
2001 file-deletion function. */
2003 return cgiEnvironmentIO;
2006 static int cgiWriteString(FILE *out, char *s) {
2007 int len = (int) strlen(s);
2008 cgiWriteInt(out, len);
2009 if (((int) fwrite(s, 1, len, out)) != len) {
2015 static int cgiWriteInt(FILE *out, int i) {
2016 if (!fwrite(&i, sizeof(int), 1, out)) {
2022 static int cgiReadString(FILE *out, char **s);
2024 static int cgiReadInt(FILE *out, int *i);
2026 cgiEnvironmentResultType cgiReadEnvironment(char *filename) {
2028 cgiFormEntry *e = 0, *p;
2030 /* Prevent compiler warnings */
2031 cgiEnvironmentResultType result = cgiEnvironmentIO;
2032 /* Free any existing data first */
2034 /* Be sure to open in binary mode */
2035 in = fopen(filename, "rb");
2037 /* Can't access file */
2038 return cgiEnvironmentIO;
2040 if (!cgiReadString(in, &version)) {
2043 if (strcmp(version, "CGIC" CGIC_VERSION)) {
2044 /* 2.02: Merezko Oleg */
2046 return cgiEnvironmentWrongVersion;
2048 /* 2.02: Merezko Oleg */
2050 if (!cgiReadString(in, &cgiServerSoftware)) {
2053 if (!cgiReadString(in, &cgiServerName)) {
2056 if (!cgiReadString(in, &cgiGatewayInterface)) {
2059 if (!cgiReadString(in, &cgiServerProtocol)) {
2062 if (!cgiReadString(in, &cgiServerPort)) {
2065 if (!cgiReadString(in, &cgiRequestMethod)) {
2068 if (!cgiReadString(in, &cgiPathInfo)) {
2071 if (!cgiReadString(in, &cgiPathTranslated)) {
2074 if (!cgiReadString(in, &cgiScriptName)) {
2077 if (!cgiReadString(in, &cgiQueryString)) {
2080 if (!cgiReadString(in, &cgiRemoteHost)) {
2083 if (!cgiReadString(in, &cgiRemoteAddr)) {
2086 if (!cgiReadString(in, &cgiAuthType)) {
2089 if (!cgiReadString(in, &cgiRemoteUser)) {
2092 if (!cgiReadString(in, &cgiRemoteIdent)) {
2095 if (!cgiReadString(in, &cgiContentType)) {
2098 if (!cgiReadString(in, &cgiAccept)) {
2101 if (!cgiReadString(in, &cgiUserAgent)) {
2104 if (!cgiReadString(in, &cgiReferrer)) {
2108 if (!cgiReadString(in, &cgiCookie)) {
2111 if (!cgiReadInt(in, &cgiContentLength)) {
2117 e = (cgiFormEntry *) calloc(1, sizeof(cgiFormEntry));
2121 return cgiEnvironmentMemory;
2123 memset(e, 0, sizeof(cgiFormEntry));
2124 if (!cgiReadString(in, &e->attr)) {
2125 /* This means we've reached the end of the list. */
2126 /* 2.02: thanks to Merezko Oleg */
2130 if (!cgiReadString(in, &e->value)) {
2133 if (!cgiReadString(in, &e->fileName)) {
2136 if (!cgiReadString(in, &e->contentType)) {
2139 if (!cgiReadInt(in, &e->valueLength)) {
2142 if (!cgiReadInt(in, &fileFlag)) {
2148 char tfileName[1024];
2150 int len = e->valueLength;
2151 if (getTempFileName(tfileName)
2154 result = cgiEnvironmentIO;
2157 out = fopen(tfileName, "w+b");
2159 result = cgiEnvironmentIO;
2163 /* 2.01: try is a bad variable name in
2164 C++, and it wasn't being used
2167 if (tryr > ((int) sizeof(buffer))) {
2168 tryr = sizeof(buffer);
2170 got = fread(buffer, 1, tryr, in);
2172 result = cgiEnvironmentIO;
2177 if (((int) fwrite(buffer, 1, got, out)) != got) {
2178 result = cgiEnvironmentIO;
2186 e->tfileName = (char *) malloc((int) strlen(tfileName) + 1);
2187 if (!e->tfileName) {
2188 result = cgiEnvironmentMemory;
2192 strcpy(e->tfileName, tfileName);
2194 e->tfileName = (char *) malloc(1);
2195 if (!e->tfileName) {
2196 result = cgiEnvironmentMemory;
2204 cgiFormEntryFirst = e;
2210 return cgiEnvironmentSuccess;
2212 result = cgiEnvironmentMemory;
2226 if (e->contentType) {
2227 free(e->contentType);
2237 static int cgiReadString(FILE *in, char **s) {
2239 /* 2.0 fix: test cgiReadInt for failure! */
2240 if (!cgiReadInt(in, &len)) {
2243 *s = (char *) malloc(len + 1);
2247 if (((int) fread(*s, 1, len, in)) != len) {
2254 static int cgiReadInt(FILE *out, int *i) {
2255 if (!fread(i, sizeof(int), 1, out)) {
2261 static int cgiStrEqNc(char *s1, char *s2) {
2269 } else if (!(*s2)) {
2273 if (tolower(*s1) != tolower(*s2)) {
2276 } else if ((*s1) != (*s2)) {
2284 static int cgiStrBeginsNc(char *s1, char *s2) {
2288 } else if (!(*s1)) {
2292 if (tolower(*s1) != tolower(*s2)) {
2295 } else if ((*s1) != (*s2)) {
2303 static char *cgiFindTarget = 0;
2304 static cgiFormEntry *cgiFindPos = 0;
2306 static cgiFormEntry *cgiFormEntryFindFirst(char *name) {
2307 cgiFindTarget = name;
2308 cgiFindPos = cgiFormEntryFirst;
2309 return cgiFormEntryFindNext();
2312 static cgiFormEntry *cgiFormEntryFindNext() {
2313 while (cgiFindPos) {
2314 cgiFormEntry *c = cgiFindPos;
2315 cgiFindPos = c->next;
2316 if (!strcmp(c -> attr, cgiFindTarget)) {
2323 static int cgiFirstNonspaceChar(char *s) {
2324 int len = strspn(s, " \n\r\t");
2328 void cgiStringArrayFree(char **stringArray) {
2330 char **arrayItself = stringArray;
2337 /* 2.0: free the array itself! */
2341 cgiFormResultType cgiCookies(char ***result) {
2354 stringArray = (char **) malloc(sizeof(char *) * (total + 1));
2357 return cgiFormMemory;
2359 /* initialize all entries to null; the last will stay that way */
2360 for (i=0; (i <= total); i++) {
2366 while (*p && isspace(*p)) {
2370 while (*p && (*p != '=')) {
2374 stringArray[i] = (char *) malloc((p - n) + 1);
2375 if (!stringArray[i]) {
2376 cgiStringArrayFree(stringArray);
2378 return cgiFormMemory;
2380 memcpy(stringArray[i], n, p - n);
2381 stringArray[i][p - n] = '\0';
2384 while (*p && (*p != ';')) {
2394 *result = stringArray;
2395 return cgiFormSuccess;
2398 cgiFormResultType cgiFormEntries(char ***result) {
2400 cgiFormEntry *e, *pe;
2403 e = cgiFormEntryFirst;
2405 /* Don't count a field name more than once if
2406 multiple values happen to be present for it */
2407 pe = cgiFormEntryFirst;
2409 if (!strcmp(e->attr, pe->attr)) {
2410 goto skipSecondValue;
2418 stringArray = (char **) malloc(sizeof(char *) * (total + 1));
2421 return cgiFormMemory;
2423 /* initialize all entries to null; the last will stay that way */
2424 for (i=0; (i <= total); i++) {
2427 /* Now go get the entries */
2428 e = cgiFormEntryFirst;
2432 /* Don't return a field name more than once if
2433 multiple values happen to be present for it */
2434 pe = cgiFormEntryFirst;
2436 if (!strcmp(e->attr, pe->attr)) {
2437 goto skipSecondValue2;
2441 space = (int) strlen(e->attr) + 1;
2442 stringArray[i] = (char *) malloc(space);
2443 if (stringArray[i] == 0) {
2444 /* Memory problems */
2445 cgiStringArrayFree(stringArray);
2447 return cgiFormMemory;
2449 strcpy(stringArray[i], e->attr);
2454 *result = stringArray;
2455 return cgiFormSuccess;
2458 #define TRYPUTC(ch) \
2460 if (putc((ch), cgiOut) == EOF) { \
2465 cgiFormResultType cgiHtmlEscapeData(char *data, int len)
2473 } else if (*data == '&') {
2479 } else if (*data == '>') {
2489 return cgiFormSuccess;
2492 cgiFormResultType cgiHtmlEscape(char *s)
2494 return cgiHtmlEscapeData(s, (int) strlen(s));
2497 /* Output data with the " character HTML-escaped, and no
2498 other characters escaped. This is useful when outputting
2499 the contents of a tag attribute such as 'href' or 'src'.
2500 'data' is not null-terminated; 'len' is the number of
2501 bytes in 'data'. Returns cgiFormIO in the event
2502 of error, cgiFormSuccess otherwise. */
2503 cgiFormResultType cgiValueEscapeData(char *data, int len)
2506 if (*data == '\"') {
2517 return cgiFormSuccess;
2520 cgiFormResultType cgiValueEscape(char *s)
2522 return cgiValueEscapeData(s, (int) strlen(s));