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

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

Fix the cgic library to avoid removing '+' characters form the input XML file or value.

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