Installer import into contrib (real import this time)
[dragonfly.git] / contrib / bsdinstaller-1.1.6 / src / frontends / cgi / cgic.c
1 /* cgicTempDir is the only setting you are likely to need
2         to change in this file. */
3
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. */
8
9 #define cgicTempDir "/tmp"
10
11 #if CGICDEBUG
12 #define CGICDEBUGSTART \
13         { \
14                 FILE *dout; \
15                 dout = fopen("/home/boutell/public_html/debug", "a"); \
16         
17 #define CGICDEBUGEND \
18                 fclose(dout); \
19         }
20 #else /* CGICDEBUG */
21 #define CGICDEBUGSTART
22 #define CGICDEBUGEND
23 #endif /* CGICDEBUG */
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include <time.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32
33 #ifdef WIN32
34 #include <io.h>
35
36 /* cgic 2.01 */
37 #include <fcntl.h>
38
39 #else
40 #include <unistd.h>
41 #endif /* WIN32 */
42 #include "cgic.h"
43
44 #define cgiStrEq(a, b) (!strcmp((a), (b)))
45
46 char *cgiServerSoftware;
47 char *cgiServerName;
48 char *cgiGatewayInterface;
49 char *cgiServerProtocol;
50 char *cgiServerPort;
51 char *cgiRequestMethod;
52 char *cgiPathInfo;
53 char *cgiPathTranslated;
54 char *cgiScriptName;
55 char *cgiQueryString;
56 char *cgiRemoteHost;
57 char *cgiRemoteAddr;
58 char *cgiAuthType;
59 char *cgiRemoteUser;
60 char *cgiRemoteIdent;
61 char cgiContentTypeData[1024];
62 char *cgiContentType = cgiContentTypeData;
63 char *cgiMultipartBoundary;
64 char *cgiCookie;
65 int cgiContentLength;
66 char *cgiAccept;
67 char *cgiUserAgent;
68 char *cgiReferrer;
69
70 FILE *cgiIn;
71 FILE *cgiOut;
72
73 /* True if CGI environment was restored from a file. */
74 static int cgiRestored = 0;
75
76 static void cgiGetenv(char **s, char *var);
77
78 typedef enum {
79         cgiParseSuccess,
80         cgiParseMemory,
81         cgiParseIO
82 } cgiParseResultType;
83
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. */
91
92 typedef struct cgiFormEntryStruct {
93         char *attr;
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. */ 
97         char *value;
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. */
102         int valueLength;
103         char *fileName; 
104         char *contentType;
105         /* Temporary file name for working storage of file uploads. */
106         char *tfileName;
107         struct cgiFormEntryStruct *next;
108 } cgiFormEntry;
109
110 /* The first form entry. */
111 static cgiFormEntry *cgiFormEntryFirst;
112
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);
121
122 int main(int argc, char *argv[]) {
123         int result;
124         char *cgiContentLengthString;
125         char *e;
126         cgiSetupConstants();
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");
145         if (e) {
146                 if (strlen(e) < sizeof(cgiContentTypeData)) {
147                         strcpy(cgiContentType, e);
148                 } else {
149                         /* Truncate safely in the event of what is almost certainly
150                                 a hack attempt */
151                         strncpy(cgiContentType, e, sizeof(cgiContentTypeData));
152                         cgiContentType[sizeof(cgiContentTypeData) - 1] = '\0';
153                 }
154         } else {
155                 cgiContentType[0] = '\0';
156         }
157         /* Never null */
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, ';');
165                 while (sat) {
166                         *sat = '\0';
167                         sat++;
168                         while (isspace(*sat)) {
169                                 sat++;
170                         }       
171                         if (cgiStrBeginsNc(sat, "boundary=")) {
172                                 char *s;
173                                 cgiMultipartBoundary = sat + strlen("boundary=");
174                                 s = cgiMultipartBoundary;
175                                 while ((*s) && (!isspace(*s))) {
176                                         s++;
177                                 }
178                                 *s = '\0';
179                                 break;
180                         } else {
181                                 sat = strchr(sat, ';');
182                         }       
183                 }
184         }
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");
191 #ifdef CGICDEBUG
192         CGICDEBUGSTART
193         fprintf(dout, "%d\n", cgiContentLength);
194         fprintf(dout, "%s\n", cgiRequestMethod);
195         fprintf(dout, "%s\n", cgiContentType);
196         CGICDEBUGEND    
197 #endif /* CGICDEBUG */
198 #ifdef WIN32
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 );
203 #endif /* WIN32 */
204         cgiFormEntryFirst = 0;
205         cgiIn = stdin;
206         cgiOut = stdout;
207         cgiRestored = 0;
208
209
210         /* These five lines keep compilers from
211                 producing warnings that argc and argv
212                 are unused. They have no actual function. */
213         if (argc) {
214                 if (argv[0]) {
215                         cgiRestored = 0;
216                 }
217         }       
218
219
220         if (cgiStrEqNc(cgiRequestMethod, "post")) {
221 #ifdef CGICDEBUG
222                 CGICDEBUGSTART
223                 fprintf(dout, "POST recognized\n");
224                 CGICDEBUGEND
225 #endif /* CGICDEBUG */
226                 if (cgiStrEqNc(cgiContentType, "application/x-www-form-urlencoded")) {  
227 #ifdef CGICDEBUG
228                         CGICDEBUGSTART
229                         fprintf(dout, "Calling PostFormInput\n");
230                         CGICDEBUGEND    
231 #endif /* CGICDEBUG */
232                         if (cgiParsePostFormInput() != cgiParseSuccess) {
233 #ifdef CGICDEBUG
234                                 CGICDEBUGSTART
235                                 fprintf(dout, "PostFormInput failed\n");
236                                 CGICDEBUGEND    
237 #endif /* CGICDEBUG */
238                                 cgiFreeResources();
239                                 return -1;
240                         }       
241 #ifdef CGICDEBUG
242                         CGICDEBUGSTART
243                         fprintf(dout, "PostFormInput succeeded\n");
244                         CGICDEBUGEND    
245 #endif /* CGICDEBUG */
246                 } else if (cgiStrEqNc(cgiContentType, "multipart/form-data")) {
247 #ifdef CGICDEBUG
248                         CGICDEBUGSTART
249                         fprintf(dout, "Calling PostMultipartInput\n");
250                         CGICDEBUGEND    
251 #endif /* CGICDEBUG */
252                         if (cgiParsePostMultipartInput() != cgiParseSuccess) {
253 #ifdef CGICDEBUG
254                                 CGICDEBUGSTART
255                                 fprintf(dout, "PostMultipartInput failed\n");
256                                 CGICDEBUGEND    
257 #endif /* CGICDEBUG */
258                                 cgiFreeResources();
259                                 return -1;
260                         }       
261 #ifdef CGICDEBUG
262                         CGICDEBUGSTART
263                         fprintf(dout, "PostMultipartInput succeeded\n");
264                         CGICDEBUGEND    
265 #endif /* CGICDEBUG */
266                 }
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) {
272 #ifdef CGICDEBUG
273                         CGICDEBUGSTART
274                         fprintf(dout, "GetFormInput failed\n");
275                         CGICDEBUGEND    
276 #endif /* CGICDEBUG */
277                         cgiFreeResources();
278                         return -1;
279                 } else {        
280 #ifdef CGICDEBUG
281                         CGICDEBUGSTART
282                         fprintf(dout, "GetFormInput succeeded\n");
283                         CGICDEBUGEND    
284 #endif /* CGICDEBUG */
285                 }
286         }
287         result = cgiMain();
288         cgiFreeResources();
289         return result;
290 }
291
292 static void cgiGetenv(char **s, char *var){
293         *s = getenv(var);
294         if (!(*s)) {
295                 *s = "";
296         }
297 }
298
299 static cgiParseResultType cgiParsePostFormInput() {
300         char *input;
301         cgiParseResultType result;
302         if (!cgiContentLength) {
303                 return cgiParseSuccess;
304         }
305         input = (char *) malloc(cgiContentLength);
306         if (!input) {
307                 return cgiParseMemory;  
308         }
309         if (((int) fread(input, 1, cgiContentLength, cgiIn)) 
310                 != cgiContentLength) 
311         {
312                 return cgiParseIO;
313         }       
314         result = cgiParseFormInput(input, cgiContentLength);
315         free(input);
316         return result;
317 }
318
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. */
322
323 typedef struct {
324         /* Buffer for putting characters back */
325         char putback[1024];     
326         /* Position in putback from which next character will be read.
327                 If readPos == writePos, then next character should
328                 come from cgiIn. */
329         int readPos;
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. */
335         int writePos;
336         /* Offset in the virtual datastream; can be compared
337                 to cgiContentLength */
338         int offset;
339 } mpStream, *mpStreamPtr;
340
341 int mpRead(mpStreamPtr mpp, char *buffer, int len)
342 {
343         int ilen = len;
344         int got = 0;
345         while (len) {
346                 if (mpp->readPos != mpp->writePos) {
347                         *buffer++ = mpp->putback[mpp->readPos++];
348                         mpp->readPos %= sizeof(mpp->putback);
349                         got++;
350                         len--;
351                 } else {
352                         break;
353                 }       
354         }
355         /* Refuse to read past the declared length in order to
356                 avoid deadlock */
357         if (len > (cgiContentLength - mpp->offset)) {
358                 len = cgiContentLength - mpp->offset;
359         }
360         if (len) {
361                 int fgot = fread(buffer, 1, len, cgiIn);
362                 if (fgot >= 0) {
363                         mpp->offset += (got + fgot);
364                         return got + fgot;
365                 } else if (got > 0) {
366                         mpp->offset += got;
367                         return got;
368                 } else {
369                         /* EOF or error */
370                         return fgot;
371                 }
372         } else if (got) {
373                 return got;
374         } else if (ilen) {      
375                 return EOF;
376         } else {
377                 /* 2.01 */
378                 return 0;
379         }
380 }
381
382 void mpPutBack(mpStreamPtr mpp, char *data, int len)
383 {
384         mpp->offset -= len;
385         while (len) {
386                 mpp->putback[mpp->writePos++] = *data++;
387                 mpp->writePos %= sizeof(mpp->putback);
388                 len--;
389         }
390 }
391
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 
404         or cgiParseIO. */
405
406 static cgiParseResultType afterNextBoundary(mpStreamPtr mpp,
407         FILE *outf,
408         char **outP,
409         int *bodyLengthP,
410         int first
411         );
412
413 static int readHeaderLine(
414         mpStreamPtr mpp,        
415         char *attr,
416         int attrSpace,
417         char *value,
418         int valueSpace);
419
420 static void decomposeValue(char *value,
421         char *mvalue, int mvalueSpace,
422         char **argNames,
423         char **argValues,
424         int argValueSpace);
425
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. */
431         
432 static cgiParseResultType getTempFileName(char *tfileName);
433
434 static cgiParseResultType cgiParsePostMultipartInput() {
435         cgiParseResultType result;
436         cgiFormEntry *n = 0, *l = 0;
437         int got;
438         FILE *outf = 0;
439         char *out = 0;
440         char tfileName[1024];
441         mpStream mp;
442         mpStreamPtr mpp = &mp;
443         memset(&mp, 0, sizeof(mp));
444         if (!cgiContentLength) {
445                 return cgiParseSuccess;
446         }
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) {
453                 return result;
454         }
455         while (1) {
456                 char d[1024];
457                 char fvalue[1024];
458                 char fname[1024];
459                 int bodyLength = 0;
460                 char ffileName[1024];
461                 char fcontentType[1024];
462                 char attr[1024];
463                 char value[1024];
464                 fvalue[0] = 0;
465                 fname[0] = 0;
466                 ffileName[0] = 0;
467                 fcontentType[0] = 0;
468                 out = 0;
469                 outf = 0;
470                 /* Check for EOF */
471                 got = mpRead(mpp, d, 2);
472                 if (got < 2) {
473                         /* Crude EOF */
474                         break;
475                 }
476                 if ((d[0] == '-') && (d[1] == '-')) {
477                         /* Graceful EOF */
478                         break;
479                 }
480                 mpPutBack(mpp, d, 2);
481                 /* Read header lines until end of header */
482                 while (readHeaderLine(
483                                 mpp, attr, sizeof(attr), value, sizeof(value))) 
484                 {
485                         char *argNames[3];
486                         char *argValues[2];
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";
492                                 argNames[2] = 0;
493                                 argValues[0] = fname;
494                                 argValues[1] = ffileName;
495                                 decomposeValue(value, 
496                                         fvalue, sizeof(fvalue),
497                                         argNames,
498                                         argValues,
499                                         1024);  
500                         } else if (cgiStrEqNc(attr, "Content-Type")) {
501                                 argNames[0] = 0;
502                                 decomposeValue(value, 
503                                         fcontentType, sizeof(fcontentType),
504                                         argNames,
505                                         0,
506                                         0);
507                         }
508                 }
509                 if (!cgiStrEqNc(fvalue, "form-data")) {
510                         /* Not form data */     
511                         continue;
512                 }
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) {
521                                 return cgiParseIO;
522                         }       
523                         outf = fopen(tfileName, "w+b");
524                 } else {
525                         outf = 0;
526                         tfileName[0] = '\0';
527                 }       
528                 result = afterNextBoundary(mpp, outf, &out, &bodyLength, 0);
529                 if (result != cgiParseSuccess) {
530                         /* Lack of a boundary here is an error. */
531                         if (outf) {
532                                 fclose(outf);
533                                 unlink(tfileName);
534                         }
535                         if (out) {
536                                 free(out);
537                         }
538                         return result;
539                 }
540                 /* OK, we have a new pair, add it to the list. */
541                 n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));      
542                 if (!n) {
543                         goto outOfMemory;
544                 }
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);
549                 if (!n->attr) {
550                         goto outOfMemory;
551                 }
552                 strcpy(n->attr, fname);
553                 if (out) {
554                         n->value = out;
555                         out = 0;
556                 } else if (outf) {
557                         n->value = (char *) malloc(1);
558                         if (!n->value) {
559                                 goto outOfMemory;
560                         }
561                         n->value[0] = '\0';
562                         fclose(outf);
563                 }
564                 n->valueLength = bodyLength;
565                 n->next = 0;
566                 if (!l) {
567                         cgiFormEntryFirst = n;
568                 } else {
569                         l->next = n;
570                 }
571                 n->fileName = (char *) malloc(strlen(ffileName) + 1);
572                 if (!n->fileName) {
573                         goto outOfMemory;
574                 }
575                 strcpy(n->fileName, ffileName);
576                 n->contentType = (char *) malloc(strlen(fcontentType) + 1);
577                 if (!n->contentType) {
578                         goto outOfMemory;
579                 }
580                 strcpy(n->contentType, fcontentType);
581                 n->tfileName = (char *) malloc(strlen(tfileName) + 1);
582                 if (!n->tfileName) {
583                         goto outOfMemory;
584                 }
585                 strcpy(n->tfileName, tfileName);
586
587                 l = n;                  
588         }       
589         return cgiParseSuccess;
590 outOfMemory:
591         if (n) {
592                 if (n->attr) {
593                         free(n->attr);
594                 }
595                 if (n->value) {
596                         free(n->value);
597                 }
598                 if (n->fileName) {
599                         free(n->fileName);
600                 }
601                 if (n->tfileName) {
602                         free(n->tfileName);
603                 }
604                 if (n->contentType) {
605                         free(n->contentType);
606                 }
607                 free(n);
608         }
609         if (out) {
610                 free(out);
611         }
612         if (outf) {
613                 fclose(outf);
614                 unlink(tfileName);
615         }
616         return cgiParseMemory;
617 }
618
619 static cgiParseResultType getTempFileName(char *tfileName)
620 {
621 #ifndef WIN32
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). */
630         int outfd; 
631         strcpy(tfileName, cgicTempDir "/cgicXXXXXX");
632         outfd = mkstemp(tfileName);
633         if (outfd == -1) {
634                 return cgiParseIO;
635         }
636         close(outfd);
637         /* Fix the permissions */
638         if (chmod(tfileName, 0600) != 0) {
639                 unlink(tfileName);
640                 return cgiParseIO;
641         }
642 #else
643         /* Non-Unix. Do what we can. */
644         if (!tmpnam(tfileName)) {
645                 return cgiParseIO;
646         }
647 #endif
648         return cgiParseSuccess;
649 }
650
651
652 #define APPEND(string, char) \
653         { \
654                 if ((string##Len + 1) < string##Space) { \
655                         string[string##Len++] = (char); \
656                 } \
657         }
658
659 #define RAPPEND(string, ch) \
660         { \
661                 if ((string##Len + 1) == string##Space)  { \
662                         char *sold = string; \
663                         string##Space *= 2; \
664                         string = (char *) realloc(string, string##Space); \
665                         if (!string) { \
666                                 string = sold; \
667                                 goto outOfMemory; \
668                         } \
669                 } \
670                 string[string##Len++] = (ch); \
671         }
672                 
673 #define BAPPEND(ch) \
674         { \
675                 if (outf) { \
676                         putc(ch, outf); \
677                         outLen++; \
678                 } else if (out) { \
679                         RAPPEND(out, ch); \
680                 } \
681         }
682
683 cgiParseResultType afterNextBoundary(mpStreamPtr mpp, FILE *outf, char **outP,
684         int *bodyLengthP, int first)
685 {
686         int outLen = 0;
687         int outSpace = 256;
688         char *out = 0;
689         cgiParseResultType result;
690         int boffset;
691         int got;
692         char d[2];      
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);
702                 if (!out) {
703                         goto outOfMemory;
704                 }
705         }
706         boffset = 0;
707         sprintf(workingBoundaryData, "\r\n--%s", cgiMultipartBoundary);
708         if (first) {
709                 workingBoundary = workingBoundaryData + 2;
710         }
711         workingBoundaryLength = strlen(workingBoundary);
712         while (1) {
713                 got = mpRead(mpp, d, 1);
714                 if (got != 1) {
715                         /* 2.01: cgiParseIO, not cgiFormIO */
716                         result = cgiParseIO;
717                         goto error;
718                 }
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. */
723                         boffset++;
724                         if (boffset == workingBoundaryLength) {
725                                 break;
726                         } 
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]);
736                         mpPutBack(mpp, 
737                                 workingBoundary + 1, boffset - 1);
738                         mpPutBack(mpp, d, 1);
739                         boffset = 0;
740                 } else {                
741                         /* Not presently in the middle of a boundary
742                                 match; just emit the character. */
743                         BAPPEND(d[0]);
744                 }       
745         }
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);
749         if (got != 2) {
750                 result = cgiParseIO;
751                 goto error;
752         }       
753         if ((d[0] == '\r') && (d[1] == '\n')) {
754                 /* OK, EOL */
755         } else if (d[0] == '-') {
756                 /* Probably EOF, but we check for
757                         that later */
758                 mpPutBack(mpp, d, 2);
759         }       
760         if (out && outSpace) {
761                 char *oout = out;
762                 out[outLen] = '\0';
763                 out = (char *) realloc(out, outLen + 1);
764                 if (!out) {
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. */
768                         out = oout;
769                 }
770                 *outP = out;
771         }
772         if (bodyLengthP) {
773                 *bodyLengthP = outLen;
774         }
775         return cgiParseSuccess;
776 outOfMemory:
777         result = cgiParseMemory;
778         if (outP) {
779                 if (out) {
780                         free(out);
781                 }
782                 *outP = '\0';   
783         }
784 error:
785         if (bodyLengthP) {
786                 *bodyLengthP = 0;
787         }
788         if (out) {
789                 free(out);
790         }
791         if (outP) {
792                 *outP = 0;      
793         }
794         return result;
795 }
796
797 static void decomposeValue(char *value,
798         char *mvalue, int mvalueSpace,
799         char **argNames,
800         char **argValues,
801         int argValueSpace)
802 {
803         char argName[1024];
804         int argNameSpace = sizeof(argName);
805         int argNameLen = 0;
806         int mvalueLen = 0;
807         char *argValue;
808         int argNum = 0;
809         while (argNames[argNum]) {
810                 if (argValueSpace) {
811                         argValues[argNum][0] = '\0';
812                 }
813                 argNum++;
814         }
815         while (isspace(*value)) {
816                 value++;
817         }
818         /* Quoted mvalue */
819         if (*value == '\"') {
820                 value++;
821                 while ((*value) && (*value != '\"')) {
822                         APPEND(mvalue, *value);
823                         value++;
824                 }
825                 while ((*value) && (*value != ';')) {
826                         value++;
827                 }
828         } else {
829                 /* Unquoted mvalue */
830                 while ((*value) && (*value != ';')) {
831                         APPEND(mvalue, *value);
832                         value++;
833                 }       
834         }       
835         if (mvalueSpace) {
836                 mvalue[mvalueLen] = '\0';
837         }
838         while (*value == ';') {
839                 int argNum;
840                 int argValueLen = 0;
841                 /* Skip the ; between parameters */
842                 value++;
843                 /* Now skip leading whitespace */
844                 while ((*value) && (isspace(*value))) { 
845                         value++;
846                 }
847                 /* Now read the parameter name */
848                 argNameLen = 0;
849                 while ((*value) && (isalnum(*value))) {
850                         APPEND(argName, *value);
851                         value++;
852                 }
853                 if (argNameSpace) {
854                         argName[argNameLen] = '\0';
855                 }
856                 while ((*value) && isspace(*value)) {
857                         value++;
858                 }
859                 if (*value != '=') {
860                         /* Malformed line */
861                         return; 
862                 }
863                 value++;
864                 while ((*value) && isspace(*value)) {
865                         value++;
866                 }
867                 /* Find the parameter in the argument list, if present */
868                 argNum = 0;
869                 argValue = 0;
870                 while (argNames[argNum]) {
871                         if (cgiStrEqNc(argName, argNames[argNum])) {
872                                 argValue = argValues[argNum];
873                                 break;
874                         }
875                         argNum++;
876                 }               
877                 /* Finally, read the parameter value */
878                 if (*value == '\"') {
879                         value++;
880                         while ((*value) && (*value != '\"')) {
881                                 if (argValue) {
882                                         APPEND(argValue, *value);
883                                 }
884                                 value++;
885                         }
886                         while ((*value) && (*value != ';')) {
887                                 value++;
888                         }
889                 } else {
890                         /* Unquoted value */
891                         while ((*value) && (*value != ';')) {
892                                 if (argNames[argNum]) {
893                                         APPEND(argValue, *value);
894                                 }
895                                 value++;
896                         }       
897                 }       
898                 if (argValueSpace) {
899                         argValue[argValueLen] = '\0';
900                 }
901         }               
902 }
903
904 static int readHeaderLine(
905         mpStreamPtr mpp,
906         char *attr,
907         int attrSpace,
908         char *value,
909         int valueSpace)
910 {       
911         int attrLen = 0;
912         int valueLen = 0;
913         int valueFound = 0;
914         while (1) {
915                 char d[1];
916                 int got = mpRead(mpp, d, 1);
917                 if (got != 1) { 
918                         return 0;
919                 }
920                 if (d[0] == '\r') {
921                         got = mpRead(mpp, d, 1);
922                         if (got == 1) { 
923                                 if (d[0] == '\n') {
924                                         /* OK */
925                                 } else {
926                                         mpPutBack(mpp, d, 1);
927                                 }
928                         }
929                         break;
930                 } else if (d[0] == '\n') {
931                         break;
932                 } else if ((d[0] == ':') && attrLen) {
933                         valueFound = 1;
934                         while (mpRead(mpp, d, 1) == 1) {
935                                 if (!isspace(d[0])) {
936                                         mpPutBack(mpp, d, 1);
937                                         break;
938                                 } 
939                         }
940                 } else if (!valueFound) {
941                         if (!isspace(*d)) {
942                                 if (attrLen < (attrSpace - 1)) {
943                                         attr[attrLen++] = *d;
944                                 }
945                         }               
946                 } else if (valueFound) {        
947                         if (valueLen < (valueSpace - 1)) {
948                                 value[valueLen++] = *d;
949                         }
950                 }
951         }
952         if (attrSpace) {
953                 attr[attrLen] = '\0';
954         }
955         if (valueSpace) {
956                 value[valueLen] = '\0';
957         }
958         if (attrLen && valueLen) {
959                 return 1;
960         } else {
961                 return 0;
962         }
963 }
964
965 static cgiParseResultType cgiParseGetFormInput() {
966         return cgiParseFormInput(cgiQueryString, cgiContentLength);
967 }
968
969 typedef enum {
970         cgiEscapeRest,
971         cgiEscapeFirst,
972         cgiEscapeSecond
973 } cgiEscapeState;
974
975 typedef enum {
976         cgiUnescapeSuccess,
977         cgiUnescapeMemory
978 } cgiUnescapeResultType;
979
980 static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
981
982 static cgiParseResultType cgiParseFormInput(char *data, int length) {
983         /* Scan for pairs, unescaping and storing them as they are found. */
984         int pos = 0;
985         cgiFormEntry *n;
986         cgiFormEntry *l = 0;
987         while (pos != length) {
988                 int foundEq = 0;
989                 int foundAmp = 0;
990                 int start = pos;
991                 int len = 0;
992                 char *attr;
993                 char *value;
994                 while (pos != length) {
995                         if (data[pos] == '=') {
996                                 foundEq = 1;
997                                 pos++;
998                                 break;
999                         }
1000                         pos++;
1001                         len++;
1002                 }
1003                 if (!foundEq) {
1004                         break;
1005                 }
1006                 if (cgiUnescapeChars(&attr, data+start, len)
1007                         != cgiUnescapeSuccess) {
1008                         return cgiParseMemory;
1009                 }       
1010                 start = pos;
1011                 len = 0;
1012                 while (pos != length) {
1013                         if (data[pos] == '&') {
1014                                 foundAmp = 1;
1015                                 pos++;
1016                                 break;
1017                         }
1018                         pos++;
1019                         len++;
1020                 }
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) {
1025                         free(attr);
1026                         return cgiParseMemory;
1027                 }       
1028                 /* OK, we have a new pair, add it to the list. */
1029                 n = (cgiFormEntry *) malloc(sizeof(cgiFormEntry));      
1030                 if (!n) {
1031                         free(attr);
1032                         free(value);
1033                         return cgiParseMemory;
1034                 }
1035                 n->attr = attr;
1036                 n->value = value;
1037                 n->valueLength = strlen(n->value);
1038                 n->fileName = (char *) malloc(1);
1039                 if (!n->fileName) {
1040                         free(attr);
1041                         free(value);
1042                         free(n);
1043                         return cgiParseMemory;
1044                 }       
1045                 n->fileName[0] = '\0';
1046                 n->contentType = (char *) malloc(1);
1047                 if (!n->contentType) {
1048                         free(attr);
1049                         free(value);
1050                         free(n->fileName);
1051                         free(n);
1052                         return cgiParseMemory;
1053                 }       
1054                 n->contentType[0] = '\0';
1055                 n->tfileName = (char *) malloc(1);
1056                 if (!n->tfileName) {
1057                         free(attr);
1058                         free(value);
1059                         free(n->fileName);
1060                         free(n->contentType);
1061                         free(n);
1062                         return cgiParseMemory;
1063                 }       
1064                 n->tfileName[0] = '\0';
1065                 n->next = 0;
1066                 if (!l) {
1067                         cgiFormEntryFirst = n;
1068                 } else {
1069                         l->next = n;
1070                 }
1071                 l = n;
1072                 if (!foundAmp) {
1073                         break;
1074                 }                       
1075         }
1076         return cgiParseSuccess;
1077 }
1078
1079 static int cgiHexValue[256];
1080
1081 cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len) {
1082         char *s;
1083         cgiEscapeState escapeState = cgiEscapeRest;
1084         int escapedValue = 0;
1085         int srcPos = 0;
1086         int dstPos = 0;
1087         s = (char *) malloc(len + 1);
1088         if (!s) {
1089                 return cgiUnescapeMemory;
1090         }
1091         while (srcPos < len) {
1092                 int ch = cp[srcPos];
1093                 switch (escapeState) {
1094                         case cgiEscapeRest:
1095                         if (ch == '%') {
1096                                 escapeState = cgiEscapeFirst;
1097                         } else if (ch == '+') {
1098                                 s[dstPos++] = ' ';
1099                         } else {
1100                                 s[dstPos++] = ch;       
1101                         }
1102                         break;
1103                         case cgiEscapeFirst:
1104                         escapedValue = cgiHexValue[ch] << 4;    
1105                         escapeState = cgiEscapeSecond;
1106                         break;
1107                         case cgiEscapeSecond:
1108                         escapedValue += cgiHexValue[ch];
1109                         s[dstPos++] = escapedValue;
1110                         escapeState = cgiEscapeRest;
1111                         break;
1112                 }
1113                 srcPos++;
1114         }
1115         s[dstPos] = '\0';
1116         *sp = s;
1117         return cgiUnescapeSuccess;
1118 }               
1119         
1120 static void cgiSetupConstants() {
1121         int i;
1122         for (i=0; (i < 256); i++) {
1123                 cgiHexValue[i] = 0;
1124         }
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;
1147 }
1148
1149 static void cgiFreeResources() {
1150         cgiFormEntry *c = cgiFormEntryFirst;
1151         cgiFormEntry *n;
1152         while (c) {
1153                 n = c->next;
1154                 free(c->attr);
1155                 free(c->value);
1156                 free(c->fileName);
1157                 free(c->contentType);
1158                 if (strlen(c->tfileName)) {
1159                         unlink(c->tfileName);
1160                 }
1161                 free(c->tfileName);
1162                 free(c);
1163                 c = n;
1164         }
1165         /* If the cgi environment was restored from a saved environment,
1166                 then these are in allocated space and must also be freed */
1167         if (cgiRestored) {
1168                 free(cgiServerSoftware);
1169                 free(cgiServerName);
1170                 free(cgiGatewayInterface);
1171                 free(cgiServerProtocol);
1172                 free(cgiServerPort);
1173                 free(cgiRequestMethod);
1174                 free(cgiPathInfo);
1175                 free(cgiPathTranslated);
1176                 free(cgiScriptName);
1177                 free(cgiQueryString);
1178                 free(cgiRemoteHost);
1179                 free(cgiRemoteAddr);
1180                 free(cgiAuthType);
1181                 free(cgiRemoteUser);
1182                 free(cgiRemoteIdent);
1183                 free(cgiContentType);
1184                 free(cgiAccept);
1185                 free(cgiUserAgent);
1186                 free(cgiReferrer);
1187         }
1188         /* 2.0: to clean up the environment for cgiReadEnvironment,
1189                 we must set these correctly */
1190         cgiFormEntryFirst = 0;
1191         cgiRestored = 0;
1192 }
1193
1194 static cgiFormResultType cgiFormEntryString(
1195         cgiFormEntry *e, char *result, int max, int newlines);
1196
1197 static cgiFormEntry *cgiFormEntryFindFirst(char *name);
1198 static cgiFormEntry *cgiFormEntryFindNext();
1199
1200 cgiFormResultType cgiFormString(
1201         char *name, char *result, int max) {
1202         cgiFormEntry *e;
1203         e = cgiFormEntryFindFirst(name);
1204         if (!e) {
1205                 strcpy(result, "");
1206                 return cgiFormNotFound;
1207         }
1208         return cgiFormEntryString(e, result, max, 1);
1209 }
1210
1211 cgiFormResultType cgiFormFileName(
1212         char *name, char *result, int resultSpace)
1213 {
1214         cgiFormEntry *e;
1215         int resultLen = 0;
1216         char *s;
1217         e = cgiFormEntryFindFirst(name);
1218         if (!e) {
1219                 strcpy(result, "");
1220                 return cgiFormNotFound;
1221         }
1222         s = e->fileName;
1223         while (*s) {
1224                 APPEND(result, *s);
1225                 s++;
1226         }       
1227         if (resultSpace) {
1228                 result[resultLen] = '\0';
1229         }
1230         if (!strlen(e->fileName)) {
1231                 return cgiFormNoFileName;
1232         } else if (((int) strlen(e->fileName)) > (resultSpace - 1)) {
1233                 return cgiFormTruncated;
1234         } else {
1235                 return cgiFormSuccess;
1236         }
1237 }
1238
1239 cgiFormResultType cgiFormFileContentType(
1240         char *name, char *result, int resultSpace)
1241 {
1242         cgiFormEntry *e;
1243         int resultLen = 0;
1244         char *s;
1245         e = cgiFormEntryFindFirst(name);
1246         if (!e) {
1247                 if (resultSpace) {
1248                         result[0] = '\0';
1249                 }       
1250                 return cgiFormNotFound;
1251         }
1252         s = e->contentType;
1253         while (*s) {
1254                 APPEND(result, *s);
1255                 s++;
1256         }       
1257         if (resultSpace) {
1258                 result[resultLen] = '\0';
1259         }
1260         if (!strlen(e->contentType)) {
1261                 return cgiFormNoContentType;
1262         } else if (((int) strlen(e->contentType)) > (resultSpace - 1)) {
1263                 return cgiFormTruncated;
1264         } else {
1265                 return cgiFormSuccess;
1266         }
1267 }
1268
1269 cgiFormResultType cgiFormFileSize(
1270         char *name, int *sizeP)
1271 {
1272         cgiFormEntry *e;
1273         e = cgiFormEntryFindFirst(name);
1274         if (!e) {
1275                 if (sizeP) {
1276                         *sizeP = 0;
1277                 }
1278                 return cgiFormNotFound;
1279         } else if (!strlen(e->tfileName)) {
1280                 if (sizeP) {
1281                         *sizeP = 0;
1282                 }
1283                 return cgiFormNotAFile;
1284         } else {
1285                 if (sizeP) {
1286                         *sizeP = e->valueLength;
1287                 }
1288                 return cgiFormSuccess;
1289         }
1290 }
1291
1292 typedef struct cgiFileStruct {
1293         FILE *in;
1294 } cgiFile;
1295
1296 cgiFormResultType cgiFormFileOpen(
1297         char *name, cgiFilePtr *cfpp)
1298 {
1299         cgiFormEntry *e;
1300         cgiFilePtr cfp;
1301         e = cgiFormEntryFindFirst(name);
1302         if (!e) {
1303                 *cfpp = 0;
1304                 return cgiFormNotFound;
1305         }
1306         if (!strlen(e->tfileName)) {
1307                 *cfpp = 0;
1308                 return cgiFormNotAFile;
1309         }
1310         cfp = (cgiFilePtr) malloc(sizeof(cgiFile));
1311         if (!cfp) {
1312                 *cfpp = 0;
1313                 return cgiFormMemory;
1314         }
1315         cfp->in = fopen(e->tfileName, "rb");
1316         if (!cfp->in) {
1317                 free(cfp);
1318                 return cgiFormIO;
1319         }
1320         *cfpp = cfp;
1321         return cgiFormSuccess;
1322 }
1323
1324 cgiFormResultType cgiFormFileRead(
1325         cgiFilePtr cfp, char *buffer, 
1326         int bufferSize, int *gotP)
1327 {
1328         int got = 0;
1329         if (!cfp) {
1330                 return cgiFormOpenFailed;
1331         }
1332         got = fread(buffer, 1, bufferSize, cfp->in);
1333         if (got <= 0) {
1334                 return cgiFormEOF;
1335         }
1336         *gotP = got;
1337         return cgiFormSuccess;
1338 }
1339
1340 cgiFormResultType cgiFormFileClose(cgiFilePtr cfp)
1341 {
1342         if (!cfp) {
1343                 return cgiFormOpenFailed;
1344         }
1345         fclose(cfp->in);
1346         free(cfp);
1347         return cgiFormSuccess;
1348 }
1349
1350 cgiFormResultType cgiFormStringNoNewlines(
1351         char *name, char *result, int max) {
1352         cgiFormEntry *e;
1353         e = cgiFormEntryFindFirst(name);
1354         if (!e) {
1355                 strcpy(result, "");
1356                 return cgiFormNotFound;
1357         }
1358         return cgiFormEntryString(e, result, max, 0);
1359 }
1360
1361 cgiFormResultType cgiFormStringMultiple(
1362         char *name, char ***result) {
1363         char **stringArray;
1364         cgiFormEntry *e;
1365         int i;
1366         int total = 0;
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);
1371         if (e != 0) {
1372                 do {
1373                         total++;
1374                 } while ((e = cgiFormEntryFindNext()) != 0); 
1375         }
1376         stringArray = (char **) malloc(sizeof(char *) * (total + 1));
1377         if (!stringArray) {
1378                 *result = 0;
1379                 return cgiFormMemory;
1380         }
1381         /* initialize all entries to null; the last will stay that way */
1382         for (i=0; (i <= total); i++) {
1383                 stringArray[i] = 0;
1384         }
1385         /* Now go get the entries */
1386         e = cgiFormEntryFindFirst(name);
1387 #ifdef CGICDEBUG
1388         CGICDEBUGSTART
1389         fprintf(dout, "StringMultiple Beginning\n");
1390         CGICDEBUGEND
1391 #endif /* CGICDEBUG */
1392         if (e) {
1393                 i = 0;
1394                 do {
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);
1400                                 *result = 0;
1401                                 return cgiFormMemory;
1402                         }       
1403                         strcpy(stringArray[i], e->value);
1404                         cgiFormEntryString(e, stringArray[i], max, 1);
1405                         i++;
1406                 } while ((e = cgiFormEntryFindNext()) != 0); 
1407                 *result = stringArray;
1408 #ifdef CGICDEBUG
1409                 CGICDEBUGSTART
1410                 fprintf(dout, "StringMultiple Succeeding\n");
1411                 CGICDEBUGEND
1412 #endif /* CGICDEBUG */
1413                 return cgiFormSuccess;
1414         } else {
1415                 *result = stringArray;
1416 #ifdef CGICDEBUG
1417                 CGICDEBUGSTART
1418                 fprintf(dout, "StringMultiple found nothing\n");
1419                 CGICDEBUGEND
1420 #endif /* CGICDEBUG */
1421                 return cgiFormNotFound;
1422         }       
1423 }
1424
1425 cgiFormResultType cgiFormStringSpaceNeeded(
1426         char *name, int *result) {
1427         cgiFormEntry *e;
1428         e = cgiFormEntryFindFirst(name);
1429         if (!e) {
1430                 *result = 1;
1431                 return cgiFormNotFound; 
1432         }
1433         *result = ((int) strlen(e->value)) + 1;
1434         return cgiFormSuccess;
1435 }
1436
1437 static cgiFormResultType cgiFormEntryString(
1438         cgiFormEntry *e, char *result, int max, int newlines) {
1439         char *dp, *sp;
1440         int truncated = 0;
1441         int len = 0;
1442         int avail = max-1;
1443         int crCount = 0;
1444         int lfCount = 0;        
1445         dp = result;
1446         sp = e->value;  
1447         while (1) {
1448                 int ch;
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. */
1454                 ch = *sp;
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)) {
1462                         if (ch == 13) {
1463                                 crCount++;
1464                         } else {
1465                                 lfCount++;
1466                         }       
1467                 } else {
1468                         if (crCount || lfCount) {
1469                                 int lfsAdd = crCount;
1470                                 if (lfCount > crCount) {
1471                                         lfsAdd = lfCount;
1472                                 }
1473                                 /* Stomp all newlines if desired */
1474                                 if (!newlines) {
1475                                         lfsAdd = 0;
1476                                 }
1477                                 while (lfsAdd) {
1478                                         if (len >= avail) {
1479                                                 truncated = 1;
1480                                                 break;
1481                                         }
1482                                         *dp = 10;
1483                                         dp++;
1484                                         lfsAdd--;
1485                                         len++;          
1486                                 }
1487                                 crCount = 0;
1488                                 lfCount = 0;
1489                         }
1490                         if (ch == '\0') {
1491                                 /* The end of the source string */
1492                                 break;                          
1493                         }       
1494                         /* 1.06: check available space before adding
1495                                 the character, because a previously added
1496                                 LF may have brought us to the limit */
1497                         if (len >= avail) {
1498                                 truncated = 1;
1499                                 break;
1500                         }
1501                         *dp = ch;
1502                         dp++;
1503                         len++;
1504                 }
1505                 sp++;   
1506         }       
1507         *dp = '\0';
1508         if (truncated) {
1509                 return cgiFormTruncated;
1510         } else if (!len) {
1511                 return cgiFormEmpty;
1512         } else {
1513                 return cgiFormSuccess;
1514         }
1515 }
1516
1517 static int cgiFirstNonspaceChar(char *s);
1518
1519 cgiFormResultType cgiFormInteger(
1520         char *name, int *result, int defaultV) {
1521         cgiFormEntry *e;
1522         int ch;
1523         e = cgiFormEntryFindFirst(name);
1524         if (!e) {
1525                 *result = defaultV;
1526                 return cgiFormNotFound; 
1527         }       
1528         if (!strlen(e->value)) {
1529                 *result = defaultV;
1530                 return cgiFormEmpty;
1531         }
1532         ch = cgiFirstNonspaceChar(e->value);
1533         if (!(isdigit(ch)) && (ch != '-') && (ch != '+')) {
1534                 *result = defaultV;
1535                 return cgiFormBadType;
1536         } else {
1537                 *result = atoi(e->value);
1538                 return cgiFormSuccess;
1539         }
1540 }
1541
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) {
1546                 return error;
1547         }
1548         if (*result < min) {
1549                 *result = min;
1550                 return cgiFormConstrained;
1551         } 
1552         if (*result > max) {
1553                 *result = max;
1554                 return cgiFormConstrained;
1555         } 
1556         return cgiFormSuccess;
1557 }
1558
1559 cgiFormResultType cgiFormDouble(
1560         char *name, double *result, double defaultV) {
1561         cgiFormEntry *e;
1562         int ch;
1563         e = cgiFormEntryFindFirst(name);
1564         if (!e) {
1565                 *result = defaultV;
1566                 return cgiFormNotFound; 
1567         }       
1568         if (!strlen(e->value)) {
1569                 *result = defaultV;
1570                 return cgiFormEmpty;
1571         } 
1572         ch = cgiFirstNonspaceChar(e->value);
1573         if (!(isdigit(ch)) && (ch != '.') && (ch != '-') && (ch != '+')) {
1574                 *result = defaultV;
1575                 return cgiFormBadType;
1576         } else {
1577                 *result = atof(e->value);
1578                 return cgiFormSuccess;
1579         }
1580 }
1581
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) {
1586                 return error;
1587         }
1588         if (*result < min) {
1589                 *result = min;
1590                 return cgiFormConstrained;
1591         } 
1592         if (*result > max) {
1593                 *result = max;
1594                 return cgiFormConstrained;
1595         } 
1596         return cgiFormSuccess;
1597 }
1598
1599 cgiFormResultType cgiFormSelectSingle(
1600         char *name, char **choicesText, int choicesTotal, 
1601         int *result, int defaultV) 
1602 {
1603         cgiFormEntry *e;
1604         int i;
1605         e = cgiFormEntryFindFirst(name);
1606 #ifdef CGICDEBUG
1607         CGICDEBUGSTART
1608         fprintf(dout, "%d\n", (int) e);
1609         CGICDEBUGEND
1610 #endif /* CGICDEBUG */
1611         if (!e) {
1612                 *result = defaultV;
1613                 return cgiFormNotFound;
1614         }
1615         for (i=0; (i < choicesTotal); i++) {
1616 #ifdef CGICDEBUG
1617                 CGICDEBUGSTART
1618                 fprintf(dout, "%s %s\n", choicesText[i], e->value);
1619                 CGICDEBUGEND
1620 #endif /* CGICDEBUG */
1621                 if (cgiStrEq(choicesText[i], e->value)) {
1622 #ifdef CGICDEBUG
1623                         CGICDEBUGSTART
1624                         fprintf(dout, "MATCH\n");
1625                         CGICDEBUGEND
1626 #endif /* CGICDEBUG */
1627                         *result = i;
1628                         return cgiFormSuccess;
1629                 }
1630         }
1631         *result = defaultV;
1632         return cgiFormNoSuchChoice;
1633 }
1634
1635 cgiFormResultType cgiFormSelectMultiple(
1636         char *name, char **choicesText, int choicesTotal, 
1637         int *result, int *invalid) 
1638 {
1639         cgiFormEntry *e;
1640         int i;
1641         int hits = 0;
1642         int invalidE = 0;
1643         for (i=0; (i < choicesTotal); i++) {
1644                 result[i] = 0;
1645         }
1646         e = cgiFormEntryFindFirst(name);
1647         if (!e) {
1648                 *invalid = invalidE;
1649                 return cgiFormNotFound;
1650         }
1651         do {
1652                 int hit = 0;
1653                 for (i=0; (i < choicesTotal); i++) {
1654                         if (cgiStrEq(choicesText[i], e->value)) {
1655                                 result[i] = 1;
1656                                 hits++;
1657                                 hit = 1;
1658                                 break;
1659                         }
1660                 }
1661                 if (!(hit)) {
1662                         invalidE++;
1663                 }
1664         } while ((e = cgiFormEntryFindNext()) != 0);
1665
1666         *invalid = invalidE;
1667
1668         if (hits) {
1669                 return cgiFormSuccess;
1670         } else {
1671                 return cgiFormNotFound;
1672         }
1673 }
1674
1675 cgiFormResultType cgiFormCheckboxSingle(
1676         char *name)
1677 {
1678         cgiFormEntry *e;
1679         e = cgiFormEntryFindFirst(name);
1680         if (!e) {
1681                 return cgiFormNotFound;
1682         }
1683         return cgiFormSuccess;
1684 }
1685
1686 extern cgiFormResultType cgiFormCheckboxMultiple(
1687         char *name, char **valuesText, int valuesTotal, 
1688         int *result, int *invalid)
1689 {
1690         /* Implementation is identical to cgiFormSelectMultiple. */
1691         return cgiFormSelectMultiple(name, valuesText, 
1692                 valuesTotal, result, invalid);
1693 }
1694
1695 cgiFormResultType cgiFormRadio(
1696         char *name, 
1697         char **valuesText, int valuesTotal, int *result, int defaultV)
1698 {
1699         /* Implementation is identical to cgiFormSelectSingle. */
1700         return cgiFormSelectSingle(name, valuesText, valuesTotal, 
1701                 result, defaultV);
1702 }
1703
1704 cgiFormResultType cgiCookieString(
1705         char *name,
1706         char *value,
1707         int space)
1708 {
1709         char *p = cgiCookie;
1710         while (*p) {
1711                 char *n = name;
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 
1717                         Tomadakis. */
1718                 while (*p == *n) {
1719                         if ((p == '\0') && (n == '\0')) {
1720                                 /* Malformed cookie header from client */
1721                                 return cgiFormNotFound;
1722                         }
1723                         p++;
1724                         n++;
1725                 }
1726                 if ((!*n) && (*p == '=')) {
1727                         p++;
1728                         while ((*p != ';') && (*p != '\0') &&
1729                                 (space > 1)) 
1730                         {
1731                                 *value = *p;
1732                                 value++;
1733                                 p++;
1734                                 space--;
1735                         }
1736                         if (space > 0) {
1737                                 *value = '\0';
1738                         }
1739                         /* Correct parens: 2.02. Thanks to
1740                                 Mathieu Villeneuve-Belair. */
1741                         if (!(((*p) == ';') || ((*p) == '\0')))
1742                         {
1743                                 return cgiFormTruncated;
1744                         } else {        
1745                                 return cgiFormSuccess;
1746                         }
1747                 } else {
1748                         /* Skip to next cookie */       
1749                         while (*p) {
1750                                 if (*p == ';') {
1751                                         break;
1752                                 }
1753                                 p++;
1754                         }
1755                         if (!*p) {
1756                                 /* 2.01: default to empty */
1757                                 if (space) {
1758                                         *value = '\0';
1759                                 }
1760                                 return cgiFormNotFound;
1761                         }
1762                         p++;    
1763                         /* Allow whitespace after semicolon */
1764                         while ((*p) && isspace(*p)) {
1765                                 p++;
1766                         } 
1767                 }
1768         }
1769         /* 2.01: actually the above loop never terminates except
1770                 with a return, but do this to placate gcc */
1771         if (space) {
1772                 *value = '\0';
1773         }
1774         return cgiFormNotFound;
1775 }
1776
1777 cgiFormResultType cgiCookieInteger(
1778         char *name,
1779         int *result,
1780         int defaultV)
1781 {
1782         char buffer[256];
1783         cgiFormResultType r = 
1784                 cgiCookieString(name, buffer, sizeof(buffer));
1785         if (r != cgiFormSuccess) {
1786                 *result = defaultV;
1787         } else {
1788                 *result = atoi(buffer);
1789         }
1790         return r;
1791 }
1792
1793 void cgiHeaderCookieSetInteger(char *name, int value, int secondsToLive,
1794         char *path, char *domain)
1795 {
1796         char svalue[256];
1797         sprintf(svalue, "%d", value);
1798         cgiHeaderCookieSetString(name, svalue, secondsToLive, path, domain);
1799 }
1800
1801 char *days[] = {
1802         "Sun",
1803         "Mon",
1804         "Tue",
1805         "Wed",
1806         "Thu",
1807         "Fri",
1808         "Sat"
1809 };
1810
1811 char *months[] = {
1812         "Jan",
1813         "Feb",
1814         "Mar",
1815         "Apr",
1816         "May",
1817         "Jun",
1818         "Jul",
1819         "Aug",
1820         "Sep",
1821         "Oct",
1822         "Nov",
1823         "Dec"
1824 };
1825
1826 void cgiHeaderCookieSetString(char *name, char *value, int secondsToLive,
1827         char *path, char *domain)
1828 {
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=/
1838         */
1839         time_t now;
1840         time_t then;
1841         struct tm *gt;
1842         time(&now);
1843         then = now + secondsToLive;
1844         gt = gmtime(&then);
1845         fprintf(cgiOut, 
1846                 "Set-Cookie: %s=%s; domain=%s; expires=%s, %02d-%s-%04d %02d:%02d:%02d GMT; path=%s\r\n",
1847                 name, value, domain, 
1848                 days[gt->tm_wday],
1849                 gt->tm_mday,
1850                 months[gt->tm_mon],
1851                 gt->tm_year + 1900,     
1852                 gt->tm_hour,
1853                 gt->tm_min,
1854                 gt->tm_sec,
1855                 path);
1856 }
1857
1858 void cgiHeaderLocation(char *redirectUrl) {
1859         fprintf(cgiOut, "Location: %s\r\n\r\n", redirectUrl);
1860 }
1861
1862 void cgiHeaderStatus(int status, char *statusMessage) {
1863         fprintf(cgiOut, "Status: %d %s\r\n\r\n", status, statusMessage);
1864 }
1865
1866 void cgiHeaderContentType(char *mimeType) {
1867         fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType);
1868 }
1869
1870 static int cgiWriteString(FILE *out, char *s);
1871
1872 static int cgiWriteInt(FILE *out, int i);
1873
1874 #define CGIC_VERSION "2.0"
1875
1876 cgiEnvironmentResultType cgiWriteEnvironment(char *filename) {
1877         FILE *out;
1878         cgiFormEntry *e;
1879         /* Be sure to open in binary mode */
1880         out = fopen(filename, "wb");
1881         if (!out) {
1882                 /* Can't create file */
1883                 return cgiEnvironmentIO;
1884         }
1885         if (!cgiWriteString(out, "CGIC2.0")) {
1886                 goto error;
1887         }
1888         if (!cgiWriteString(out, cgiServerSoftware)) {
1889                 goto error;
1890         }
1891         if (!cgiWriteString(out, cgiServerName)) {
1892                 goto error;
1893         }
1894         if (!cgiWriteString(out, cgiGatewayInterface)) {
1895                 goto error;
1896         }
1897         if (!cgiWriteString(out, cgiServerProtocol)) {
1898                 goto error;
1899         }
1900         if (!cgiWriteString(out, cgiServerPort)) {
1901                 goto error;
1902         }
1903         if (!cgiWriteString(out, cgiRequestMethod)) {
1904                 goto error;
1905         }
1906         if (!cgiWriteString(out, cgiPathInfo)) {
1907                 goto error;
1908         }
1909         if (!cgiWriteString(out, cgiPathTranslated)) {
1910                 goto error;
1911         }
1912         if (!cgiWriteString(out, cgiScriptName)) {
1913                 goto error;
1914         }
1915         if (!cgiWriteString(out, cgiQueryString)) {
1916                 goto error;
1917         }
1918         if (!cgiWriteString(out, cgiRemoteHost)) {
1919                 goto error;
1920         }
1921         if (!cgiWriteString(out, cgiRemoteAddr)) {
1922                 goto error;
1923         }
1924         if (!cgiWriteString(out, cgiAuthType)) {
1925                 goto error;
1926         }
1927         if (!cgiWriteString(out, cgiRemoteUser)) {
1928                 goto error;
1929         }
1930         if (!cgiWriteString(out, cgiRemoteIdent)) {
1931                 goto error;
1932         }
1933         if (!cgiWriteString(out, cgiContentType)) {
1934                 goto error;
1935         }
1936         if (!cgiWriteString(out, cgiAccept)) {
1937                 goto error;
1938         }
1939         if (!cgiWriteString(out, cgiUserAgent)) {
1940                 goto error;
1941         }
1942         if (!cgiWriteString(out, cgiReferrer)) {
1943                 goto error;
1944         }
1945         if (!cgiWriteString(out, cgiCookie)) {
1946                 goto error;
1947         }
1948         if (!cgiWriteInt(out, cgiContentLength)) {
1949                 goto error;
1950         }
1951         e = cgiFormEntryFirst;
1952         while (e) {
1953                 cgiFilePtr fp;
1954                 if (!cgiWriteString(out, e->attr)) {
1955                         goto error;
1956                 }
1957                 if (!cgiWriteString(out, e->value)) {
1958                         goto error;
1959                 }
1960                 /* New 2.0 fields and file uploads */
1961                 if (!cgiWriteString(out, e->fileName)) {
1962                         goto error;
1963                 }
1964                 if (!cgiWriteString(out, e->contentType)) {
1965                         goto error;
1966                 }
1967                 if (!cgiWriteInt(out, e->valueLength)) {
1968                         goto error;
1969                 }
1970                 if (cgiFormFileOpen(e->attr, &fp) == cgiFormSuccess) {
1971                         char buffer[1024];
1972                         int got;
1973                         if (!cgiWriteInt(out, 1)) {
1974                                 cgiFormFileClose(fp);
1975                                 goto error;
1976                         }
1977                         while (cgiFormFileRead(fp, buffer, 
1978                                 sizeof(buffer), &got) == cgiFormSuccess)
1979                         {
1980                                 if (((int) fwrite(buffer, 1, got, out)) != got) {
1981                                         cgiFormFileClose(fp);
1982                                         goto error;
1983                                 }
1984                         }
1985                         if (cgiFormFileClose(fp) != cgiFormSuccess) {
1986                                 goto error;
1987                         }
1988                 } else {
1989                         if (!cgiWriteInt(out, 0)) {
1990                                 goto error;
1991                         }
1992                 }
1993                 e = e->next;
1994         }
1995         fclose(out);
1996         return cgiEnvironmentSuccess;
1997 error:
1998         fclose(out);
1999         /* If this function is not defined in your system,
2000                 you must substitute the appropriate 
2001                 file-deletion function. */
2002         unlink(filename);
2003         return cgiEnvironmentIO;
2004 }
2005
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) {
2010                 return 0;
2011         }
2012         return 1;
2013 }
2014
2015 static int cgiWriteInt(FILE *out, int i) {
2016         if (!fwrite(&i, sizeof(int), 1, out)) {
2017                 return 0;
2018         }
2019         return 1;
2020 }
2021
2022 static int cgiReadString(FILE *out, char **s);
2023
2024 static int cgiReadInt(FILE *out, int *i);
2025
2026 cgiEnvironmentResultType cgiReadEnvironment(char *filename) {
2027         FILE *in;
2028         cgiFormEntry *e = 0, *p;
2029         char *version;
2030         /* Prevent compiler warnings */
2031         cgiEnvironmentResultType result = cgiEnvironmentIO;
2032         /* Free any existing data first */
2033         cgiFreeResources();
2034         /* Be sure to open in binary mode */
2035         in = fopen(filename, "rb");
2036         if (!in) {
2037                 /* Can't access file */
2038                 return cgiEnvironmentIO;
2039         }
2040         if (!cgiReadString(in, &version)) {
2041                 goto error;
2042         }
2043         if (strcmp(version, "CGIC" CGIC_VERSION)) {
2044                 /* 2.02: Merezko Oleg */
2045                 free(version);
2046                 return cgiEnvironmentWrongVersion;
2047         }       
2048         /* 2.02: Merezko Oleg */
2049         free(version);
2050         if (!cgiReadString(in, &cgiServerSoftware)) {
2051                 goto error;
2052         }
2053         if (!cgiReadString(in, &cgiServerName)) {
2054                 goto error;
2055         }
2056         if (!cgiReadString(in, &cgiGatewayInterface)) {
2057                 goto error;
2058         }
2059         if (!cgiReadString(in, &cgiServerProtocol)) {
2060                 goto error;
2061         }
2062         if (!cgiReadString(in, &cgiServerPort)) {
2063                 goto error;
2064         }
2065         if (!cgiReadString(in, &cgiRequestMethod)) {
2066                 goto error;
2067         }
2068         if (!cgiReadString(in, &cgiPathInfo)) {
2069                 goto error;
2070         }
2071         if (!cgiReadString(in, &cgiPathTranslated)) {
2072                 goto error;
2073         }
2074         if (!cgiReadString(in, &cgiScriptName)) {
2075                 goto error;
2076         }
2077         if (!cgiReadString(in, &cgiQueryString)) {
2078                 goto error;
2079         }
2080         if (!cgiReadString(in, &cgiRemoteHost)) {
2081                 goto error;
2082         }
2083         if (!cgiReadString(in, &cgiRemoteAddr)) {
2084                 goto error;
2085         }
2086         if (!cgiReadString(in, &cgiAuthType)) {
2087                 goto error;
2088         }
2089         if (!cgiReadString(in, &cgiRemoteUser)) {
2090                 goto error;
2091         }
2092         if (!cgiReadString(in, &cgiRemoteIdent)) {
2093                 goto error;
2094         }
2095         if (!cgiReadString(in, &cgiContentType)) {
2096                 goto error;
2097         }
2098         if (!cgiReadString(in, &cgiAccept)) {
2099                 goto error;
2100         }
2101         if (!cgiReadString(in, &cgiUserAgent)) {
2102                 goto error;
2103         }
2104         if (!cgiReadString(in, &cgiReferrer)) {
2105                 goto error;
2106         }
2107         /* 2.0 */
2108         if (!cgiReadString(in, &cgiCookie)) {
2109                 goto error;
2110         }
2111         if (!cgiReadInt(in, &cgiContentLength)) {
2112                 goto error;
2113         }
2114         p = 0;
2115         while (1) {
2116                 int fileFlag;
2117                 e = (cgiFormEntry *) calloc(1, sizeof(cgiFormEntry));
2118                 if (!e) {
2119                         cgiFreeResources();
2120                         fclose(in);
2121                         return cgiEnvironmentMemory;
2122                 }
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 */
2127                         free(e);
2128                         break;
2129                 }
2130                 if (!cgiReadString(in, &e->value)) {
2131                         goto outOfMemory;
2132                 }
2133                 if (!cgiReadString(in, &e->fileName)) {
2134                         goto outOfMemory;
2135                 }
2136                 if (!cgiReadString(in, &e->contentType)) {
2137                         goto outOfMemory;
2138                 }
2139                 if (!cgiReadInt(in, &e->valueLength)) {
2140                         goto outOfMemory;
2141                 }
2142                 if (!cgiReadInt(in, &fileFlag)) {
2143                         goto outOfMemory;
2144                 }
2145                 if (fileFlag) {
2146                         char buffer[1024];
2147                         FILE *out;
2148                         char tfileName[1024];
2149                         int got;
2150                         int len = e->valueLength;
2151                         if (getTempFileName(tfileName)
2152                                 != cgiParseSuccess)
2153                         {
2154                                 result = cgiEnvironmentIO;
2155                                 goto error;
2156                         }
2157                         out = fopen(tfileName, "w+b");
2158                         if (!out) {
2159                                 result = cgiEnvironmentIO;
2160                                 goto error;
2161                         }
2162                         while (len > 0) {               
2163                                 /* 2.01: try is a bad variable name in
2164                                         C++, and it wasn't being used
2165                                         properly either */
2166                                 int tryr = len;
2167                                 if (tryr > ((int) sizeof(buffer))) {
2168                                         tryr = sizeof(buffer);
2169                                 }
2170                                 got = fread(buffer, 1, tryr, in);
2171                                 if (got <= 0) {
2172                                         result = cgiEnvironmentIO;
2173                                         fclose(out);
2174                                         unlink(tfileName);
2175                                         goto error;
2176                                 }
2177                                 if (((int) fwrite(buffer, 1, got, out)) != got) {
2178                                         result = cgiEnvironmentIO;
2179                                         fclose(out);
2180                                         unlink(tfileName);
2181                                         goto error;
2182                                 }
2183                                 len -= got;
2184                         }
2185                         rewind(out);
2186                         e->tfileName = (char *) malloc((int) strlen(tfileName) + 1);
2187                         if (!e->tfileName) {
2188                                 result = cgiEnvironmentMemory;
2189                                 unlink(tfileName);
2190                                 goto error;
2191                         }
2192                         strcpy(e->tfileName, tfileName);
2193                 } else {
2194                         e->tfileName = (char *) malloc(1);
2195                         if (!e->tfileName) {
2196                                 result = cgiEnvironmentMemory;
2197                                 goto error;
2198                         }
2199                 }       
2200                 e->next = 0;
2201                 if (p) {
2202                         p->next = e;
2203                 } else {
2204                         cgiFormEntryFirst = e;
2205                 }       
2206                 p = e;
2207         }
2208         fclose(in);
2209         cgiRestored = 1;
2210         return cgiEnvironmentSuccess;
2211 outOfMemory:
2212         result = cgiEnvironmentMemory;
2213 error:
2214         cgiFreeResources();
2215         fclose(in);
2216         if (e) {
2217                 if (e->attr) {
2218                         free(e->attr);
2219                 }
2220                 if (e->value) {
2221                         free(e->value);
2222                 }
2223                 if (e->fileName) {
2224                         free(e->fileName);
2225                 }
2226                 if (e->contentType) {
2227                         free(e->contentType);
2228                 }
2229                 if (e->tfileName) {
2230                         free(e->tfileName);
2231                 }
2232                 free(e);
2233         }
2234         return result;
2235 }
2236
2237 static int cgiReadString(FILE *in, char **s) {
2238         int len;
2239         /* 2.0 fix: test cgiReadInt for failure! */ 
2240         if (!cgiReadInt(in, &len)) {
2241                 return 0;
2242         }
2243         *s = (char *) malloc(len + 1);
2244         if (!(*s)) {
2245                 return 0;
2246         }       
2247         if (((int) fread(*s, 1, len, in)) != len) {
2248                 return 0;
2249         }
2250         (*s)[len] = '\0';
2251         return 1;
2252 }
2253
2254 static int cgiReadInt(FILE *out, int *i) {
2255         if (!fread(i, sizeof(int), 1, out)) {
2256                 return 0;
2257         }
2258         return 1;
2259 }
2260
2261 static int cgiStrEqNc(char *s1, char *s2) {
2262         while(1) {
2263                 if (!(*s1)) {
2264                         if (!(*s2)) {
2265                                 return 1;
2266                         } else {
2267                                 return 0;
2268                         }
2269                 } else if (!(*s2)) {
2270                         return 0;
2271                 }
2272                 if (isalpha(*s1)) {
2273                         if (tolower(*s1) != tolower(*s2)) {
2274                                 return 0;
2275                         }
2276                 } else if ((*s1) != (*s2)) {
2277                         return 0;
2278                 }
2279                 s1++;
2280                 s2++;
2281         }
2282 }
2283
2284 static int cgiStrBeginsNc(char *s1, char *s2) {
2285         while(1) {
2286                 if (!(*s2)) {
2287                         return 1;
2288                 } else if (!(*s1)) {
2289                         return 0;
2290                 }
2291                 if (isalpha(*s1)) {
2292                         if (tolower(*s1) != tolower(*s2)) {
2293                                 return 0;
2294                         }
2295                 } else if ((*s1) != (*s2)) {
2296                         return 0;
2297                 }
2298                 s1++;
2299                 s2++;
2300         }
2301 }
2302
2303 static char *cgiFindTarget = 0;
2304 static cgiFormEntry *cgiFindPos = 0;
2305
2306 static cgiFormEntry *cgiFormEntryFindFirst(char *name) {
2307         cgiFindTarget = name;
2308         cgiFindPos = cgiFormEntryFirst;
2309         return cgiFormEntryFindNext();
2310 }
2311
2312 static cgiFormEntry *cgiFormEntryFindNext() {
2313         while (cgiFindPos) {
2314                 cgiFormEntry *c = cgiFindPos;
2315                 cgiFindPos = c->next;
2316                 if (!strcmp(c -> attr, cgiFindTarget)) {
2317                         return c;
2318                 }
2319         }
2320         return 0;
2321 }
2322
2323 static int cgiFirstNonspaceChar(char *s) {
2324         int len = strspn(s, " \n\r\t");
2325         return s[len];
2326 }
2327
2328 void cgiStringArrayFree(char **stringArray) {
2329         char *p;
2330         char **arrayItself = stringArray;
2331         p = *stringArray;
2332         while (p) {
2333                 free(p);
2334                 stringArray++;
2335                 p = *stringArray;
2336         }
2337         /* 2.0: free the array itself! */
2338         free(arrayItself);
2339 }       
2340
2341 cgiFormResultType cgiCookies(char ***result) {
2342         char **stringArray;
2343         int i;
2344         int total = 0;
2345         char *p;
2346         char *n;
2347         p = cgiCookie;
2348         while (*p) {
2349                 if (*p == '=') {
2350                         total++;
2351                 }
2352                 p++;
2353         }
2354         stringArray = (char **) malloc(sizeof(char *) * (total + 1));
2355         if (!stringArray) {
2356                 *result = 0;
2357                 return cgiFormMemory;
2358         }
2359         /* initialize all entries to null; the last will stay that way */
2360         for (i=0; (i <= total); i++) {
2361                 stringArray[i] = 0;
2362         }
2363         i = 0;
2364         p = cgiCookie;
2365         while (*p) {
2366                 while (*p && isspace(*p)) {
2367                         p++;
2368                 }
2369                 n = p;
2370                 while (*p && (*p != '=')) {
2371                         p++;
2372                 }
2373                 if (p != n) {
2374                         stringArray[i] = (char *) malloc((p - n) + 1);
2375                         if (!stringArray[i]) {
2376                                 cgiStringArrayFree(stringArray);
2377                                 *result = 0;
2378                                 return cgiFormMemory;
2379                         }       
2380                         memcpy(stringArray[i], n, p - n);
2381                         stringArray[i][p - n] = '\0';
2382                         i++;
2383                 }
2384                 while (*p && (*p != ';')) {
2385                         p++;    
2386                 }
2387                 if (!*p) {
2388                         break;
2389                 }
2390                 if (*p == ';') {
2391                         p++;
2392                 }
2393         }
2394         *result = stringArray;
2395         return cgiFormSuccess;
2396 }
2397
2398 cgiFormResultType cgiFormEntries(char ***result) {
2399         char **stringArray;
2400         cgiFormEntry *e, *pe;
2401         int i;
2402         int total = 0;
2403         e = cgiFormEntryFirst;
2404         while (e) {
2405                 /* Don't count a field name more than once if
2406                         multiple values happen to be present for it */
2407                 pe = cgiFormEntryFirst;
2408                 while (pe != e) {
2409                         if (!strcmp(e->attr, pe->attr)) {
2410                                 goto skipSecondValue;
2411                         }
2412                         pe = pe->next;                                  
2413                 }
2414                 total++;
2415 skipSecondValue:
2416                 e = e->next;
2417         }
2418         stringArray = (char **) malloc(sizeof(char *) * (total + 1));
2419         if (!stringArray) {
2420                 *result = 0;
2421                 return cgiFormMemory;
2422         }
2423         /* initialize all entries to null; the last will stay that way */
2424         for (i=0; (i <= total); i++) {
2425                 stringArray[i] = 0;
2426         }
2427         /* Now go get the entries */
2428         e = cgiFormEntryFirst;
2429         i = 0;
2430         while (e) {
2431                 int space;
2432                 /* Don't return a field name more than once if
2433                         multiple values happen to be present for it */
2434                 pe = cgiFormEntryFirst;
2435                 while (pe != e) {
2436                         if (!strcmp(e->attr, pe->attr)) {
2437                                 goto skipSecondValue2;
2438                         }
2439                         pe = pe->next;                                  
2440                 }               
2441                 space = (int) strlen(e->attr) + 1;
2442                 stringArray[i] = (char *) malloc(space);
2443                 if (stringArray[i] == 0) {
2444                         /* Memory problems */
2445                         cgiStringArrayFree(stringArray);
2446                         *result = 0;
2447                         return cgiFormMemory;
2448                 }       
2449                 strcpy(stringArray[i], e->attr);
2450                 i++;
2451 skipSecondValue2:
2452                 e = e->next;
2453         }
2454         *result = stringArray;
2455         return cgiFormSuccess;
2456 }
2457
2458 #define TRYPUTC(ch) \
2459         { \
2460                 if (putc((ch), cgiOut) == EOF) { \
2461                         return cgiFormIO; \
2462                 } \
2463         } 
2464
2465 cgiFormResultType cgiHtmlEscapeData(char *data, int len)
2466 {
2467         while (len--) {
2468                 if (*data == '<') {
2469                         TRYPUTC('&');
2470                         TRYPUTC('l');
2471                         TRYPUTC('t');
2472                         TRYPUTC(';');
2473                 } else if (*data == '&') {
2474                         TRYPUTC('&');
2475                         TRYPUTC('a');
2476                         TRYPUTC('m');
2477                         TRYPUTC('p');
2478                         TRYPUTC(';');
2479                 } else if (*data == '>') {
2480                         TRYPUTC('&');
2481                         TRYPUTC('g');
2482                         TRYPUTC('t');
2483                         TRYPUTC(';');
2484                 } else {
2485                         TRYPUTC(*data);
2486                 }
2487                 data++;
2488         }
2489         return cgiFormSuccess;
2490 }
2491
2492 cgiFormResultType cgiHtmlEscape(char *s)
2493 {
2494         return cgiHtmlEscapeData(s, (int) strlen(s));
2495 }
2496
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)
2504 {
2505         while (len--) {
2506                 if (*data == '\"') {
2507                         TRYPUTC('&');
2508                         TRYPUTC('#');
2509                         TRYPUTC('3');
2510                         TRYPUTC('4');
2511                         TRYPUTC(';');
2512                 } else {
2513                         TRYPUTC(*data);
2514                 }
2515                 data++;
2516         }
2517         return cgiFormSuccess;
2518 }
2519
2520 cgiFormResultType cgiValueEscape(char *s)
2521 {
2522         return cgiValueEscapeData(s, (int) strlen(s));
2523 }
2524
2525