source: tags/0.56/cgic205/cgic.cpp @ 9

Last change on this file since 9 was 9, checked in by willem, 11 years ago

willem

File size: 54.0 KB
Line 
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
46char *cgiServerSoftware;
47char *cgiServerName;
48char *cgiGatewayInterface;
49char *cgiServerProtocol;
50char *cgiServerPort;
51char *cgiRequestMethod;
52char *cgiPathInfo;
53char *cgiPathTranslated;
54char *cgiScriptName;
55char *cgiQueryString;
56char *cgiRemoteHost;
57char *cgiRemoteAddr;
58char *cgiAuthType;
59char *cgiRemoteUser;
60char *cgiRemoteIdent;
61char cgiContentTypeData[1024];
62char *cgiContentType = cgiContentTypeData;
63char *cgiMultipartBoundary;
64char *cgiCookie;
65int cgiContentLength;
66char *cgiAccept;
67char *cgiUserAgent;
68char *cgiReferrer;
69
70FILE *cgiIn;
71FILE *cgiOut;
72
73/* True if CGI environment was restored from a file. */
74static int cgiRestored = 0;
75
76static void cgiGetenv(char **s, char *var);
77
78typedef 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
92typedef 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. */
111static cgiFormEntry *cgiFormEntryFirst;
112
113static cgiParseResultType cgiParseGetFormInput();
114static cgiParseResultType cgiParsePostFormInput();
115static cgiParseResultType cgiParsePostMultipartInput();
116static cgiParseResultType cgiParseFormInput(char *data, int length);
117static void cgiSetupConstants();
118static void cgiFreeResources();
119static int cgiStrEqNc(char *s1, char *s2);
120static int cgiStrBeginsNc(char *s1, char *s2);
121
122int 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
292static void cgiGetenv(char **s, char *var){
293        *s = getenv(var);
294        if (!(*s)) {
295                *s = "";
296        }
297}
298
299static 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
323typedef 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
341int 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
382void 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
406static cgiParseResultType afterNextBoundary(mpStreamPtr mpp,
407        FILE *outf,
408        char **outP,
409        int *bodyLengthP,
410        int first
411        );
412
413static int readHeaderLine(
414        mpStreamPtr mpp,       
415        char *attr,
416        int attrSpace,
417        char *value,
418        int valueSpace);
419
420static 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       
432static cgiParseResultType getTempFileName(char *tfileName);
433
434static 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;
590outOfMemory:
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
619static 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
683cgiParseResultType 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;
776outOfMemory:
777        result = cgiParseMemory;
778        if (outP) {
779                if (out) {
780                        free(out);
781                }
782                *outP = '\0';   
783        }
784error:
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
797static 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
904static 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
965static cgiParseResultType cgiParseGetFormInput() {
966        return cgiParseFormInput(cgiQueryString, cgiContentLength);
967}
968
969typedef enum {
970        cgiEscapeRest,
971        cgiEscapeFirst,
972        cgiEscapeSecond
973} cgiEscapeState;
974
975typedef enum {
976        cgiUnescapeSuccess,
977        cgiUnescapeMemory
978} cgiUnescapeResultType;
979
980static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
981
982static 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
1079static int cgiHexValue[256];
1080
1081cgiUnescapeResultType 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       
1120static 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
1149static 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
1194static cgiFormResultType cgiFormEntryString(
1195        cgiFormEntry *e, char *result, int max, int newlines);
1196
1197static cgiFormEntry *cgiFormEntryFindFirst(char *name);
1198static cgiFormEntry *cgiFormEntryFindNext();
1199
1200cgiFormResultType 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
1211cgiFormResultType 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
1239cgiFormResultType 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
1269cgiFormResultType 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
1292typedef struct cgiFileStruct {
1293        FILE *in;
1294} cgiFile;
1295
1296cgiFormResultType 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
1324cgiFormResultType 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
1340cgiFormResultType cgiFormFileClose(cgiFilePtr cfp)
1341{
1342        if (!cfp) {
1343                return cgiFormOpenFailed;
1344        }
1345        fclose(cfp->in);
1346        free(cfp);
1347        return cgiFormSuccess;
1348}
1349
1350cgiFormResultType 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
1361cgiFormResultType 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
1425cgiFormResultType 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
1437static 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
1517static int cgiFirstNonspaceChar(char *s);
1518
1519cgiFormResultType 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
1542cgiFormResultType 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
1559cgiFormResultType 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
1582cgiFormResultType 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
1599cgiFormResultType 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
1635cgiFormResultType 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
1675cgiFormResultType cgiFormCheckboxSingle(
1676        char *name)
1677{
1678        cgiFormEntry *e;
1679        e = cgiFormEntryFindFirst(name);
1680        if (!e) {
1681                return cgiFormNotFound;
1682        }
1683        return cgiFormSuccess;
1684}
1685
1686extern 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
1695cgiFormResultType 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
1704cgiFormResultType 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
1777cgiFormResultType 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
1793void 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
1801char *days[] = {
1802        "Sun",
1803        "Mon",
1804        "Tue",
1805        "Wed",
1806        "Thu",
1807        "Fri",
1808        "Sat"
1809};
1810
1811char *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
1826void 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
1858void cgiHeaderLocation(char *redirectUrl) {
1859        fprintf(cgiOut, "Location: %s\r\n\r\n", redirectUrl);
1860}
1861
1862void cgiHeaderStatus(int status, char *statusMessage) {
1863        fprintf(cgiOut, "Status: %d %s\r\n\r\n", status, statusMessage);
1864}
1865
1866void cgiHeaderContentType(char *mimeType) {
1867        fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType);
1868}
1869
1870static int cgiWriteString(FILE *out, char *s);
1871
1872static int cgiWriteInt(FILE *out, int i);
1873
1874#define CGIC_VERSION "2.0"
1875
1876cgiEnvironmentResultType 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;
1997error:
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
2006static 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
2015static int cgiWriteInt(FILE *out, int i) {
2016        if (!fwrite(&i, sizeof(int), 1, out)) {
2017                return 0;
2018        }
2019        return 1;
2020}
2021
2022static int cgiReadString(FILE *out, char **s);
2023
2024static int cgiReadInt(FILE *out, int *i);
2025
2026cgiEnvironmentResultType 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                        /* cgic 2.05: should be fclose not rewind */
2186                        fclose(out);
2187                        e->tfileName = (char *) malloc((int) strlen(tfileName) + 1);
2188                        if (!e->tfileName) {
2189                                result = cgiEnvironmentMemory;
2190                                unlink(tfileName);
2191                                goto error;
2192                        }
2193                        strcpy(e->tfileName, tfileName);
2194                } else {
2195                        e->tfileName = (char *) malloc(1);
2196                        if (!e->tfileName) {
2197                                result = cgiEnvironmentMemory;
2198                                goto error;
2199                        }
2200                }       
2201                e->next = 0;
2202                if (p) {
2203                        p->next = e;
2204                } else {
2205                        cgiFormEntryFirst = e;
2206                }       
2207                p = e;
2208        }
2209        fclose(in);
2210        cgiRestored = 1;
2211        return cgiEnvironmentSuccess;
2212outOfMemory:
2213        result = cgiEnvironmentMemory;
2214error:
2215        cgiFreeResources();
2216        fclose(in);
2217        if (e) {
2218                if (e->attr) {
2219                        free(e->attr);
2220                }
2221                if (e->value) {
2222                        free(e->value);
2223                }
2224                if (e->fileName) {
2225                        free(e->fileName);
2226                }
2227                if (e->contentType) {
2228                        free(e->contentType);
2229                }
2230                if (e->tfileName) {
2231                        free(e->tfileName);
2232                }
2233                free(e);
2234        }
2235        return result;
2236}
2237
2238static int cgiReadString(FILE *in, char **s) {
2239        int len;
2240        /* 2.0 fix: test cgiReadInt for failure! */ 
2241        if (!cgiReadInt(in, &len)) {
2242                return 0;
2243        }
2244        *s = (char *) malloc(len + 1);
2245        if (!(*s)) {
2246                return 0;
2247        }       
2248        if (((int) fread(*s, 1, len, in)) != len) {
2249                return 0;
2250        }
2251        (*s)[len] = '\0';
2252        return 1;
2253}
2254
2255static int cgiReadInt(FILE *out, int *i) {
2256        if (!fread(i, sizeof(int), 1, out)) {
2257                return 0;
2258        }
2259        return 1;
2260}
2261
2262static int cgiStrEqNc(char *s1, char *s2) {
2263        while(1) {
2264                if (!(*s1)) {
2265                        if (!(*s2)) {
2266                                return 1;
2267                        } else {
2268                                return 0;
2269                        }
2270                } else if (!(*s2)) {
2271                        return 0;
2272                }
2273                if (isalpha(*s1)) {
2274                        if (tolower(*s1) != tolower(*s2)) {
2275                                return 0;
2276                        }
2277                } else if ((*s1) != (*s2)) {
2278                        return 0;
2279                }
2280                s1++;
2281                s2++;
2282        }
2283}
2284
2285static int cgiStrBeginsNc(char *s1, char *s2) {
2286        while(1) {
2287                if (!(*s2)) {
2288                        return 1;
2289                } else if (!(*s1)) {
2290                        return 0;
2291                }
2292                if (isalpha(*s1)) {
2293                        if (tolower(*s1) != tolower(*s2)) {
2294                                return 0;
2295                        }
2296                } else if ((*s1) != (*s2)) {
2297                        return 0;
2298                }
2299                s1++;
2300                s2++;
2301        }
2302}
2303
2304static char *cgiFindTarget = 0;
2305static cgiFormEntry *cgiFindPos = 0;
2306
2307static cgiFormEntry *cgiFormEntryFindFirst(char *name) {
2308        cgiFindTarget = name;
2309        cgiFindPos = cgiFormEntryFirst;
2310        return cgiFormEntryFindNext();
2311}
2312
2313static cgiFormEntry *cgiFormEntryFindNext() {
2314        while (cgiFindPos) {
2315                cgiFormEntry *c = cgiFindPos;
2316                cgiFindPos = c->next;
2317                if (!strcmp(c -> attr, cgiFindTarget)) {
2318                        return c;
2319                }
2320        }
2321        return 0;
2322}
2323
2324static int cgiFirstNonspaceChar(char *s) {
2325        int len = strspn(s, " \n\r\t");
2326        return s[len];
2327}
2328
2329void cgiStringArrayFree(char **stringArray) {
2330        char *p;
2331        char **arrayItself = stringArray;
2332        p = *stringArray;
2333        while (p) {
2334                free(p);
2335                stringArray++;
2336                p = *stringArray;
2337        }
2338        /* 2.0: free the array itself! */
2339        free(arrayItself);
2340}       
2341
2342cgiFormResultType cgiCookies(char ***result) {
2343        char **stringArray;
2344        int i;
2345        int total = 0;
2346        char *p;
2347        char *n;
2348        p = cgiCookie;
2349        while (*p) {
2350                if (*p == '=') {
2351                        total++;
2352                }
2353                p++;
2354        }
2355        stringArray = (char **) malloc(sizeof(char *) * (total + 1));
2356        if (!stringArray) {
2357                *result = 0;
2358                return cgiFormMemory;
2359        }
2360        /* initialize all entries to null; the last will stay that way */
2361        for (i=0; (i <= total); i++) {
2362                stringArray[i] = 0;
2363        }
2364        i = 0;
2365        p = cgiCookie;
2366        while (*p) {
2367                while (*p && isspace(*p)) {
2368                        p++;
2369                }
2370                n = p;
2371                while (*p && (*p != '=')) {
2372                        p++;
2373                }
2374                if (p != n) {
2375                        stringArray[i] = (char *) malloc((p - n) + 1);
2376                        if (!stringArray[i]) {
2377                                cgiStringArrayFree(stringArray);
2378                                *result = 0;
2379                                return cgiFormMemory;
2380                        }       
2381                        memcpy(stringArray[i], n, p - n);
2382                        stringArray[i][p - n] = '\0';
2383                        i++;
2384                }
2385                while (*p && (*p != ';')) {
2386                        p++;   
2387                }
2388                if (!*p) {
2389                        break;
2390                }
2391                if (*p == ';') {
2392                        p++;
2393                }
2394        }
2395        *result = stringArray;
2396        return cgiFormSuccess;
2397}
2398
2399cgiFormResultType cgiFormEntries(char ***result) {
2400        char **stringArray;
2401        cgiFormEntry *e, *pe;
2402        int i;
2403        int total = 0;
2404        e = cgiFormEntryFirst;
2405        while (e) {
2406                /* Don't count a field name more than once if
2407                        multiple values happen to be present for it */
2408                pe = cgiFormEntryFirst;
2409                while (pe != e) {
2410                        if (!strcmp(e->attr, pe->attr)) {
2411                                goto skipSecondValue;
2412                        }
2413                        pe = pe->next;                                 
2414                }
2415                total++;
2416skipSecondValue:
2417                e = e->next;
2418        }
2419        stringArray = (char **) malloc(sizeof(char *) * (total + 1));
2420        if (!stringArray) {
2421                *result = 0;
2422                return cgiFormMemory;
2423        }
2424        /* initialize all entries to null; the last will stay that way */
2425        for (i=0; (i <= total); i++) {
2426                stringArray[i] = 0;
2427        }
2428        /* Now go get the entries */
2429        e = cgiFormEntryFirst;
2430        i = 0;
2431        while (e) {
2432                int space;
2433                /* Don't return a field name more than once if
2434                        multiple values happen to be present for it */
2435                pe = cgiFormEntryFirst;
2436                while (pe != e) {
2437                        if (!strcmp(e->attr, pe->attr)) {
2438                                goto skipSecondValue2;
2439                        }
2440                        pe = pe->next;                                 
2441                }               
2442                space = (int) strlen(e->attr) + 1;
2443                stringArray[i] = (char *) malloc(space);
2444                if (stringArray[i] == 0) {
2445                        /* Memory problems */
2446                        cgiStringArrayFree(stringArray);
2447                        *result = 0;
2448                        return cgiFormMemory;
2449                }       
2450                strcpy(stringArray[i], e->attr);
2451                i++;
2452skipSecondValue2:
2453                e = e->next;
2454        }
2455        *result = stringArray;
2456        return cgiFormSuccess;
2457}
2458
2459#define TRYPUTC(ch) \
2460        { \
2461                if (putc((ch), cgiOut) == EOF) { \
2462                        return cgiFormIO; \
2463                } \
2464        }
2465
2466cgiFormResultType cgiHtmlEscapeData(char *data, int len)
2467{
2468        while (len--) {
2469                if (*data == '<') {
2470                        TRYPUTC('&');
2471                        TRYPUTC('l');
2472                        TRYPUTC('t');
2473                        TRYPUTC(';');
2474                } else if (*data == '&') {
2475                        TRYPUTC('&');
2476                        TRYPUTC('a');
2477                        TRYPUTC('m');
2478                        TRYPUTC('p');
2479                        TRYPUTC(';');
2480                } else if (*data == '>') {
2481                        TRYPUTC('&');
2482                        TRYPUTC('g');
2483                        TRYPUTC('t');
2484                        TRYPUTC(';');
2485                } else {
2486                        TRYPUTC(*data);
2487                }
2488                data++;
2489        }
2490        return cgiFormSuccess;
2491}
2492
2493cgiFormResultType cgiHtmlEscape(char *s)
2494{
2495        return cgiHtmlEscapeData(s, (int) strlen(s));
2496}
2497
2498/* Output data with the " character HTML-escaped, and no
2499        other characters escaped. This is useful when outputting
2500        the contents of a tag attribute such as 'href' or 'src'.
2501        'data' is not null-terminated; 'len' is the number of
2502        bytes in 'data'. Returns cgiFormIO in the event
2503        of error, cgiFormSuccess otherwise. */
2504cgiFormResultType cgiValueEscapeData(char *data, int len)
2505{
2506        while (len--) {
2507                if (*data == '\"') {
2508                        TRYPUTC('&');
2509                        TRYPUTC('#');
2510                        TRYPUTC('3');
2511                        TRYPUTC('4');
2512                        TRYPUTC(';');
2513                } else {
2514                        TRYPUTC(*data);
2515                }
2516                data++;
2517        }
2518        return cgiFormSuccess;
2519}
2520
2521cgiFormResultType cgiValueEscape(char *s)
2522{
2523        return cgiValueEscapeData(s, (int) strlen(s));
2524}
2525
2526
Note: See TracBrowser for help on using the repository browser.