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

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

Search

ZOO Sponsors

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

Become a sponsor !

Knowledge partners

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

Become a knowledge partner

Related links

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