source: branches/PublicaMundi_David-devel/thirds/cgic206/cgic.c @ 737

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

Search

Context Navigation

ZOO Sponsors

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

Become a sponsor !

Knowledge partners

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

Become a knowledge partner

Related links

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