source: trunk/thirds/cgic206/cgic.c @ 97

Last change on this file since 97 was 97, checked in by djay, 13 years ago

Keep the default behavior to correctly decode url. Avoid any decoding when the method is POST.

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

Search

ZOO Sponsors

http://www.zoo-project.org/trac/chrome/site/img/geolabs-logo.pnghttp://www.zoo-project.org/trac/chrome/site/img/neogeo-logo.png http://www.zoo-project.org/trac/chrome/site/img/apptech-logo.png http://www.zoo-project.org/trac/chrome/site/img/3liz-logo.png http://www.zoo-project.org/trac/chrome/site/img/gateway-logo.png

Become a sponsor !

Knowledge partners

http://www.zoo-project.org/trac/chrome/site/img/ocu-logo.png http://www.zoo-project.org/trac/chrome/site/img/gucas-logo.png http://www.zoo-project.org/trac/chrome/site/img/polimi-logo.png http://www.zoo-project.org/trac/chrome/site/img/fem-logo.png http://www.zoo-project.org/trac/chrome/site/img/supsi-logo.png http://www.zoo-project.org/trac/chrome/site/img/cumtb-logo.png

Become a knowledge partner

Related links

http://zoo-project.org/img/ogclogo.png http://zoo-project.org/img/osgeologo.png