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

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

Initial ZOO SVN Repository Import.

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

Search

Context Navigation

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