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

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

Remove request length display and fix Makefile for MacOS X.

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

Search

ZOO Sponsors

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

Become a sponsor !

Knowledge partners

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

Become a knowledge partner

Related links

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