cgic.c

00001 /* cgicTempDir is the only setting you are likely to need
00002   to change in this file. */
00003 
00004 /* Used only in Unix environments, in conjunction with mkstemp(). 
00005   Elsewhere (Windows), temporary files go where the tmpnam() 
00006   function suggests. If this behavior does not work for you, 
00007   modify the getTempFileName() function to suit your needs. */
00008 
00009 #define cgicTempDir "/tmp"
00010 
00011 #if CGICDEBUG
00012 #define CGICDEBUGSTART \
00013   { \
00014     FILE *dout; \
00015     dout = fopen("/home/boutell/public_html/debug", "a"); \
00016   
00017 #define CGICDEBUGEND \
00018     fclose(dout); \
00019   }
00020 #else /* CGICDEBUG */
00021 #define CGICDEBUGSTART
00022 #define CGICDEBUGEND
00023 #endif /* CGICDEBUG */
00024 
00025 #include <stdio.h>
00026 #include <string.h>
00027 #include <ctype.h>
00028 #include <stdlib.h>
00029 #include <time.h>
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032 
00033 #ifdef WIN32
00034 #include <io.h>
00035 
00036 /* cgic 2.01 */
00037 #include <fcntl.h>
00038 
00039 #else
00040 #include <unistd.h>
00041 #endif /* WIN32 */
00042 #include "cgic.h"
00043 
00044 #define cgiStrEq(a, b) (!strcmp((a), (b)))
00045 
00046 char *cgiServerSoftware;
00047 char *cgiServerName;
00048 char *cgiGatewayInterface;
00049 char *cgiServerProtocol;
00050 char *cgiServerPort;
00051 char *cgiRequestMethod;
00052 char *cgiPathInfo;
00053 char *cgiPathTranslated;
00054 char *cgiScriptName;
00055 char *cgiQueryString;
00056 char *cgiRemoteHost;
00057 char *cgiRemoteAddr;
00058 char *cgiAuthType;
00059 char *cgiRemoteUser;
00060 char *cgiRemoteIdent;
00061 char cgiContentTypeData[1024];
00062 char *cgiContentType = cgiContentTypeData;
00063 char *cgiMultipartBoundary;
00064 char *cgiCookie;
00065 int cgiContentLength;
00066 char *cgiAccept;
00067 char *cgiUserAgent;
00068 char *cgiReferrer;
00069 
00070 FILE *cgiIn;
00071 FILE *cgiOut;
00072 
00073 /* True if CGI environment was restored from a file. */
00074 static int cgiRestored = 0;
00075 
00076 static void cgiGetenv(char **s, char *var);
00077 
00078 typedef enum {
00079   cgiParseSuccess,
00080   cgiParseMemory,
00081   cgiParseIO
00082 } cgiParseResultType;
00083 
00084 /* One form entry, consisting of an attribute-value pair,
00085   and an optional filename and content type. All of
00086   these are guaranteed to be valid null-terminated strings,
00087   which will be of length zero in the event that the
00088   field is not present, with the exception of tfileName
00089   which will be null when 'in' is null. DO NOT MODIFY THESE 
00090   VALUES. Make local copies if modifications are desired. */
00091 
00092 typedef struct cgiFormEntryStruct {
00093         char *attr;
00094   /* value is populated for regular form fields only.
00095     For file uploads, it points to an empty string, and file
00096     upload data should be read from the file tfileName. */ 
00097   char *value;
00098   /* When fileName is not an empty string, tfileName is not null,
00099     and 'value' points to an empty string. */
00100   /* Valid for both files and regular fields; does not include
00101     terminating null of regular fields. */
00102   int valueLength;
00103   char *fileName; 
00104   char *contentType;
00105   /* Temporary file name for working storage of file uploads. */
00106   char *tfileName;
00107         struct cgiFormEntryStruct *next;
00108 } cgiFormEntry;
00109 
00110 /* The first form entry. */
00111 static cgiFormEntry *cgiFormEntryFirst;
00112 
00113 static cgiParseResultType cgiParseGetFormInput();
00114 static cgiParseResultType cgiParsePostFormInput();
00115 static cgiParseResultType cgiParsePostMultipartInput();
00116 static cgiParseResultType cgiParseFormInput(char *data, int length);
00117 static void cgiSetupConstants();
00118 static void cgiFreeResources();
00119 static int cgiStrEqNc(char *s1, char *s2);
00120 static int cgiStrBeginsNc(char *s1, char *s2);
00121 
00122 int main(int argc, char *argv[]) {
00123   int result;
00124   char *cgiContentLengthString;
00125   char *e;
00126   cgiSetupConstants();
00127   cgiGetenv(&cgiServerSoftware, "SERVER_SOFTWARE");
00128   cgiGetenv(&cgiServerName, "SERVER_NAME");
00129   cgiGetenv(&cgiGatewayInterface, "GATEWAY_INTERFACE");
00130   cgiGetenv(&cgiServerProtocol, "SERVER_PROTOCOL");
00131   cgiGetenv(&cgiServerPort, "SERVER_PORT");
00132   cgiGetenv(&cgiRequestMethod, "REQUEST_METHOD");
00133   cgiGetenv(&cgiPathInfo, "PATH_INFO");
00134   cgiGetenv(&cgiPathTranslated, "PATH_TRANSLATED");
00135   cgiGetenv(&cgiScriptName, "SCRIPT_NAME");
00136   cgiGetenv(&cgiQueryString, "QUERY_STRING");
00137   cgiGetenv(&cgiRemoteHost, "REMOTE_HOST");
00138   cgiGetenv(&cgiRemoteAddr, "REMOTE_ADDR");
00139   cgiGetenv(&cgiAuthType, "AUTH_TYPE");
00140   cgiGetenv(&cgiRemoteUser, "REMOTE_USER");
00141   cgiGetenv(&cgiRemoteIdent, "REMOTE_IDENT");
00142   /* 2.0: the content type string needs to be parsed and modified, so
00143     copy it to a buffer. */
00144   e = getenv("CONTENT_TYPE");
00145   if (e) {
00146     if (strlen(e) < sizeof(cgiContentTypeData)) {
00147       strcpy(cgiContentType, e);
00148     } else {
00149       /* Truncate safely in the event of what is almost certainly
00150         a hack attempt */
00151       strncpy(cgiContentType, e, sizeof(cgiContentTypeData));
00152       cgiContentType[sizeof(cgiContentTypeData) - 1] = '\0';
00153     }
00154   } else {
00155     cgiContentType[0] = '\0';
00156   }
00157   /* Never null */
00158   cgiMultipartBoundary = "";
00159   /* 2.0: parse semicolon-separated additional parameters of the
00160     content type. The one we're interested in is 'boundary'.
00161     We discard the rest to make cgiContentType more useful
00162     to the typical programmer. */
00163   if (strchr(cgiContentType, ';')) {
00164     char *sat = strchr(cgiContentType, ';');
00165     while (sat) {
00166       *sat = '\0';
00167       sat++;
00168       while (isspace(*sat)) {
00169         sat++;
00170       } 
00171       if (cgiStrBeginsNc(sat, "boundary=")) {
00172         char *s;
00173         cgiMultipartBoundary = sat + strlen("boundary=");
00174         s = cgiMultipartBoundary;
00175         while ((*s) && (!isspace(*s))) {
00176           s++;
00177         }
00178         *s = '\0';
00179         break;
00180       } else {
00181         sat = strchr(sat, ';');
00182       }   
00183     }
00184   }
00185   cgiGetenv(&cgiContentLengthString, "CONTENT_LENGTH");
00186   cgiContentLength = atoi(cgiContentLengthString);  
00187   cgiGetenv(&cgiAccept, "HTTP_ACCEPT");
00188   cgiGetenv(&cgiUserAgent, "HTTP_USER_AGENT");
00189   cgiGetenv(&cgiReferrer, "HTTP_REFERER");
00190   cgiGetenv(&cgiCookie, "HTTP_COOKIE");
00191 #ifdef CGICDEBUG
00192   CGICDEBUGSTART
00193   fprintf(dout, "%d\n", cgiContentLength);
00194   fprintf(dout, "%s\n", cgiRequestMethod);
00195   fprintf(dout, "%s\n", cgiContentType);
00196   CGICDEBUGEND  
00197 #endif /* CGICDEBUG */
00198 #ifdef WIN32
00199   /* 1.07: Must set stdin and stdout to binary mode */
00200   /* 2.0: this is particularly crucial now and must not be removed */
00201   _setmode( _fileno( stdin ), _O_BINARY );
00202   _setmode( _fileno( stdout ), _O_BINARY );
00203 #endif /* WIN32 */
00204   cgiFormEntryFirst = 0;
00205   cgiIn = stdin;
00206   cgiOut = stdout;
00207   cgiRestored = 0;
00208 
00209 
00210   /* These five lines keep compilers from
00211     producing warnings that argc and argv
00212     are unused. They have no actual function. */
00213   if (argc) {
00214     if (argv[0]) {
00215       cgiRestored = 0;
00216     }
00217   } 
00218 
00219 
00220   if (cgiStrEqNc(cgiRequestMethod, "post")) {
00221 #ifdef CGICDEBUG
00222     CGICDEBUGSTART
00223     fprintf(dout, "POST recognized\n");
00224     CGICDEBUGEND
00225 #endif /* CGICDEBUG */
00226     if (cgiStrEqNc(cgiContentType, "application/x-www-form-urlencoded")) {  
00227 #ifdef CGICDEBUG
00228       CGICDEBUGSTART
00229       fprintf(dout, "Calling PostFormInput\n");
00230       CGICDEBUGEND  
00231 #endif /* CGICDEBUG */
00232       if (cgiParsePostFormInput() != cgiParseSuccess) {
00233 #ifdef CGICDEBUG
00234         CGICDEBUGSTART
00235         fprintf(dout, "PostFormInput failed\n");
00236         CGICDEBUGEND  
00237 #endif /* CGICDEBUG */
00238         cgiFreeResources();
00239         return -1;
00240       } 
00241 #ifdef CGICDEBUG
00242       CGICDEBUGSTART
00243       fprintf(dout, "PostFormInput succeeded\n");
00244       CGICDEBUGEND  
00245 #endif /* CGICDEBUG */
00246     } else if (cgiStrEqNc(cgiContentType, "multipart/form-data")) {
00247 #ifdef CGICDEBUG
00248       CGICDEBUGSTART
00249       fprintf(dout, "Calling PostMultipartInput\n");
00250       CGICDEBUGEND  
00251 #endif /* CGICDEBUG */
00252       if (cgiParsePostMultipartInput() != cgiParseSuccess) {
00253 #ifdef CGICDEBUG
00254         CGICDEBUGSTART
00255         fprintf(dout, "PostMultipartInput failed\n");
00256         CGICDEBUGEND  
00257 #endif /* CGICDEBUG */
00258         cgiFreeResources();
00259         return -1;
00260       } 
00261 #ifdef CGICDEBUG
00262       CGICDEBUGSTART
00263       fprintf(dout, "PostMultipartInput succeeded\n");
00264       CGICDEBUGEND  
00265 #endif /* CGICDEBUG */
00266     }
00267   } else if (cgiStrEqNc(cgiRequestMethod, "get")) { 
00268     /* The spec says this should be taken care of by
00269       the server, but... it isn't */
00270     cgiContentLength = strlen(cgiQueryString);
00271     if (cgiParseGetFormInput() != cgiParseSuccess) {
00272 #ifdef CGICDEBUG
00273       CGICDEBUGSTART
00274       fprintf(dout, "GetFormInput failed\n");
00275       CGICDEBUGEND  
00276 #endif /* CGICDEBUG */
00277       cgiFreeResources();
00278       return -1;
00279     } else {  
00280 #ifdef CGICDEBUG
00281       CGICDEBUGSTART
00282       fprintf(dout, "GetFormInput succeeded\n");
00283       CGICDEBUGEND  
00284 #endif /* CGICDEBUG */
00285     }
00286   }
00287   result = cgiMain();
00288   cgiFreeResources();
00289   return result;
00290 }
00291 
00292 static void cgiGetenv(char **s, char *var){
00293   *s = getenv(var);
00294   if (!(*s)) {
00295     *s = "";
00296   }
00297 }
00298 
00299 static cgiParseResultType cgiParsePostFormInput() {
00300   char *input;
00301   cgiParseResultType result;
00302   if (!cgiContentLength) {
00303     return cgiParseSuccess;
00304   }
00305   input = (char *) malloc(cgiContentLength);
00306   if (!input) {
00307     return cgiParseMemory;  
00308   }
00309   if (((int) fread(input, 1, cgiContentLength, cgiIn)) 
00310     != cgiContentLength) 
00311   {
00312     return cgiParseIO;
00313   } 
00314   result = cgiParseFormInput(input, cgiContentLength);
00315   free(input);
00316   return result;
00317 }
00318 
00319 /* 2.0: A virtual datastream supporting putback of 
00320   enough characters to handle multipart boundaries easily.
00321   A simple memset(&mp, 0, sizeof(mp)) is suitable initialization. */
00322 
00323 typedef struct {
00324   /* Buffer for putting characters back */
00325   char putback[1024]; 
00326   /* Position in putback from which next character will be read.
00327     If readPos == writePos, then next character should
00328     come from cgiIn. */
00329   int readPos;
00330   /* Position in putback to which next character will be put back.
00331     If writePos catches up to readPos, as opposed to the other
00332     way around, the stream no longer functions properly.
00333     Calling code must guarantee that no more than 
00334     sizeof(putback) bytes are put back at any given time. */
00335   int writePos;
00336   /* Offset in the virtual datastream; can be compared
00337     to cgiContentLength */
00338   int offset;
00339 } mpStream, *mpStreamPtr;
00340 
00341 int mpRead(mpStreamPtr mpp, char *buffer, int len)
00342 {
00343   int ilen = len;
00344   int got = 0;
00345   while (len) {
00346     if (mpp->readPos != mpp->writePos) {
00347       *buffer++ = mpp->putback[mpp->readPos++];
00348       mpp->readPos %= sizeof(mpp->putback);
00349       got++;
00350       len--;
00351     } else {
00352       break;
00353     } 
00354   }
00355   /* Refuse to read past the declared length in order to
00356     avoid deadlock */
00357   if (len > (cgiContentLength - mpp->offset)) {
00358     len = cgiContentLength - mpp->offset;
00359   }
00360   if (len) {
00361     int fgot = fread(buffer, 1, len, cgiIn);
00362     if (fgot >= 0) {
00363       mpp->offset += (got + fgot);
00364       return got + fgot;
00365     } else if (got > 0) {
00366       mpp->offset += got;
00367       return got;
00368     } else {
00369       /* EOF or error */
00370       return fgot;
00371     }
00372   } else if (got) {
00373     return got;
00374   } else if (ilen) {  
00375     return EOF;
00376   } else {
00377     /* 2.01 */
00378     return 0;
00379   }
00380 }
00381 
00382 void mpPutBack(mpStreamPtr mpp, char *data, int len)
00383 {
00384   mpp->offset -= len;
00385   while (len) {
00386     mpp->putback[mpp->writePos++] = *data++;
00387     mpp->writePos %= sizeof(mpp->putback);
00388     len--;
00389   }
00390 }
00391 
00392 /* This function copies the body to outf if it is not null, otherwise to
00393   a newly allocated character buffer at *outP, which will be null
00394   terminated; if both outf and outP are null the body is not stored.
00395   If bodyLengthP is not null, the size of the body in bytes is stored
00396   to *bodyLengthP, not including any terminating null added to *outP. 
00397   If 'first' is nonzero, a preceding newline is not expected before
00398   the boundary. If 'first' is zero, a preceding newline is expected.
00399   Upon return mpp is positioned after the boundary and its trailing 
00400   newline, if any; if the boundary is followed by -- the next two 
00401   characters read after this function returns will be --. Upon error, 
00402   if outP is not null, *outP is a null pointer; *bodyLengthP 
00403   is set to zero. Returns cgiParseSuccess, cgiParseMemory 
00404   or cgiParseIO. */
00405 
00406 static cgiParseResultType afterNextBoundary(mpStreamPtr mpp,
00407   FILE *outf,
00408   char **outP,
00409   int *bodyLengthP,
00410   int first
00411   );
00412 
00413 static int readHeaderLine(
00414   mpStreamPtr mpp,  
00415   char *attr,
00416   int attrSpace,
00417   char *value,
00418   int valueSpace);
00419 
00420 static void decomposeValue(char *value,
00421   char *mvalue, int mvalueSpace,
00422   char **argNames,
00423   char **argValues,
00424   int argValueSpace);
00425 
00426 /* tfileName must be 1024 bytes to ensure adequacy on
00427   win32 (1024 exceeds the maximum path length and
00428   certainly exceeds observed behavior of _tmpnam).
00429   May as well also be 1024 bytes on Unix, although actual
00430   length is strlen(cgiTempDir) + a short unique pattern. */
00431   
00432 static cgiParseResultType getTempFileName(char *tfileName);
00433 
00434 static cgiParseResultType cgiParsePostMultipartInput() {
00435   cgiParseResultType result;
00436   cgiFormEntry *n = 0, *l = 0;
00437   int got;
00438   FILE *outf = 0;
00439   char *out = 0;
00440   char tfileName[1024];
00441   mpStream mp;
00442   mpStreamPtr mpp = &mp;
00443   memset(&mp, 0, sizeof(mp));
00444   if (!cgiContentLength) {
00445     return cgiParseSuccess;
00446   }
00447   /* Read first boundary, including trailing newline */
00448   result = afterNextBoundary(mpp, 0, 0, 0, 1);
00449   if (result == cgiParseIO) { 
00450     /* An empty submission is not necessarily an error */
00451     return cgiParseSuccess;
00452   } else if (result != cgiParseSuccess) {
00453     return result;
00454   }
00455   while (1) {
00456     char d[1024];
00457     char fvalue[1024];
00458     char fname[1024];
00459     int bodyLength = 0;
00460     char ffileName[1024];
00461     char fcontentType[1024];
00462     char attr[1024];
00463     char value[1024];
00464     fvalue[0] = 0;
00465     fname[0] = 0;
00466     ffileName[0] = 0;
00467     fcontentType[0] = 0;
00468     out = 0;
00469     outf = 0;
00470     /* Check for EOF */
00471     got = mpRead(mpp, d, 2);
00472     if (got < 2) {
00473       /* Crude EOF */
00474       break;
00475     }
00476     if ((d[0] == '-') && (d[1] == '-')) {
00477       /* Graceful EOF */
00478       break;
00479     }
00480     mpPutBack(mpp, d, 2);
00481     /* Read header lines until end of header */
00482     while (readHeaderLine(
00483         mpp, attr, sizeof(attr), value, sizeof(value))) 
00484     {
00485       char *argNames[3];
00486       char *argValues[2];
00487       /* Content-Disposition: form-data; 
00488         name="test"; filename="googley.gif" */
00489       if (cgiStrEqNc(attr, "Content-Disposition")) {
00490         argNames[0] = "name";
00491         argNames[1] = "filename";
00492         argNames[2] = 0;
00493         argValues[0] = fname;
00494         argValues[1] = ffileName;
00495         decomposeValue(value, 
00496           fvalue, sizeof(fvalue),
00497           argNames,
00498           argValues,
00499           1024);  
00500       } else if (cgiStrEqNc(attr, "Content-Type")) {
00501         argNames[0] = 0;
00502         decomposeValue(value, 
00503           fcontentType, sizeof(fcontentType),
00504           argNames,
00505           0,
00506           0);
00507       }
00508     }
00509     if (!cgiStrEqNc(fvalue, "form-data")) {
00510       /* Not form data */ 
00511       continue;
00512     }
00513     /* Body is everything from here until the next 
00514       boundary. So, set it aside and move past boundary. 
00515       If a filename was submitted as part of the
00516       disposition header, store to a temporary file.
00517       Otherwise, store to a memory buffer (it is
00518       presumably a regular form field). */
00519     if (strlen(ffileName)) {
00520       if (getTempFileName(tfileName) != cgiParseSuccess) {
00521         return cgiParseIO;
00522       } 
00523       outf = fopen(tfileName, "w+b");
00524     } else {
00525       outf = 0;
00526       tfileName[0] = '\0';
00527     } 
00528     result = afterNextBoundary(mpp, outf, &out, &bodyLength, 0);
00529     if (result != cgiParseSuccess) {
00530       /* Lack of a boundary here is an error. */
00531       if (outf) {
00532         fclose(outf);
00533         unlink(tfileName);
00534       }
00535       if (out) {
00536         free(out);
00537       }
00538       return result;
00539     }
00540     /* OK, we have a new pair, add it to the list. */
00541     n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));  
00542     if (!n) {
00543       goto outOfMemory;
00544     }
00545     memset(n, 0, sizeof(cgiFormEntry));
00546     /* 2.01: one of numerous new casts required
00547       to please C++ compilers */
00548     n->attr = (char *) malloc(strlen(fname) + 1);
00549     if (!n->attr) {
00550       goto outOfMemory;
00551     }
00552     strcpy(n->attr, fname);
00553     if (out) {
00554       n->value = out;
00555       out = 0;
00556     } else if (outf) {
00557       n->value = (char *) malloc(1);
00558       if (!n->value) {
00559         goto outOfMemory;
00560       }
00561       n->value[0] = '\0';
00562       fclose(outf);
00563     }
00564     n->valueLength = bodyLength;
00565     n->next = 0;
00566     if (!l) {
00567       cgiFormEntryFirst = n;
00568     } else {
00569       l->next = n;
00570     }
00571     n->fileName = (char *) malloc(strlen(ffileName) + 1);
00572     if (!n->fileName) {
00573       goto outOfMemory;
00574     }
00575     strcpy(n->fileName, ffileName);
00576     n->contentType = (char *) malloc(strlen(fcontentType) + 1);
00577     if (!n->contentType) {
00578       goto outOfMemory;
00579     }
00580     strcpy(n->contentType, fcontentType);
00581     n->tfileName = (char *) malloc(strlen(tfileName) + 1);
00582     if (!n->tfileName) {
00583       goto outOfMemory;
00584     }
00585     strcpy(n->tfileName, tfileName);
00586 
00587     l = n;      
00588   } 
00589   return cgiParseSuccess;
00590 outOfMemory:
00591   if (n) {
00592     if (n->attr) {
00593       free(n->attr);
00594     }
00595     if (n->value) {
00596       free(n->value);
00597     }
00598     if (n->fileName) {
00599       free(n->fileName);
00600     }
00601     if (n->tfileName) {
00602       free(n->tfileName);
00603     }
00604     if (n->contentType) {
00605       free(n->contentType);
00606     }
00607     free(n);
00608   }
00609   if (out) {
00610     free(out);
00611   }
00612   if (outf) {
00613     fclose(outf);
00614     unlink(tfileName);
00615   }
00616   return cgiParseMemory;
00617 }
00618 
00619 static cgiParseResultType getTempFileName(char *tfileName)
00620 {
00621 #ifndef WIN32
00622   /* Unix. Use the robust 'mkstemp' function to create
00623     a temporary file that is truly unique, with
00624     permissions that are truly safe. The 
00625     fopen-for-write destroys any bogus information
00626     written by potential hackers during the brief
00627     window between the file's creation and the
00628     chmod call (glibc 2.0.6 and lower might
00629     otherwise have allowed this). */
00630   int outfd; 
00631   strcpy(tfileName, cgicTempDir "/cgicXXXXXX");
00632   outfd = mkstemp(tfileName);
00633   if (outfd == -1) {
00634     return cgiParseIO;
00635   }
00636   close(outfd);
00637   /* Fix the permissions */
00638   if (chmod(tfileName, 0600) != 0) {
00639     unlink(tfileName);
00640     return cgiParseIO;
00641   }
00642 #else
00643   /* Non-Unix. Do what we can. */
00644   if (!tmpnam(tfileName)) {
00645     return cgiParseIO;
00646   }
00647 #endif
00648   return cgiParseSuccess;
00649 }
00650 
00651 
00652 #define APPEND(string, char) \
00653   { \
00654     if ((string##Len + 1) < string##Space) { \
00655       string[string##Len++] = (char); \
00656     } \
00657   }
00658 
00659 #define RAPPEND(string, ch) \
00660   { \
00661     if ((string##Len + 1) == string##Space)  { \
00662       char *sold = string; \
00663       string##Space *= 2; \
00664       string = (char *) realloc(string, string##Space); \
00665       if (!string) { \
00666         string = sold; \
00667         goto outOfMemory; \
00668       } \
00669     } \
00670     string[string##Len++] = (ch); \
00671   }
00672     
00673 #define BAPPEND(ch) \
00674   { \
00675     if (outf) { \
00676       putc(ch, outf); \
00677       outLen++; \
00678     } else if (out) { \
00679       RAPPEND(out, ch); \
00680     } \
00681   }
00682 
00683 cgiParseResultType afterNextBoundary(mpStreamPtr mpp, FILE *outf, char **outP,
00684   int *bodyLengthP, int first)
00685 {
00686   int outLen = 0;
00687   int outSpace = 256;
00688   char *out = 0;
00689   cgiParseResultType result;
00690   int boffset;
00691   int got;
00692   char d[2];  
00693   /* This is large enough, because the buffer into which the
00694     original boundary string is fetched is shorter by more
00695     than four characters due to the space required for
00696     the attribute name */
00697   char workingBoundaryData[1024];
00698   char *workingBoundary = workingBoundaryData;
00699   int workingBoundaryLength;
00700   if ((!outf) && (outP)) {
00701     out = (char *) malloc(outSpace);
00702     if (!out) {
00703       goto outOfMemory;
00704     }
00705   }
00706   boffset = 0;
00707   sprintf(workingBoundaryData, "\r\n--%s", cgiMultipartBoundary);
00708   if (first) {
00709     workingBoundary = workingBoundaryData + 2;
00710   }
00711   workingBoundaryLength = strlen(workingBoundary);
00712   while (1) {
00713     got = mpRead(mpp, d, 1);
00714     if (got != 1) {
00715       /* 2.01: cgiParseIO, not cgiFormIO */
00716       result = cgiParseIO;
00717       goto error;
00718     }
00719     if (d[0] == workingBoundary[boffset]) {
00720       /* We matched the next byte of the boundary.
00721         Keep track of our progress into the
00722         boundary and don't emit anything. */
00723       boffset++;
00724       if (boffset == workingBoundaryLength) {
00725         break;
00726       } 
00727     } else if (boffset > 0) {
00728       /* We matched part, but not all, of the
00729         boundary. Now we have to be careful:
00730         put back all except the first
00731         character and try again. The 
00732         real boundary could begin in the
00733         middle of a false match. We can
00734         emit the first character only so far. */
00735       BAPPEND(workingBoundary[0]);
00736       mpPutBack(mpp, 
00737         workingBoundary + 1, boffset - 1);
00738       mpPutBack(mpp, d, 1);
00739       boffset = 0;
00740     } else {    
00741       /* Not presently in the middle of a boundary
00742         match; just emit the character. */
00743       BAPPEND(d[0]);
00744     } 
00745   }
00746   /* Read trailing newline or -- EOF marker. A literal EOF here
00747     would be an error in the input stream. */
00748   got = mpRead(mpp, d, 2);
00749   if (got != 2) {
00750     result = cgiParseIO;
00751     goto error;
00752   } 
00753   if ((d[0] == '\r') && (d[1] == '\n')) {
00754     /* OK, EOL */
00755   } else if (d[0] == '-') {
00756     /* Probably EOF, but we check for
00757       that later */
00758     mpPutBack(mpp, d, 2);
00759   } 
00760   if (out && outSpace) {
00761     char *oout = out;
00762     out[outLen] = '\0';
00763     out = (char *) realloc(out, outLen + 1);
00764     if (!out) {
00765       /* Surprising if it happens; and not fatal! We were
00766         just trying to give some space back. We can
00767         keep it if we have to. */
00768       out = oout;
00769     }
00770     *outP = out;
00771   }
00772   if (bodyLengthP) {
00773     *bodyLengthP = outLen;
00774   }
00775   return cgiParseSuccess;
00776 outOfMemory:
00777   result = cgiParseMemory;
00778   if (outP) {
00779     if (out) {
00780       free(out);
00781     }
00782     *outP = '\0'; 
00783   }
00784 error:
00785   if (bodyLengthP) {
00786     *bodyLengthP = 0;
00787   }
00788   if (out) {
00789     free(out);
00790   }
00791   if (outP) {
00792     *outP = 0;  
00793   }
00794   return result;
00795 }
00796 
00797 static void decomposeValue(char *value,
00798   char *mvalue, int mvalueSpace,
00799   char **argNames,
00800   char **argValues,
00801   int argValueSpace)
00802 {
00803   char argName[1024];
00804   int argNameSpace = sizeof(argName);
00805   int argNameLen = 0;
00806   int mvalueLen = 0;
00807   char *argValue;
00808   int argNum = 0;
00809   while (argNames[argNum]) {
00810     if (argValueSpace) {
00811       argValues[argNum][0] = '\0';
00812     }
00813     argNum++;
00814   }
00815   while (isspace(*value)) {
00816     value++;
00817   }
00818   /* Quoted mvalue */
00819   if (*value == '\"') {
00820     value++;
00821     while ((*value) && (*value != '\"')) {
00822       APPEND(mvalue, *value);
00823       value++;
00824     }
00825     while ((*value) && (*value != ';')) {
00826       value++;
00827     }
00828   } else {
00829     /* Unquoted mvalue */
00830     while ((*value) && (*value != ';')) {
00831       APPEND(mvalue, *value);
00832       value++;
00833     } 
00834   } 
00835   if (mvalueSpace) {
00836     mvalue[mvalueLen] = '\0';
00837   }
00838   while (*value == ';') {
00839     int argNum;
00840     int argValueLen = 0;
00841     /* Skip the ; between parameters */
00842     value++;
00843     /* Now skip leading whitespace */
00844     while ((*value) && (isspace(*value))) { 
00845       value++;
00846     }
00847     /* Now read the parameter name */
00848     argNameLen = 0;
00849     while ((*value) && (isalnum(*value))) {
00850       APPEND(argName, *value);
00851       value++;
00852     }
00853     if (argNameSpace) {
00854       argName[argNameLen] = '\0';
00855     }
00856     while ((*value) && isspace(*value)) {
00857       value++;
00858     }
00859     if (*value != '=') {
00860       /* Malformed line */
00861       return; 
00862     }
00863     value++;
00864     while ((*value) && isspace(*value)) {
00865       value++;
00866     }
00867     /* Find the parameter in the argument list, if present */
00868     argNum = 0;
00869     argValue = 0;
00870     while (argNames[argNum]) {
00871       if (cgiStrEqNc(argName, argNames[argNum])) {
00872         argValue = argValues[argNum];
00873         break;
00874       }
00875       argNum++;
00876     }   
00877     /* Finally, read the parameter value */
00878     if (*value == '\"') {
00879       value++;
00880       while ((*value) && (*value != '\"')) {
00881         if (argValue) {
00882           APPEND(argValue, *value);
00883         }
00884         value++;
00885       }
00886       while ((*value) && (*value != ';')) {
00887         value++;
00888       }
00889     } else {
00890       /* Unquoted value */
00891       while ((*value) && (*value != ';')) {
00892         if (argNames[argNum]) {
00893           APPEND(argValue, *value);
00894         }
00895         value++;
00896       } 
00897     } 
00898     if (argValueSpace) {
00899       argValue[argValueLen] = '\0';
00900     }
00901   }   
00902 }
00903 
00904 static int readHeaderLine(
00905   mpStreamPtr mpp,
00906   char *attr,
00907   int attrSpace,
00908   char *value,
00909   int valueSpace)
00910 { 
00911   int attrLen = 0;
00912   int valueLen = 0;
00913   int valueFound = 0;
00914   while (1) {
00915     char d[1];
00916     int got = mpRead(mpp, d, 1);
00917     if (got != 1) { 
00918       return 0;
00919     }
00920     if (d[0] == '\r') {
00921       got = mpRead(mpp, d, 1);
00922       if (got == 1) { 
00923         if (d[0] == '\n') {
00924           /* OK */
00925         } else {
00926           mpPutBack(mpp, d, 1);
00927         }
00928       }
00929       break;
00930     } else if (d[0] == '\n') {
00931       break;
00932     } else if ((d[0] == ':') && attrLen) {
00933       valueFound = 1;
00934       while (mpRead(mpp, d, 1) == 1) {
00935         if (!isspace(d[0])) {
00936           mpPutBack(mpp, d, 1);
00937           break;
00938         } 
00939       }
00940     } else if (!valueFound) {
00941       if (!isspace(*d)) {
00942         if (attrLen < (attrSpace - 1)) {
00943           attr[attrLen++] = *d;
00944         }
00945       }   
00946     } else if (valueFound) {  
00947       if (valueLen < (valueSpace - 1)) {
00948         value[valueLen++] = *d;
00949       }
00950     }
00951   }
00952   if (attrSpace) {
00953     attr[attrLen] = '\0';
00954   }
00955   if (valueSpace) {
00956     value[valueLen] = '\0';
00957   }
00958   if (attrLen && valueLen) {
00959     return 1;
00960   } else {
00961     return 0;
00962   }
00963 }
00964 
00965 static cgiParseResultType cgiParseGetFormInput() {
00966   return cgiParseFormInput(cgiQueryString, cgiContentLength);
00967 }
00968 
00969 typedef enum {
00970   cgiEscapeRest,
00971   cgiEscapeFirst,
00972   cgiEscapeSecond
00973 } cgiEscapeState;
00974 
00975 typedef enum {
00976   cgiUnescapeSuccess,
00977   cgiUnescapeMemory
00978 } cgiUnescapeResultType;
00979 
00980 static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
00981 
00982 static cgiParseResultType cgiParseFormInput(char *data, int length) {
00983   /* Scan for pairs, unescaping and storing them as they are found. */
00984   int pos = 0;
00985   cgiFormEntry *n;
00986   cgiFormEntry *l = 0;
00987   while (pos != length) {
00988     int foundEq = 0;
00989     int foundAmp = 0;
00990     int start = pos;
00991     int len = 0;
00992     char *attr;
00993     char *value;
00994     while (pos != length) {
00995       if (data[pos] == '=') {
00996         foundEq = 1;
00997         pos++;
00998         break;
00999       }
01000       pos++;
01001       len++;
01002     }
01003     if (!foundEq) {
01004       break;
01005     }
01006     if (cgiUnescapeChars(&attr, data+start, len)
01007       != cgiUnescapeSuccess) {
01008       return cgiParseMemory;
01009     } 
01010     start = pos;
01011     len = 0;
01012     while (pos != length) {
01013       if (data[pos] == '&') {
01014         foundAmp = 1;
01015         pos++;
01016         break;
01017       }
01018       pos++;
01019       len++;
01020     }
01021     /* The last pair probably won't be followed by a &, but
01022       that's fine, so check for that after accepting it */
01023     if (cgiUnescapeChars(&value, data+start, len)
01024       != cgiUnescapeSuccess) {
01025       free(attr);
01026       return cgiParseMemory;
01027     } 
01028     /* OK, we have a new pair, add it to the list. */
01029     n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));  
01030     if (!n) {
01031       free(attr);
01032       free(value);
01033       return cgiParseMemory;
01034     }
01035     n->attr = attr;
01036     n->value = value;
01037     n->valueLength = strlen(n->value);
01038     n->fileName = (char *) malloc(1);
01039     if (!n->fileName) {
01040       free(attr);
01041       free(value);
01042       free(n);
01043       return cgiParseMemory;
01044     } 
01045     n->fileName[0] = '\0';
01046     n->contentType = (char *) malloc(1);
01047     if (!n->contentType) {
01048       free(attr);
01049       free(value);
01050       free(n->fileName);
01051       free(n);
01052       return cgiParseMemory;
01053     } 
01054     n->contentType[0] = '\0';
01055     n->tfileName = (char *) malloc(1);
01056     if (!n->tfileName) {
01057       free(attr);
01058       free(value);
01059       free(n->fileName);
01060       free(n->contentType);
01061       free(n);
01062       return cgiParseMemory;
01063     } 
01064     n->tfileName[0] = '\0';
01065     n->next = 0;
01066     if (!l) {
01067       cgiFormEntryFirst = n;
01068     } else {
01069       l->next = n;
01070     }
01071     l = n;
01072     if (!foundAmp) {
01073       break;
01074     }     
01075   }
01076   return cgiParseSuccess;
01077 }
01078 
01079 static int cgiHexValue[256];
01080 
01081 cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len) {
01082   char *s;
01083   cgiEscapeState escapeState = cgiEscapeRest;
01084   int escapedValue = 0;
01085   int srcPos = 0;
01086   int dstPos = 0;
01087   s = (char *) malloc(len + 1);
01088   if (!s) {
01089     return cgiUnescapeMemory;
01090   }
01091   while (srcPos < len) {
01092     int ch = cp[srcPos];
01093     switch (escapeState) {
01094       case cgiEscapeRest:
01095       if (ch == '%') {
01096         escapeState = cgiEscapeFirst;
01097       } else if (ch == '+') {
01098         s[dstPos++] = ' ';
01099       } else {
01100         s[dstPos++] = ch; 
01101       }
01102       break;
01103       case cgiEscapeFirst:
01104       escapedValue = cgiHexValue[ch] << 4;  
01105       escapeState = cgiEscapeSecond;
01106       break;
01107       case cgiEscapeSecond:
01108       escapedValue += cgiHexValue[ch];
01109       s[dstPos++] = escapedValue;
01110       escapeState = cgiEscapeRest;
01111       break;
01112     }
01113     srcPos++;
01114   }
01115   s[dstPos] = '\0';
01116   *sp = s;
01117   return cgiUnescapeSuccess;
01118 }   
01119   
01120 static void cgiSetupConstants() {
01121   int i;
01122   for (i=0; (i < 256); i++) {
01123     cgiHexValue[i] = 0;
01124   }
01125   cgiHexValue['0'] = 0; 
01126   cgiHexValue['1'] = 1; 
01127   cgiHexValue['2'] = 2; 
01128   cgiHexValue['3'] = 3; 
01129   cgiHexValue['4'] = 4; 
01130   cgiHexValue['5'] = 5; 
01131   cgiHexValue['6'] = 6; 
01132   cgiHexValue['7'] = 7; 
01133   cgiHexValue['8'] = 8; 
01134   cgiHexValue['9'] = 9;
01135   cgiHexValue['A'] = 10;
01136   cgiHexValue['B'] = 11;
01137   cgiHexValue['C'] = 12;
01138   cgiHexValue['D'] = 13;
01139   cgiHexValue['E'] = 14;
01140   cgiHexValue['F'] = 15;
01141   cgiHexValue['a'] = 10;
01142   cgiHexValue['b'] = 11;
01143   cgiHexValue['c'] = 12;
01144   cgiHexValue['d'] = 13;
01145   cgiHexValue['e'] = 14;
01146   cgiHexValue['f'] = 15;
01147 }
01148 
01149 static void cgiFreeResources() {
01150   cgiFormEntry *c = cgiFormEntryFirst;
01151   cgiFormEntry *n;
01152   while (c) {
01153     n = c->next;
01154     free(c->attr);
01155     free(c->value);
01156     free(c->fileName);
01157     free(c->contentType);
01158     if (strlen(c->tfileName)) {
01159       unlink(c->tfileName);
01160     }
01161     free(c->tfileName);
01162     free(c);
01163     c = n;
01164   }
01165   /* If the cgi environment was restored from a saved environment,
01166     then these are in allocated space and must also be freed */
01167   if (cgiRestored) {
01168     free(cgiServerSoftware);
01169     free(cgiServerName);
01170     free(cgiGatewayInterface);
01171     free(cgiServerProtocol);
01172     free(cgiServerPort);
01173     free(cgiRequestMethod);
01174     free(cgiPathInfo);
01175     free(cgiPathTranslated);
01176     free(cgiScriptName);
01177     free(cgiQueryString);
01178     free(cgiRemoteHost);
01179     free(cgiRemoteAddr);
01180     free(cgiAuthType);
01181     free(cgiRemoteUser);
01182     free(cgiRemoteIdent);
01183     free(cgiContentType);
01184     free(cgiAccept);
01185     free(cgiUserAgent);
01186     free(cgiReferrer);
01187   }
01188   /* 2.0: to clean up the environment for cgiReadEnvironment,
01189     we must set these correctly */
01190   cgiFormEntryFirst = 0;
01191   cgiRestored = 0;
01192 }
01193 
01194 static cgiFormResultType cgiFormEntryString(
01195   cgiFormEntry *e, char *result, int max, int newlines);
01196 
01197 static cgiFormEntry *cgiFormEntryFindFirst(char *name);
01198 static cgiFormEntry *cgiFormEntryFindNext();
01199 
01200 cgiFormResultType cgiFormString(
01201         char *name, char *result, int max) {
01202   cgiFormEntry *e;
01203   e = cgiFormEntryFindFirst(name);
01204   if (!e) {
01205     strcpy(result, "");
01206     return cgiFormNotFound;
01207   }
01208   return cgiFormEntryString(e, result, max, 1);
01209 }
01210 
01211 cgiFormResultType cgiFormFileName(
01212   char *name, char *result, int resultSpace)
01213 {
01214   cgiFormEntry *e;
01215   int resultLen = 0;
01216   char *s;
01217   e = cgiFormEntryFindFirst(name);
01218   if (!e) {
01219     strcpy(result, "");
01220     return cgiFormNotFound;
01221   }
01222   s = e->fileName;
01223   while (*s) {
01224     APPEND(result, *s);
01225     s++;
01226   } 
01227   if (resultSpace) {
01228     result[resultLen] = '\0';
01229   }
01230   if (!strlen(e->fileName)) {
01231     return cgiFormNoFileName;
01232   } else if (((int) strlen(e->fileName)) > (resultSpace - 1)) {
01233     return cgiFormTruncated;
01234   } else {
01235     return cgiFormSuccess;
01236   }
01237 }
01238 
01239 cgiFormResultType cgiFormFileContentType(
01240   char *name, char *result, int resultSpace)
01241 {
01242   cgiFormEntry *e;
01243   int resultLen = 0;
01244   char *s;
01245   e = cgiFormEntryFindFirst(name);
01246   if (!e) {
01247     if (resultSpace) {
01248       result[0] = '\0';
01249     } 
01250     return cgiFormNotFound;
01251   }
01252   s = e->contentType;
01253   while (*s) {
01254     APPEND(result, *s);
01255     s++;
01256   } 
01257   if (resultSpace) {
01258     result[resultLen] = '\0';
01259   }
01260   if (!strlen(e->contentType)) {
01261     return cgiFormNoContentType;
01262   } else if (((int) strlen(e->contentType)) > (resultSpace - 1)) {
01263     return cgiFormTruncated;
01264   } else {
01265     return cgiFormSuccess;
01266   }
01267 }
01268 
01269 cgiFormResultType cgiFormFileSize(
01270   char *name, int *sizeP)
01271 {
01272   cgiFormEntry *e;
01273   e = cgiFormEntryFindFirst(name);
01274   if (!e) {
01275     if (sizeP) {
01276       *sizeP = 0;
01277     }
01278     return cgiFormNotFound;
01279   } else if (!strlen(e->tfileName)) {
01280     if (sizeP) {
01281       *sizeP = 0;
01282     }
01283     return cgiFormNotAFile;
01284   } else {
01285     if (sizeP) {
01286       *sizeP = e->valueLength;
01287     }
01288     return cgiFormSuccess;
01289   }
01290 }
01291 
01292 typedef struct cgiFileStruct {
01293   FILE *in;
01294 } cgiFile;
01295 
01296 cgiFormResultType cgiFormFileOpen(
01297   char *name, cgiFilePtr *cfpp)
01298 {
01299   cgiFormEntry *e;
01300   cgiFilePtr cfp;
01301   e = cgiFormEntryFindFirst(name);
01302   if (!e) {
01303     *cfpp = 0;
01304     return cgiFormNotFound;
01305   }
01306   if (!strlen(e->tfileName)) {
01307     *cfpp = 0;
01308     return cgiFormNotAFile;
01309   }
01310   cfp = (cgiFilePtr) malloc(sizeof(cgiFile));
01311   if (!cfp) {
01312     *cfpp = 0;
01313     return cgiFormMemory;
01314   }
01315   cfp->in = fopen(e->tfileName, "rb");
01316   if (!cfp->in) {
01317     free(cfp);
01318     return cgiFormIO;
01319   }
01320   *cfpp = cfp;
01321   return cgiFormSuccess;
01322 }
01323 
01324 cgiFormResultType cgiFormFileRead(
01325   cgiFilePtr cfp, char *buffer, 
01326   int bufferSize, int *gotP)
01327 {
01328   int got = 0;
01329   if (!cfp) {
01330     return cgiFormOpenFailed;
01331   }
01332   got = fread(buffer, 1, bufferSize, cfp->in);
01333   if (got <= 0) {
01334     return cgiFormEOF;
01335   }
01336   *gotP = got;
01337   return cgiFormSuccess;
01338 }
01339 
01340 cgiFormResultType cgiFormFileClose(cgiFilePtr cfp)
01341 {
01342   if (!cfp) {
01343     return cgiFormOpenFailed;
01344   }
01345   fclose(cfp->in);
01346   free(cfp);
01347   return cgiFormSuccess;
01348 }
01349 
01350 cgiFormResultType cgiFormStringNoNewlines(
01351         char *name, char *result, int max) {
01352   cgiFormEntry *e;
01353   e = cgiFormEntryFindFirst(name);
01354   if (!e) {
01355     strcpy(result, "");
01356     return cgiFormNotFound;
01357   }
01358   return cgiFormEntryString(e, result, max, 0);
01359 }
01360 
01361 cgiFormResultType cgiFormStringMultiple(
01362         char *name, char ***result) {
01363   char **stringArray;
01364   cgiFormEntry *e;
01365   int i;
01366   int total = 0;
01367   /* Make two passes. One would be more efficient, but this
01368     function is not commonly used. The select menu and
01369     radio box functions are faster. */
01370   e = cgiFormEntryFindFirst(name);
01371   if (e != 0) {
01372     do {
01373       total++;
01374     } while ((e = cgiFormEntryFindNext()) != 0); 
01375   }
01376   stringArray = (char **) malloc(sizeof(char *) * (total + 1));
01377   if (!stringArray) {
01378     *result = 0;
01379     return cgiFormMemory;
01380   }
01381   /* initialize all entries to null; the last will stay that way */
01382   for (i=0; (i <= total); i++) {
01383     stringArray[i] = 0;
01384   }
01385   /* Now go get the entries */
01386   e = cgiFormEntryFindFirst(name);
01387 #ifdef CGICDEBUG
01388   CGICDEBUGSTART
01389   fprintf(dout, "StringMultiple Beginning\n");
01390   CGICDEBUGEND
01391 #endif /* CGICDEBUG */
01392   if (e) {
01393     i = 0;
01394     do {
01395       int max = (int) (strlen(e->value) + 1);
01396       stringArray[i] = (char *) malloc(max);
01397       if (stringArray[i] == 0) {
01398         /* Memory problems */
01399         cgiStringArrayFree(stringArray);
01400         *result = 0;
01401         return cgiFormMemory;
01402       } 
01403       strcpy(stringArray[i], e->value);
01404       cgiFormEntryString(e, stringArray[i], max, 1);
01405       i++;
01406     } while ((e = cgiFormEntryFindNext()) != 0); 
01407     *result = stringArray;
01408 #ifdef CGICDEBUG
01409     CGICDEBUGSTART
01410     fprintf(dout, "StringMultiple Succeeding\n");
01411     CGICDEBUGEND
01412 #endif /* CGICDEBUG */
01413     return cgiFormSuccess;
01414   } else {
01415     *result = stringArray;
01416 #ifdef CGICDEBUG
01417     CGICDEBUGSTART
01418     fprintf(dout, "StringMultiple found nothing\n");
01419     CGICDEBUGEND
01420 #endif /* CGICDEBUG */
01421     return cgiFormNotFound;
01422   } 
01423 }
01424 
01425 cgiFormResultType cgiFormStringSpaceNeeded(
01426         char *name, int *result) {
01427   cgiFormEntry *e;
01428   e = cgiFormEntryFindFirst(name);
01429   if (!e) {
01430     *result = 1;
01431     return cgiFormNotFound; 
01432   }
01433   *result = ((int) strlen(e->value)) + 1;
01434   return cgiFormSuccess;
01435 }
01436 
01437 static cgiFormResultType cgiFormEntryString(
01438   cgiFormEntry *e, char *result, int max, int newlines) {
01439   char *dp, *sp;
01440   int truncated = 0;
01441   int len = 0;
01442   int avail = max-1;
01443   int crCount = 0;
01444   int lfCount = 0;  
01445   dp = result;
01446   sp = e->value;  
01447   while (1) {
01448     int ch;
01449     /* 1.07: don't check for available space now.
01450       We check for it immediately before adding
01451       an actual character. 1.06 handled the
01452       trailing null of the source string improperly,
01453       resulting in a cgiFormTruncated error. */
01454     ch = *sp;
01455     /* Fix the CR/LF, LF, CR nightmare: watch for
01456       consecutive bursts of CRs and LFs in whatever
01457       pattern, then actually output the larger number 
01458       of LFs. Consistently sane, yet it still allows
01459       consecutive blank lines when the user
01460       actually intends them. */
01461     if ((ch == 13) || (ch == 10)) {
01462       if (ch == 13) {
01463         crCount++;
01464       } else {
01465         lfCount++;
01466       } 
01467     } else {
01468       if (crCount || lfCount) {
01469         int lfsAdd = crCount;
01470         if (lfCount > crCount) {
01471           lfsAdd = lfCount;
01472         }
01473         /* Stomp all newlines if desired */
01474         if (!newlines) {
01475           lfsAdd = 0;
01476         }
01477         while (lfsAdd) {
01478           if (len >= avail) {
01479             truncated = 1;
01480             break;
01481           }
01482           *dp = 10;
01483           dp++;
01484           lfsAdd--;
01485           len++;    
01486         }
01487         crCount = 0;
01488         lfCount = 0;
01489       }
01490       if (ch == '\0') {
01491         /* The end of the source string */
01492         break;        
01493       } 
01494       /* 1.06: check available space before adding
01495         the character, because a previously added
01496         LF may have brought us to the limit */
01497       if (len >= avail) {
01498         truncated = 1;
01499         break;
01500       }
01501       *dp = ch;
01502       dp++;
01503       len++;
01504     }
01505     sp++; 
01506   } 
01507   *dp = '\0';
01508   if (truncated) {
01509     return cgiFormTruncated;
01510   } else if (!len) {
01511     return cgiFormEmpty;
01512   } else {
01513     return cgiFormSuccess;
01514   }
01515 }
01516 
01517 static int cgiFirstNonspaceChar(char *s);
01518 
01519 cgiFormResultType cgiFormInteger(
01520         char *name, int *result, int defaultV) {
01521   cgiFormEntry *e;
01522   int ch;
01523   e = cgiFormEntryFindFirst(name);
01524   if (!e) {
01525     *result = defaultV;
01526     return cgiFormNotFound; 
01527   } 
01528   if (!strlen(e->value)) {
01529     *result = defaultV;
01530     return cgiFormEmpty;
01531   }
01532   ch = cgiFirstNonspaceChar(e->value);
01533   if (!(isdigit(ch)) && (ch != '-') && (ch != '+')) {
01534     *result = defaultV;
01535     return cgiFormBadType;
01536   } else {
01537     *result = atoi(e->value);
01538     return cgiFormSuccess;
01539   }
01540 }
01541 
01542 cgiFormResultType cgiFormIntegerBounded(
01543         char *name, int *result, int min, int max, int defaultV) {
01544   cgiFormResultType error = cgiFormInteger(name, result, defaultV);
01545   if (error != cgiFormSuccess) {
01546     return error;
01547   }
01548   if (*result < min) {
01549     *result = min;
01550     return cgiFormConstrained;
01551   } 
01552   if (*result > max) {
01553     *result = max;
01554     return cgiFormConstrained;
01555   } 
01556   return cgiFormSuccess;
01557 }
01558 
01559 cgiFormResultType cgiFormDouble(
01560         char *name, double *result, double defaultV) {
01561   cgiFormEntry *e;
01562   int ch;
01563   e = cgiFormEntryFindFirst(name);
01564   if (!e) {
01565     *result = defaultV;
01566     return cgiFormNotFound; 
01567   } 
01568   if (!strlen(e->value)) {
01569     *result = defaultV;
01570     return cgiFormEmpty;
01571   } 
01572   ch = cgiFirstNonspaceChar(e->value);
01573   if (!(isdigit(ch)) && (ch != '.') && (ch != '-') && (ch != '+')) {
01574     *result = defaultV;
01575     return cgiFormBadType;
01576   } else {
01577     *result = atof(e->value);
01578     return cgiFormSuccess;
01579   }
01580 }
01581 
01582 cgiFormResultType cgiFormDoubleBounded(
01583         char *name, double *result, double min, double max, double defaultV) {
01584   cgiFormResultType error = cgiFormDouble(name, result, defaultV);
01585   if (error != cgiFormSuccess) {
01586     return error;
01587   }
01588   if (*result < min) {
01589     *result = min;
01590     return cgiFormConstrained;
01591   } 
01592   if (*result > max) {
01593     *result = max;
01594     return cgiFormConstrained;
01595   } 
01596   return cgiFormSuccess;
01597 }
01598 
01599 cgiFormResultType cgiFormSelectSingle(
01600   char *name, char **choicesText, int choicesTotal, 
01601   int *result, int defaultV) 
01602 {
01603   cgiFormEntry *e;
01604   int i;
01605   e = cgiFormEntryFindFirst(name);
01606 #ifdef CGICDEBUG
01607   CGICDEBUGSTART
01608   fprintf(dout, "%d\n", (int) e);
01609   CGICDEBUGEND
01610 #endif /* CGICDEBUG */
01611   if (!e) {
01612     *result = defaultV;
01613     return cgiFormNotFound;
01614   }
01615   for (i=0; (i < choicesTotal); i++) {
01616 #ifdef CGICDEBUG
01617     CGICDEBUGSTART
01618     fprintf(dout, "%s %s\n", choicesText[i], e->value);
01619     CGICDEBUGEND
01620 #endif /* CGICDEBUG */
01621     if (cgiStrEq(choicesText[i], e->value)) {
01622 #ifdef CGICDEBUG
01623       CGICDEBUGSTART
01624       fprintf(dout, "MATCH\n");
01625       CGICDEBUGEND
01626 #endif /* CGICDEBUG */
01627       *result = i;
01628       return cgiFormSuccess;
01629     }
01630   }
01631   *result = defaultV;
01632   return cgiFormNoSuchChoice;
01633 }
01634 
01635 cgiFormResultType cgiFormSelectMultiple(
01636   char *name, char **choicesText, int choicesTotal, 
01637   int *result, int *invalid) 
01638 {
01639   cgiFormEntry *e;
01640   int i;
01641   int hits = 0;
01642   int invalidE = 0;
01643   for (i=0; (i < choicesTotal); i++) {
01644     result[i] = 0;
01645   }
01646   e = cgiFormEntryFindFirst(name);
01647   if (!e) {
01648     *invalid = invalidE;
01649     return cgiFormNotFound;
01650   }
01651   do {
01652     int hit = 0;
01653     for (i=0; (i < choicesTotal); i++) {
01654       if (cgiStrEq(choicesText[i], e->value)) {
01655         result[i] = 1;
01656         hits++;
01657         hit = 1;
01658         break;
01659       }
01660     }
01661     if (!(hit)) {
01662       invalidE++;
01663     }
01664   } while ((e = cgiFormEntryFindNext()) != 0);
01665 
01666   *invalid = invalidE;
01667 
01668   if (hits) {
01669     return cgiFormSuccess;
01670   } else {
01671     return cgiFormNotFound;
01672   }
01673 }
01674 
01675 cgiFormResultType cgiFormCheckboxSingle(
01676   char *name)
01677 {
01678   cgiFormEntry *e;
01679   e = cgiFormEntryFindFirst(name);
01680   if (!e) {
01681     return cgiFormNotFound;
01682   }
01683   return cgiFormSuccess;
01684 }
01685 
01686 extern cgiFormResultType cgiFormCheckboxMultiple(
01687   char *name, char **valuesText, int valuesTotal, 
01688   int *result, int *invalid)
01689 {
01690   /* Implementation is identical to cgiFormSelectMultiple. */
01691   return cgiFormSelectMultiple(name, valuesText, 
01692     valuesTotal, result, invalid);
01693 }
01694 
01695 cgiFormResultType cgiFormRadio(
01696   char *name, 
01697   char **valuesText, int valuesTotal, int *result, int defaultV)
01698 {
01699   /* Implementation is identical to cgiFormSelectSingle. */
01700   return cgiFormSelectSingle(name, valuesText, valuesTotal, 
01701     result, defaultV);
01702 }
01703 
01704 cgiFormResultType cgiCookieString(
01705   char *name,
01706   char *value,
01707   int space)
01708 {
01709   char *p = cgiCookie;
01710   while (*p) {
01711     char *n = name;
01712     /* 2.02: if cgiCookie is exactly equal to name, this
01713       can cause an overrun. The server probably wouldn't
01714       allow it, since a name without values makes no sense 
01715       -- but then again it might not check, so this is a
01716       genuine security concern. Thanks to Nicolas 
01717       Tomadakis. */
01718     while (*p == *n) {
01719       if ((p == '\0') && (n == '\0')) {
01720         /* Malformed cookie header from client */
01721         return cgiFormNotFound;
01722       }
01723       p++;
01724       n++;
01725     }
01726     if ((!*n) && (*p == '=')) {
01727       p++;
01728       while ((*p != ';') && (*p != '\0') &&
01729         (space > 1)) 
01730       {
01731         *value = *p;
01732         value++;
01733         p++;
01734         space--;
01735       }
01736       if (space > 0) {
01737         *value = '\0';
01738       }
01739       /* Correct parens: 2.02. Thanks to
01740         Mathieu Villeneuve-Belair. */
01741       if (!(((*p) == ';') || ((*p) == '\0')))
01742       {
01743         return cgiFormTruncated;
01744       } else {  
01745         return cgiFormSuccess;
01746       }
01747     } else {
01748       /* Skip to next cookie */ 
01749       while (*p) {
01750         if (*p == ';') {
01751           break;
01752         }
01753         p++;
01754       }
01755       if (!*p) {
01756         /* 2.01: default to empty */
01757         if (space) {
01758           *value = '\0';
01759         }
01760         return cgiFormNotFound;
01761       }
01762       p++;  
01763       /* Allow whitespace after semicolon */
01764       while ((*p) && isspace(*p)) {
01765         p++;
01766       } 
01767     }
01768   }
01769   /* 2.01: actually the above loop never terminates except
01770     with a return, but do this to placate gcc */
01771   if (space) {
01772     *value = '\0';
01773   }
01774   return cgiFormNotFound;
01775 }
01776 
01777 cgiFormResultType cgiCookieInteger(
01778   char *name,
01779   int *result,
01780   int defaultV)
01781 {
01782   char buffer[256];
01783   cgiFormResultType r = 
01784     cgiCookieString(name, buffer, sizeof(buffer));
01785   if (r != cgiFormSuccess) {
01786     *result = defaultV;
01787   } else {
01788     *result = atoi(buffer);
01789   }
01790   return r;
01791 }
01792 
01793 void cgiHeaderCookieSetInteger(char *name, int value, int secondsToLive,
01794   char *path, char *domain)
01795 {
01796   char svalue[256];
01797   sprintf(svalue, "%d", value);
01798   cgiHeaderCookieSetString(name, svalue, secondsToLive, path, domain);
01799 }
01800 
01801 char *days[] = {
01802   "Sun",
01803   "Mon",
01804   "Tue",
01805   "Wed",
01806   "Thu",
01807   "Fri",
01808   "Sat"
01809 };
01810 
01811 char *months[] = {
01812   "Jan",
01813   "Feb",
01814   "Mar",
01815   "Apr",
01816   "May",
01817   "Jun",
01818   "Jul",
01819   "Aug",
01820   "Sep",
01821   "Oct",
01822   "Nov",
01823   "Dec"
01824 };
01825 
01826 void cgiHeaderCookieSetString(char *name, char *value, int secondsToLive,
01827   char *path, char *domain)
01828 {
01829   /* cgic 2.02: simpler and more widely compatible implementation.
01830     Thanks to Chunfu Lai. 
01831      cgic 2.03: yes, but it didn't work. Reimplemented by
01832     Thomas Boutell. ; after last element was a bug. 
01833      Examples of real world cookies that really work:
01834        Set-Cookie: MSNADS=UM=; domain=.slate.com; 
01835              expires=Tue, 26-Apr-2022 19:00:00 GMT; path=/
01836      Set-Cookie: MC1=V=3&ID=b5bc08af2b8a43ff85fcb5efd8b238f0; 
01837              domain=.slate.com; expires=Mon, 04-Oct-2021 19:00:00 GMT; path=/
01838   */
01839   time_t now;
01840   time_t then;
01841   struct tm *gt;
01842   time(&now);
01843   then = now + secondsToLive;
01844   gt = gmtime(&then);
01845   fprintf(cgiOut, 
01846     "Set-Cookie: %s=%s; domain=%s; expires=%s, %02d-%s-%04d %02d:%02d:%02d GMT; path=%s\r\n",
01847     name, value, domain, 
01848     days[gt->tm_wday],
01849     gt->tm_mday,
01850     months[gt->tm_mon],
01851     gt->tm_year + 1900,   
01852     gt->tm_hour,
01853     gt->tm_min,
01854     gt->tm_sec,
01855     path);
01856 }
01857 
01858 void cgiHeaderLocation(char *redirectUrl) {
01859   fprintf(cgiOut, "Location: %s\r\n\r\n", redirectUrl);
01860 }
01861 
01862 void cgiHeaderStatus(int status, char *statusMessage) {
01863   fprintf(cgiOut, "Status: %d %s\r\n\r\n", status, statusMessage);
01864 }
01865 
01866 void cgiHeaderContentType(char *mimeType) {
01867   fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType);
01868 }
01869 
01870 static int cgiWriteString(FILE *out, char *s);
01871 
01872 static int cgiWriteInt(FILE *out, int i);
01873 
01874 #define CGIC_VERSION "2.0"
01875 
01876 cgiEnvironmentResultType cgiWriteEnvironment(char *filename) {
01877   FILE *out;
01878   cgiFormEntry *e;
01879   /* Be sure to open in binary mode */
01880   out = fopen(filename, "wb");
01881   if (!out) {
01882     /* Can't create file */
01883     return cgiEnvironmentIO;
01884   }
01885   if (!cgiWriteString(out, "CGIC2.0")) {
01886     goto error;
01887   }
01888   if (!cgiWriteString(out, cgiServerSoftware)) {
01889     goto error;
01890   }
01891   if (!cgiWriteString(out, cgiServerName)) {
01892     goto error;
01893   }
01894   if (!cgiWriteString(out, cgiGatewayInterface)) {
01895     goto error;
01896   }
01897   if (!cgiWriteString(out, cgiServerProtocol)) {
01898     goto error;
01899   }
01900   if (!cgiWriteString(out, cgiServerPort)) {
01901     goto error;
01902   }
01903   if (!cgiWriteString(out, cgiRequestMethod)) {
01904     goto error;
01905   }
01906   if (!cgiWriteString(out, cgiPathInfo)) {
01907     goto error;
01908   }
01909   if (!cgiWriteString(out, cgiPathTranslated)) {
01910     goto error;
01911   }
01912   if (!cgiWriteString(out, cgiScriptName)) {
01913     goto error;
01914   }
01915   if (!cgiWriteString(out, cgiQueryString)) {
01916     goto error;
01917   }
01918   if (!cgiWriteString(out, cgiRemoteHost)) {
01919     goto error;
01920   }
01921   if (!cgiWriteString(out, cgiRemoteAddr)) {
01922     goto error;
01923   }
01924   if (!cgiWriteString(out, cgiAuthType)) {
01925     goto error;
01926   }
01927   if (!cgiWriteString(out, cgiRemoteUser)) {
01928     goto error;
01929   }
01930   if (!cgiWriteString(out, cgiRemoteIdent)) {
01931     goto error;
01932   }
01933   if (!cgiWriteString(out, cgiContentType)) {
01934     goto error;
01935   }
01936   if (!cgiWriteString(out, cgiAccept)) {
01937     goto error;
01938   }
01939   if (!cgiWriteString(out, cgiUserAgent)) {
01940     goto error;
01941   }
01942   if (!cgiWriteString(out, cgiReferrer)) {
01943     goto error;
01944   }
01945   if (!cgiWriteString(out, cgiCookie)) {
01946     goto error;
01947   }
01948   if (!cgiWriteInt(out, cgiContentLength)) {
01949     goto error;
01950   }
01951   e = cgiFormEntryFirst;
01952   while (e) {
01953     cgiFilePtr fp;
01954     if (!cgiWriteString(out, e->attr)) {
01955       goto error;
01956     }
01957     if (!cgiWriteString(out, e->value)) {
01958       goto error;
01959     }
01960     /* New 2.0 fields and file uploads */
01961     if (!cgiWriteString(out, e->fileName)) {
01962       goto error;
01963     }
01964     if (!cgiWriteString(out, e->contentType)) {
01965       goto error;
01966     }
01967     if (!cgiWriteInt(out, e->valueLength)) {
01968       goto error;
01969     }
01970     if (cgiFormFileOpen(e->attr, &fp) == cgiFormSuccess) {
01971       char buffer[1024];
01972       int got;
01973       if (!cgiWriteInt(out, 1)) {
01974         cgiFormFileClose(fp);
01975         goto error;
01976       }
01977       while (cgiFormFileRead(fp, buffer, 
01978         sizeof(buffer), &got) == cgiFormSuccess)
01979       {
01980         if (((int) fwrite(buffer, 1, got, out)) != got) {
01981           cgiFormFileClose(fp);
01982           goto error;
01983         }
01984       }
01985       if (cgiFormFileClose(fp) != cgiFormSuccess) {
01986         goto error;
01987       }
01988     } else {
01989       if (!cgiWriteInt(out, 0)) {
01990         goto error;
01991       }
01992     }
01993     e = e->next;
01994   }
01995   fclose(out);
01996   return cgiEnvironmentSuccess;
01997 error:
01998   fclose(out);
01999   /* If this function is not defined in your system,
02000     you must substitute the appropriate 
02001     file-deletion function. */
02002   unlink(filename);
02003   return cgiEnvironmentIO;
02004 }
02005 
02006 static int cgiWriteString(FILE *out, char *s) {
02007   int len = (int) strlen(s);
02008   cgiWriteInt(out, len);
02009   if (((int) fwrite(s, 1, len, out)) != len) {
02010     return 0;
02011   }
02012   return 1;
02013 }
02014 
02015 static int cgiWriteInt(FILE *out, int i) {
02016   if (!fwrite(&i, sizeof(int), 1, out)) {
02017     return 0;
02018   }
02019   return 1;
02020 }
02021 
02022 static int cgiReadString(FILE *out, char **s);
02023 
02024 static int cgiReadInt(FILE *out, int *i);
02025 
02026 cgiEnvironmentResultType cgiReadEnvironment(char *filename) {
02027   FILE *in;
02028   cgiFormEntry *e = 0, *p;
02029   char *version;
02030   /* Prevent compiler warnings */
02031   cgiEnvironmentResultType result = cgiEnvironmentIO;
02032   /* Free any existing data first */
02033   cgiFreeResources();
02034   /* Be sure to open in binary mode */
02035   in = fopen(filename, "rb");
02036   if (!in) {
02037     /* Can't access file */
02038     return cgiEnvironmentIO;
02039   }
02040   if (!cgiReadString(in, &version)) {
02041     goto error;
02042   }
02043   if (strcmp(version, "CGIC" CGIC_VERSION)) {
02044     /* 2.02: Merezko Oleg */
02045     free(version);
02046     return cgiEnvironmentWrongVersion;
02047   } 
02048   /* 2.02: Merezko Oleg */
02049   free(version);
02050   if (!cgiReadString(in, &cgiServerSoftware)) {
02051     goto error;
02052   }
02053   if (!cgiReadString(in, &cgiServerName)) {
02054     goto error;
02055   }
02056   if (!cgiReadString(in, &cgiGatewayInterface)) {
02057     goto error;
02058   }
02059   if (!cgiReadString(in, &cgiServerProtocol)) {
02060     goto error;
02061   }
02062   if (!cgiReadString(in, &cgiServerPort)) {
02063     goto error;
02064   }
02065   if (!cgiReadString(in, &cgiRequestMethod)) {
02066     goto error;
02067   }
02068   if (!cgiReadString(in, &cgiPathInfo)) {
02069     goto error;
02070   }
02071   if (!cgiReadString(in, &cgiPathTranslated)) {
02072     goto error;
02073   }
02074   if (!cgiReadString(in, &cgiScriptName)) {
02075     goto error;
02076   }
02077   if (!cgiReadString(in, &cgiQueryString)) {
02078     goto error;
02079   }
02080   if (!cgiReadString(in, &cgiRemoteHost)) {
02081     goto error;
02082   }
02083   if (!cgiReadString(in, &cgiRemoteAddr)) {
02084     goto error;
02085   }
02086   if (!cgiReadString(in, &cgiAuthType)) {
02087     goto error;
02088   }
02089   if (!cgiReadString(in, &cgiRemoteUser)) {
02090     goto error;
02091   }
02092   if (!cgiReadString(in, &cgiRemoteIdent)) {
02093     goto error;
02094   }
02095   if (!cgiReadString(in, &cgiContentType)) {
02096     goto error;
02097   }
02098   if (!cgiReadString(in, &cgiAccept)) {
02099     goto error;
02100   }
02101   if (!cgiReadString(in, &cgiUserAgent)) {
02102     goto error;
02103   }
02104   if (!cgiReadString(in, &cgiReferrer)) {
02105     goto error;
02106   }
02107   /* 2.0 */
02108   if (!cgiReadString(in, &cgiCookie)) {
02109     goto error;
02110   }
02111   if (!cgiReadInt(in, &cgiContentLength)) {
02112     goto error;
02113   }
02114   p = 0;
02115   while (1) {
02116     int fileFlag;
02117     e = (cgiFormEntry *) calloc(1, sizeof(cgiFormEntry));
02118     if (!e) {
02119       cgiFreeResources();
02120       fclose(in);
02121       return cgiEnvironmentMemory;
02122     }
02123     memset(e, 0, sizeof(cgiFormEntry));
02124     if (!cgiReadString(in, &e->attr)) {
02125       /* This means we've reached the end of the list. */
02126       /* 2.02: thanks to Merezko Oleg */
02127       free(e);
02128       break;
02129     }
02130     if (!cgiReadString(in, &e->value)) {
02131       goto outOfMemory;
02132     }
02133     if (!cgiReadString(in, &e->fileName)) {
02134       goto outOfMemory;
02135     }
02136     if (!cgiReadString(in, &e->contentType)) {
02137       goto outOfMemory;
02138     }
02139     if (!cgiReadInt(in, &e->valueLength)) {
02140       goto outOfMemory;
02141     }
02142     if (!cgiReadInt(in, &fileFlag)) {
02143       goto outOfMemory;
02144     }
02145     if (fileFlag) {
02146       char buffer[1024];
02147       FILE *out;
02148       char tfileName[1024];
02149       int got;
02150       int len = e->valueLength;
02151       if (getTempFileName(tfileName)
02152         != cgiParseSuccess)
02153       {
02154         result = cgiEnvironmentIO;
02155         goto error;
02156       }
02157       out = fopen(tfileName, "w+b");
02158       if (!out) {
02159         result = cgiEnvironmentIO;
02160         goto error;
02161       }
02162       while (len > 0) {   
02163         /* 2.01: try is a bad variable name in
02164           C++, and it wasn't being used
02165           properly either */
02166         int tryr = len;
02167         if (tryr > ((int) sizeof(buffer))) {
02168           tryr = sizeof(buffer);
02169         }
02170         got = fread(buffer, 1, tryr, in);
02171         if (got <= 0) {
02172           result = cgiEnvironmentIO;
02173           fclose(out);
02174           unlink(tfileName);
02175           goto error;
02176         }
02177         if (((int) fwrite(buffer, 1, got, out)) != got) {
02178           result = cgiEnvironmentIO;
02179           fclose(out);
02180           unlink(tfileName);
02181           goto error;
02182         }
02183         len -= got;
02184       }
02185       fclose(out);
02186       e->tfileName = (char *) malloc((int) strlen(tfileName) + 1);
02187       if (!e->tfileName) {
02188         result = cgiEnvironmentMemory;
02189         unlink(tfileName);
02190         goto error;
02191       }
02192       strcpy(e->tfileName, tfileName);
02193     } else {
02194       e->tfileName = (char *) malloc(1);
02195       if (!e->tfileName) {
02196         result = cgiEnvironmentMemory;
02197         goto error;
02198       }
02199     } 
02200     e->next = 0;
02201     if (p) {
02202       p->next = e;
02203     } else {
02204       cgiFormEntryFirst = e;
02205     } 
02206     p = e;
02207   }
02208   fclose(in);
02209   cgiRestored = 1;
02210   return cgiEnvironmentSuccess;
02211 outOfMemory:
02212   result = cgiEnvironmentMemory;
02213 error:
02214   cgiFreeResources();
02215   fclose(in);
02216   if (e) {
02217     if (e->attr) {
02218       free(e->attr);
02219     }
02220     if (e->value) {
02221       free(e->value);
02222     }
02223     if (e->fileName) {
02224       free(e->fileName);
02225     }
02226     if (e->contentType) {
02227       free(e->contentType);
02228     }
02229     if (e->tfileName) {
02230       free(e->tfileName);
02231     }
02232     free(e);
02233   }
02234   return result;
02235 }
02236 
02237 static int cgiReadString(FILE *in, char **s) {
02238   int len;
02239   /* 2.0 fix: test cgiReadInt for failure! */ 
02240   if (!cgiReadInt(in, &len)) {
02241     return 0;
02242   }
02243   *s = (char *) malloc(len + 1);
02244   if (!(*s)) {
02245     return 0;
02246   } 
02247   if (((int) fread(*s, 1, len, in)) != len) {
02248     return 0;
02249   }
02250   (*s)[len] = '\0';
02251   return 1;
02252 }
02253 
02254 static int cgiReadInt(FILE *out, int *i) {
02255   if (!fread(i, sizeof(int), 1, out)) {
02256     return 0;
02257   }
02258   return 1;
02259 }
02260 
02261 static int cgiStrEqNc(char *s1, char *s2) {
02262   while(1) {
02263     if (!(*s1)) {
02264       if (!(*s2)) {
02265         return 1;
02266       } else {
02267         return 0;
02268       }
02269     } else if (!(*s2)) {
02270       return 0;
02271     }
02272     if (isalpha(*s1)) {
02273       if (tolower(*s1) != tolower(*s2)) {
02274         return 0;
02275       }
02276     } else if ((*s1) != (*s2)) {
02277       return 0;
02278     }
02279     s1++;
02280     s2++;
02281   }
02282 }
02283 
02284 static int cgiStrBeginsNc(char *s1, char *s2) {
02285   while(1) {
02286     if (!(*s2)) {
02287       return 1;
02288     } else if (!(*s1)) {
02289       return 0;
02290     }
02291     if (isalpha(*s1)) {
02292       if (tolower(*s1) != tolower(*s2)) {
02293         return 0;
02294       }
02295     } else if ((*s1) != (*s2)) {
02296       return 0;
02297     }
02298     s1++;
02299     s2++;
02300   }
02301 }
02302 
02303 static char *cgiFindTarget = 0;
02304 static cgiFormEntry *cgiFindPos = 0;
02305 
02306 static cgiFormEntry *cgiFormEntryFindFirst(char *name) {
02307   cgiFindTarget = name;
02308   cgiFindPos = cgiFormEntryFirst;
02309   return cgiFormEntryFindNext();
02310 }
02311 
02312 static cgiFormEntry *cgiFormEntryFindNext() {
02313   while (cgiFindPos) {
02314     cgiFormEntry *c = cgiFindPos;
02315     cgiFindPos = c->next;
02316     if (!strcmp(c -> attr, cgiFindTarget)) {
02317       return c;
02318     }
02319   }
02320   return 0;
02321 }
02322 
02323 static int cgiFirstNonspaceChar(char *s) {
02324   int len = strspn(s, " \n\r\t");
02325   return s[len];
02326 }
02327 
02328 void cgiStringArrayFree(char **stringArray) {
02329   char *p;
02330   char **arrayItself = stringArray;
02331   p = *stringArray;
02332   while (p) {
02333     free(p);
02334     stringArray++;
02335     p = *stringArray;
02336   }
02337   /* 2.0: free the array itself! */
02338   free(arrayItself);
02339 } 
02340 
02341 cgiFormResultType cgiCookies(char ***result) {
02342   char **stringArray;
02343   int i;
02344   int total = 0;
02345   char *p;
02346   char *n;
02347   p = cgiCookie;
02348   while (*p) {
02349     if (*p == '=') {
02350       total++;
02351     }
02352     p++;
02353   }
02354   stringArray = (char **) malloc(sizeof(char *) * (total + 1));
02355   if (!stringArray) {
02356     *result = 0;
02357     return cgiFormMemory;
02358   }
02359   /* initialize all entries to null; the last will stay that way */
02360   for (i=0; (i <= total); i++) {
02361     stringArray[i] = 0;
02362   }
02363   i = 0;
02364   p = cgiCookie;
02365   while (*p) {
02366     while (*p && isspace(*p)) {
02367       p++;
02368     }
02369     n = p;
02370     while (*p && (*p != '=')) {
02371       p++;
02372     }
02373     if (p != n) {
02374       stringArray[i] = (char *) malloc((p - n) + 1);
02375       if (!stringArray[i]) {
02376         cgiStringArrayFree(stringArray);
02377         *result = 0;
02378         return cgiFormMemory;
02379       } 
02380       memcpy(stringArray[i], n, p - n);
02381       stringArray[i][p - n] = '\0';
02382       i++;
02383     }
02384     while (*p && (*p != ';')) {
02385       p++;  
02386     }
02387     if (!*p) {
02388       break;
02389     }
02390     if (*p == ';') {
02391       p++;
02392     }
02393   }
02394   *result = stringArray;
02395   return cgiFormSuccess;
02396 }
02397 
02398 cgiFormResultType cgiFormEntries(char ***result) {
02399   char **stringArray;
02400   cgiFormEntry *e, *pe;
02401   int i;
02402   int total = 0;
02403   e = cgiFormEntryFirst;
02404   while (e) {
02405     /* Don't count a field name more than once if
02406       multiple values happen to be present for it */
02407     pe = cgiFormEntryFirst;
02408     while (pe != e) {
02409       if (!strcmp(e->attr, pe->attr)) {
02410         goto skipSecondValue;
02411       }
02412       pe = pe->next;          
02413     }
02414     total++;
02415 skipSecondValue:
02416     e = e->next;
02417   }
02418   stringArray = (char **) malloc(sizeof(char *) * (total + 1));
02419   if (!stringArray) {
02420     *result = 0;
02421     return cgiFormMemory;
02422   }
02423   /* initialize all entries to null; the last will stay that way */
02424   for (i=0; (i <= total); i++) {
02425     stringArray[i] = 0;
02426   }
02427   /* Now go get the entries */
02428   e = cgiFormEntryFirst;
02429   i = 0;
02430   while (e) {
02431     int space;
02432     /* Don't return a field name more than once if
02433       multiple values happen to be present for it */
02434     pe = cgiFormEntryFirst;
02435     while (pe != e) {
02436       if (!strcmp(e->attr, pe->attr)) {
02437         goto skipSecondValue2;
02438       }
02439       pe = pe->next;          
02440     }   
02441     space = (int) strlen(e->attr) + 1;
02442     stringArray[i] = (char *) malloc(space);
02443     if (stringArray[i] == 0) {
02444       /* Memory problems */
02445       cgiStringArrayFree(stringArray);
02446       *result = 0;
02447       return cgiFormMemory;
02448     } 
02449     strcpy(stringArray[i], e->attr);
02450     i++;
02451 skipSecondValue2:
02452     e = e->next;
02453   }
02454   *result = stringArray;
02455   return cgiFormSuccess;
02456 }
02457 
02458 #define TRYPUTC(ch) \
02459   { \
02460     if (putc((ch), cgiOut) == EOF) { \
02461       return cgiFormIO; \
02462     } \
02463   } 
02464 
02465 cgiFormResultType cgiHtmlEscapeData(char *data, int len)
02466 {
02467   while (len--) {
02468     if (*data == '<') {
02469       TRYPUTC('&');
02470       TRYPUTC('l');
02471       TRYPUTC('t');
02472       TRYPUTC(';');
02473     } else if (*data == '&') {
02474       TRYPUTC('&');
02475       TRYPUTC('a');
02476       TRYPUTC('m');
02477       TRYPUTC('p');
02478       TRYPUTC(';');
02479     } else if (*data == '>') {
02480       TRYPUTC('&');
02481       TRYPUTC('g');
02482       TRYPUTC('t');
02483       TRYPUTC(';');
02484     } else {
02485       TRYPUTC(*data);
02486     }
02487     data++;
02488   }
02489   return cgiFormSuccess;
02490 }
02491 
02492 cgiFormResultType cgiHtmlEscape(char *s)
02493 {
02494   return cgiHtmlEscapeData(s, (int) strlen(s));
02495 }
02496 
02497 /* Output data with the " character HTML-escaped, and no
02498   other characters escaped. This is useful when outputting
02499   the contents of a tag attribute such as 'href' or 'src'.
02500   'data' is not null-terminated; 'len' is the number of
02501   bytes in 'data'. Returns cgiFormIO in the event
02502   of error, cgiFormSuccess otherwise. */
02503 cgiFormResultType cgiValueEscapeData(char *data, int len)
02504 {
02505   while (len--) {
02506     if (*data == '\"') {
02507       TRYPUTC('&');
02508       TRYPUTC('#');
02509       TRYPUTC('3');
02510       TRYPUTC('4');
02511       TRYPUTC(';');
02512     } else {
02513       TRYPUTC(*data);
02514     }
02515     data++;
02516   }
02517   return cgiFormSuccess;
02518 }
02519 
02520 cgiFormResultType cgiValueEscape(char *s)
02521 {
02522   return cgiValueEscapeData(s, (int) strlen(s));
02523 }
02524 
02525 

Generated on Thu Oct 28 10:59:06 2004 for gitk by doxygen 1.3.6