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

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

Add WIN32 platform support. Fix for values containing @ passed as KVP.

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