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

Last change on this file since 383 was 379, checked in by djay, 12 years ago

Fix handling of multiple outputs. Small fix to build with bleeding edge gcc. Add missing copyright in service_internal_ms.c/h. Better gesture of session data. Update HISTORY.txt content.

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

Search

Context Navigation

ZOO Sponsors

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

Become a sponsor !

Knowledge partners

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

Become a knowledge partner

Related links

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