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

Last change on this file since 74 was 6, checked in by djay, 14 years ago

Update the cgic library to let run the ZOO Kernel from the command line.

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