Update to the new FSF address
[infodrom/cgilib] / cgi.c
1 /*
2     cgi.c - Some simple routines for CGI programming
3     Copyright (c) 1996-9,2007,8  Martin Schulze <joey@infodrom.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software Foundation
17     Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19
20 #define _GNU_SOURCE 1
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <strings.h>
27 #include <ctype.h>
28 #include <syslog.h>
29 #include <cgi.h>
30 #include "aux.h"
31
32 char *cgiHeaderString = NULL;
33 char *cgiType = NULL;
34
35 extern s_cookie **cgiReadCookies();
36
37 int cgiSetHeader (const char *name, const char *value)
38 {
39     const char *cp, *vp;
40     char *pivot;
41     int len;
42
43     if (!name || !strlen (name) || !value || !strlen (value))
44         return 0;
45     
46     for (cp=name;*cp && *cp!=' ' && *cp!='\r' && *cp!='\n' && *cp!=':';cp++);
47     for (vp=value;*vp && *vp!='\r' && *vp!='\n';vp++);
48
49     if (cgiHeaderString) {
50         len = (strlen (cgiHeaderString) + cp-name + vp-value + 5) * sizeof (char);
51         if ((pivot = (char *)realloc (cgiHeaderString,len)) == NULL)
52             return 0;
53         cgiHeaderString = pivot;
54         pivot += strlen (cgiHeaderString);
55     } else {
56         len = (cp-name + vp-value + 5) * sizeof (char);
57         if ((cgiHeaderString = (char *)malloc (len)) == NULL)
58             return 0;
59         pivot = cgiHeaderString;
60     }
61     strncpy (pivot, name, cp-name);
62     strncat (pivot, ": ", 2);
63     strncat (pivot, value, vp-value);
64     strncat (pivot, "\r\n", 2);
65
66     return 1;
67 }
68
69 int cgiSetType (const char *type)
70 {
71     int len;
72     const char *cp;
73
74     if (!type || !strlen (type))
75         return 0;
76     if (cgiType)
77         free (cgiType);
78
79     for (cp=type;*cp && *cp!='\r' && *cp!='\n';cp++);
80
81     len = (cp-type+1) * sizeof (char);
82     if ((cgiType = (char *)malloc (len+20)) == NULL)
83         return 0;
84     memset (cgiType, 0, len);
85     strncpy (cgiType, type, cp-type);
86     
87     return 1;
88 }
89
90 void cgiHeader ()
91 {
92     if (cgiType)
93         printf ("Content-type: %s\r\n", cgiType);
94     else
95         printf ("Content-type: text/html\r\n");
96     if (cgiHeaderString)
97         printf ("%s", cgiHeaderString);
98     printf ("\r\n");
99 }
100
101 void cgiDebug (int level, int where)
102 {
103     if (level > 0)
104         cgiDebugLevel = level;
105     else
106         cgiDebugLevel = 0;
107     if (where > 0) {
108         if (where < 3) {
109             cgiDebugType = where;
110             if (where == 2)
111                 openlog("cgilib", LOG_PID, LOG_USER);
112         } else
113             cgiDebugType = 0;
114     }
115 }
116
117 char *cgiDecodeString (char *text)
118 {
119     char *cp, *xp;
120
121     for (cp=text,xp=text; *cp; cp++) {
122         if (*cp == '%') {
123             if (strchr("0123456789ABCDEFabcdef", *(cp+1))
124                 && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
125                 if (islower(*(cp+1)))
126                     *(cp+1) = toupper(*(cp+1));
127                 if (islower(*(cp+2)))
128                     *(cp+2) = toupper(*(cp+2));
129                 *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
130                     + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
131                 xp++;cp+=2;
132             }
133         } else {
134             *(xp++) = *cp;
135         }
136     }
137     memset(xp, 0, cp-xp);
138     return text;
139 }
140
141 /* cgiReadFile()
142  *
143  * Read and save a file fro a multipart request
144  */
145 #include <errno.h>
146 char *cgiReadFile (FILE *stream, char *boundary)
147 {
148     char *crlfboundary, *buf;
149     size_t boundarylen;
150     int c;
151     unsigned int pivot;
152     char *cp;
153     char template[]= "/tmp/cgilibXXXXXX";
154     FILE *tmpfile;
155     int fd;
156
157     boundarylen = strlen(boundary)+3;
158     if ((crlfboundary = (char *)malloc (boundarylen)) == NULL)
159         return NULL;
160     sprintf (crlfboundary, "\r\n%s", boundary);
161
162     if ((buf = (char *)malloc (boundarylen)) == NULL) {
163         free (crlfboundary);
164         return NULL;
165     }
166     memset (buf, 0, boundarylen);
167     pivot = 0;
168
169     if ((fd = mkstemp (template)) == -1) {
170         free (crlfboundary);
171         free (buf);
172         return NULL;
173     }
174
175     if ((tmpfile = fdopen (fd, "w")) == NULL) {
176         free (crlfboundary);
177         free (buf);
178         unlink (template);
179         return NULL;
180     }
181     
182     while (!feof (stream)) {
183         c = fgetc (stream);
184
185         if (c == 0) {
186             if (strlen (buf)) {
187                 for (cp=buf; *cp; cp++)
188                     putc (*cp, tmpfile);
189                 memset (buf, 0, boundarylen);
190                 pivot = 0;
191             }
192             putc (c, tmpfile);
193             continue;
194         }
195
196         if (strlen (buf)) {
197             if (crlfboundary[pivot+1] == c) {
198                 buf[++pivot] = c;
199
200                 if (strlen (buf) == strlen (crlfboundary))
201                     break;
202                 else
203                     continue;
204             } else {
205                 for (cp=buf; *cp; cp++)
206                     putc (*cp, tmpfile);
207                 memset (buf, 0, boundarylen);
208                 pivot = 0;
209             }
210         }
211
212         if (crlfboundary[0] == c) {
213             buf[0] = c;
214         } else {
215             fputc (c, tmpfile);
216         }
217     }
218
219     if (!feof (stream))
220         fgets (buf, boundarylen, stream);
221
222     fclose (tmpfile);
223
224     free (crlfboundary);
225     free (buf);
226
227     return strdup (template);
228 }
229
230 /* cgiReadMultipart()
231  *
232  * Decode multipart/form-data
233  */
234 #define MULTIPART_DELTA 5
235 s_cgi *cgiReadMultipart (char *boundary)
236 {
237     char *line;
238     char *cp, *xp;
239     char *name = NULL, *type = NULL;
240     char *fname = NULL;
241     char *tmpfile;
242     int header = 1;
243     s_var **result = NULL;
244     s_var **tmp;
245     int numresults = 0, current = 0;
246     s_file **files = NULL;
247     s_file **tmpf;
248     s_file *file;
249     int index = 0;
250     size_t len;
251     s_cgi *res;
252     
253     while ((line = cgiGetLine (stdin)) != NULL) {
254
255         if (!strncmp (line, boundary, strlen(boundary))) {
256             header = 1;
257         } else if (!strncasecmp (line, "Content-Disposition: form-data; ", 32)) {
258             if (!name) {
259                 if ((cp = strstr (line, "name=\"")) == NULL)
260                     continue;
261                 cp += 6;
262                 if ((xp = strchr (cp, '\"')) == NULL)
263                     continue;
264                 name = strndup (cp, xp-cp);
265                 cgiDecodeString (name);
266                 cgiDebugOutput (2, "Found field name %s", name);
267
268                 if ((cp = strstr (line, "filename=\"")) == NULL)
269                     continue;
270                 cp += 10;
271                 if ((xp = strchr (cp, '\"')) == NULL)
272                     continue;
273                 fname = strndup (cp, xp-cp);
274                 cgiDecodeString (fname);
275                 cgiDebugOutput (2, "Found filename %s", fname);
276             }
277         } else if (!strncasecmp (line, "Content-Type: ", 14)) {
278             if (!type) {
279                 cp = line + 14;
280                 type = strdup (cp);
281                 cgiDebugOutput (2, "Found mime type %s", type);
282             }
283         } else if (header) {
284             if (!strlen(line)) {
285                 header = 0;
286
287                 if (fname) {
288                     header = 1;
289                     tmpfile = cgiReadFile (stdin, boundary);
290
291                     if (!tmpfile) {
292                         free (name);
293                         free (fname);
294                         if (type)
295                             free (type);
296                         name = fname = type = NULL;
297                     }
298
299                     cgiDebugOutput (2, "Wrote %s (%s) to file: %s", name, fname, tmpfile);
300
301                     if (!strlen (fname)) {
302                         cgiDebugOutput (3, "Found empty filename, removing");
303                         unlink (tmpfile);
304                         free (tmpfile);
305                         free (name);
306                         free (fname);
307                         if (type)
308                             free (type);
309                         name = fname = type = NULL;
310                     } else {
311                         if ((file = (s_file *)malloc (sizeof (s_file))) == NULL) {
312                             cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
313                             unlink (tmpfile);
314                             free (tmpfile);
315                             free (name);
316                             free (fname);
317                             if (type)
318                                 free (type);
319                             name = fname = type = NULL;
320                             continue;
321                         }
322
323                         file->name = name;
324                         file->type = type;
325                         file->tmpfile = tmpfile;
326                         if ((cp = rindex (fname, '/')) == NULL)
327                             file->filename = fname;
328                         else {
329                             file->filename = strdup (++cp);
330                             free (fname);
331                         }
332                         name = type = fname = NULL;
333
334                         if (!files) {
335                             if ((files = (s_file **)malloc(2*sizeof (s_file *))) == NULL) {
336                                 cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
337                                 unlink (tmpfile);
338                                 free (tmpfile);
339                                 free (name);
340                                 name = NULL;
341                                 if (type) {
342                                     free (type);
343                                     type = NULL;
344                                 }
345                                 free (file->filename);
346                                 free (file);
347                                 continue;
348                             }
349                             memset (files, 0, 2*sizeof (s_file *));
350                             index = 0;
351                         } else {
352                             for (index=0; files[index]; index++);
353                             if ((tmpf = (s_file **)realloc(files, (index+2)*sizeof (s_file *))) == NULL) {
354                                 cgiDebugOutput (3, "realloc failed, ignoring %s=%s", name, fname);
355                                 unlink (tmpfile);
356                                 free (tmpfile);
357                                 free (name);
358                                 if (type)
359                                     free (type);
360                                 free (file->filename);
361                                 free (file);
362                                 name = type = fname = NULL;
363                                 continue;
364                             }
365                             files = tmpf;
366                             memset (files + index, 0, 2*sizeof (s_file *));
367                         }
368                         files[index] = file;
369                     }
370                 }
371             }
372         } else {
373             if (name) {
374
375                 /* try to find out if there's already such a variable */
376                 for (index=0; index<current && strcmp (result[index]->name,name); index++);
377
378                 if (index == current) {
379                     if (!result) {
380                         len = MULTIPART_DELTA * sizeof (s_var *);
381                         if ((result = (s_var **)malloc (len)) == NULL) {
382                             free (name);
383                             if (type)
384                                 free (type);
385                             return NULL;
386                         }
387                         numresults = MULTIPART_DELTA;
388                         memset (result, 0, len);
389                         current = 0;
390                     } else {
391                         if (current+2 > numresults) {
392                             len = (numresults + MULTIPART_DELTA) * sizeof(s_var *);
393                             if ((tmp = (s_var **)realloc (result, len)) == NULL) {
394                                 for (index=0; result[index]; index++)
395                                     free (result[index]);
396                                 free (result);
397                                 free (name);
398                                 if (type)
399                                     free (type);
400                                 return NULL;
401                             }
402                             result = tmp;
403                             memset (result + numresults, 0, len - numresults*sizeof(s_var *));
404                             numresults += MULTIPART_DELTA;
405                         }
406                     }
407                     if ((result[current] = (s_var *)malloc(sizeof(s_var))) == NULL) {
408                         for (index=0; result[index]; index++)
409                             free (result[index]);
410                         free (result);
411                         free (name);
412                         if (type)
413                             free (type);
414                         return NULL;
415                     }
416                     current++;
417                     cgiDebugOutput (3, "Set #%d to %s=%s", index, name, line);
418                     result[index]->name = name; name = NULL;
419                     result[index]->value = strdup (line);
420                     cgiDecodeString (result[index]->value);
421                     if (type) {
422                         free (type);
423                         type = NULL;
424                     }
425                 } else {
426                     cgiDebugOutput (3, "Set #%d to %s=%s", index, name, line);
427                     free (name);
428                     name = NULL;
429                     if ((cp = (char *)realloc (result[index]->value, strlen(result[index]->value)+strlen(line)+2)) != NULL) {
430                         strcat(cp, "\n");
431                         strcat(cp, line);
432                         result[index]->value = cp;
433                         if (type) {
434                             free (type);
435                             type = NULL;
436                         }
437                     }
438                 }
439             } else {
440                 if (index > 0) {
441                     xp = strdup (line);
442                     cgiDecodeString (xp);
443
444                     if ((name = (char *)malloc (strlen(result[index]->value)+strlen(xp)+3)) == NULL) {
445                         for (index=0; result[index]; index++)
446                             free (result[index]);
447                         free (result);
448                         free (xp);
449                         return NULL;
450                     }
451                     sprintf (name, "%s\r\n%s", result[index]->value, xp);
452                     free (result[index]->value);
453                     result[index]->value = name;
454                     name = NULL;
455                     free (xp);
456                 }
457             }
458         }
459     }
460
461     if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
462         return NULL;
463
464     res->vars = result;
465     res->cookies = NULL;
466     res->files = files;
467
468     return res;
469 }
470
471 /*  cgiReadVariables()
472  *
473  *  Read from stdin if no string is provided via CGI.  Variables that
474  *  doesn't have a value associated with it doesn't get stored.
475  */
476 s_cgi *cgiReadVariables ()
477 {
478     int length;
479     char *line = NULL;
480     int numargs;
481     char *cp, *ip, *esp, *sptr;
482     s_var **result;
483     int i, k, len;
484     char tmp[101];
485     s_cgi *res;
486
487     cp = getenv("CONTENT_TYPE");
488     if (cp)
489         cgiDebugOutput (2, "Content-Type: %s", cp);
490     if (cp && strstr(cp, "multipart/form-data") && strstr(cp, "boundary=")) {
491         cp = strstr(cp, "boundary=") + strlen ("boundary=") - 2;
492         *cp = *(cp+1) = '-';
493         return cgiReadMultipart (cp);
494     }
495
496     if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
497         return NULL;
498
499     cp = getenv("REQUEST_METHOD");
500     if (cp)
501         cgiDebugOutput (2, "REQUEST_METHOD: %s", cp);
502     ip = getenv("CONTENT_LENGTH");
503     if (ip)
504         cgiDebugOutput (2, "CONTENT_LENGTH: %s", ip);
505
506     if (cp && !strcmp(cp, "POST")) {
507         if (ip) {
508             length = atoi(ip);
509             if (length <= 0)
510                 return NULL;
511             if ((line = (char *)malloc (length+2)) == NULL)
512                 return NULL;
513             fgets(line, length+1, stdin);
514         } else
515             return NULL;
516     } else if (cp && !strcmp(cp, "GET")) {
517         esp = getenv("QUERY_STRING");
518         if (esp && strlen(esp)) {
519             if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
520                 return NULL;
521             sprintf (line, "%s", esp);
522         } else
523             return NULL;
524     } else {
525         length = 0;
526         printf ("(offline mode: enter name=value pairs on standard input)\n");
527         memset (tmp, 0, sizeof(tmp));
528         while((cp = fgets (tmp, 100, stdin)) != NULL) {
529             if (strlen(tmp)) {
530                 if (tmp[strlen(tmp)-1] == '\n')
531                     tmp[strlen(tmp)-1] = '&';
532                 if (length) {
533                     length += strlen(tmp);
534                     len = (length+1) * sizeof(char);
535                     if ((line = (char *)realloc (line, len)) == NULL)
536                         return NULL;
537                     strcat (line, tmp);
538                 } else {
539                     length = strlen(tmp);
540                     len = (length+1) * sizeof(char);
541                     if ((line = (char *)malloc (len)) == NULL)
542                         return NULL;
543                     memset (line, 0, len);
544                     strcpy (line, tmp);
545                 }
546             }
547             memset (tmp, 0, sizeof(tmp));
548         }
549         if (!line)
550             return NULL;
551         if (line[strlen(line)-1] == '&')
552             line[strlen(line)-1] = '\0';
553     }
554
555     /*
556      *  From now on all cgi variables are stored in the variable line
557      *  and look like  foo=bar&foobar=barfoo&foofoo=
558      */
559
560     cgiDebugOutput (1, "Received CGI input: %s", line);
561
562     for (cp=line; *cp; cp++)
563         if (*cp == '+')
564             *cp = ' ';
565
566     if (strlen(line)) {
567         for (numargs=1,cp=line; *cp; cp++)
568             if (*cp == '&' || *cp == ';' ) numargs++;
569     } else
570         numargs = 0;
571     cgiDebugOutput (1, "%d cgi variables found.", numargs);
572
573     len = (numargs+1) * sizeof(s_var *);
574     if ((result = (s_var **)malloc (len)) == NULL)
575         return NULL;
576     memset (result, 0, len);
577
578     cp = line;
579     i=0;
580     while (*cp) {
581         if ((ip = (char *)strchr(cp, '&')) != NULL) {
582             *ip = '\0';
583         } else if ((ip = (char *)strchr(cp, ';')) != NULL) {
584             *ip = '\0';
585         } else
586             ip = cp + strlen(cp);
587
588         if ((esp=(char *)strchr(cp, '=')) == NULL) {
589             cp = ++ip;
590             continue;
591         }
592
593         if (!strlen(esp)) {
594             cp = ++ip;
595             continue;
596         }
597
598         if (i<numargs) {
599             char *name;
600             char *value;
601
602             if ((name = (char *)malloc((esp-cp+1) * sizeof (char))) == NULL)
603                 return NULL;
604             strncpy(name, cp, esp-cp);
605             name[esp-cp] = '\0';
606             cgiDecodeString (name);
607
608             cp = ++esp;
609
610             if ((value = (char *)malloc((ip-esp+1) * sizeof (char))) == NULL) {
611                 free (name);
612                 return NULL;
613             }
614             strncpy(value, cp, ip-esp);
615             value[ip-esp] = '\0';
616             cgiDecodeString (value);
617
618             /* try to find out if there's already such a variable */
619             for (k=0; k<i && strcmp (result[k]->name, name); k++);
620
621             if (k == i) {       /* No such variable yet */
622                 if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
623                     return NULL;
624                 result[i]->name = name;
625                 result[i]->value = value;
626                 cgiDebugOutput (1, "%s: %s", result[i]->name, result[i]->value);
627                 i++;
628             } else {    /* There is already such a name, suppose a mutiple field */
629                 free (name);
630                 len = (strlen(result[k]->value)+strlen(value)+2) * sizeof (char);
631                 if ((sptr = (char *)malloc(len)) == NULL) {
632                     free (value);
633                     return NULL;
634                 }
635                 memset (sptr, 0, len);
636                 sprintf (sptr, "%s\n%s", result[k]->value, value);
637                 free (result[k]->value);
638                 free (value);
639                 result[k]->value = sptr;
640                 cgiDebugOutput (1, "%s: %s", result[i]->name, result[i]->value);
641             }
642         }
643         cp = ++ip;
644     }
645
646     res->vars = result;
647     res->cookies = NULL;
648     res->files = NULL;
649
650     return res;
651 }
652
653 /*  cgiInit()
654  *
655  *  Read from stdin if no string is provided via CGI.  Variables that
656  *  doesn't have a value associated with it doesn't get stored.
657  */
658 s_cgi *cgiInit()
659 {
660     s_cgi *res;
661
662     res = cgiReadVariables ();
663     if (res)
664         res->cookies = cgiReadCookies ();
665     else
666         return NULL;
667
668     if (!res->vars && !res->cookies && !res->files) {
669         free (res);
670         return NULL;
671     }
672
673     return res;
674 }
675
676 char *cgiGetValue (s_cgi *parms, const char *name)
677 {
678     int i;
679
680     if (!parms || !parms->vars)
681         return NULL;
682     for (i=0;parms->vars[i]; i++)
683         if (!strcmp(name,parms->vars[i]->name)) {
684             cgiDebugOutput (1, "%s found as %s", name, parms->vars[i]->value);
685             if (strlen(parms->vars[i]->value) > 0)
686                 return parms->vars[i]->value;
687             else
688                 return NULL;
689         }
690     cgiDebugOutput (1, "%s not found", name);
691     return NULL;
692 }
693
694 char **cgiGetVariables (s_cgi *parms)
695 {
696     int i;
697     char **res = NULL;
698     int len;
699
700     if (!parms || !parms->vars)
701         return NULL;
702     for (i=0;parms->vars[i]; i++);
703     len = sizeof (char *) * ++i;
704     if ((res = (char **)malloc (len)) == NULL)
705         return NULL;
706     memset (res, 0, len);
707     for (i=0;parms->vars[i]; i++) {
708         len = strlen (parms->vars[i]->name) +1;
709         if ((res[i] = (char *)malloc (len)) == NULL)
710             return NULL;
711         memset (res[i], 0, len);
712         strcpy (res[i], parms->vars[i]->name);
713     }
714     return res;
715 }
716
717 /* cgiGetFiles
718  *
719  * Returns a list of names of all files.
720  */
721 char **cgiGetFiles (s_cgi *parms)
722 {
723     int i;
724     char **res = NULL;
725     int len;
726
727     if (!parms || !parms->files)
728         return NULL;
729
730     for (i=0;parms->files[i]; i++);
731     len = sizeof (char *) * ++i;
732     if ((res = (char **)malloc (len)) == NULL)
733         return NULL;
734     memset (res, 0, len);
735     for (i=0;parms->files[i]; i++) {
736         len = strlen (parms->files[i]->name) +1;
737         if ((res[i] = (char *)malloc (len)) == NULL)
738             return NULL;
739         memset (res[i], 0, len);
740         strcpy (res[i], parms->files[i]->name);
741     }
742     return res;
743 }
744
745 /* cgiGetFile
746  *
747  * Return data structure for CGI file variable
748  */
749 s_file *cgiGetFile (s_cgi *parms, const char *name)
750 {
751     int i;
752
753     if (!parms || !parms->files)
754         return NULL;
755
756     for (i=0;parms->files[i]; i++)
757         if (!strcmp(name,parms->files[i]->name)) {
758             cgiDebugOutput (1, "%s found as %s", name, parms->files[i]->filename);
759             return parms->files[i];
760         }
761     cgiDebugOutput (1, "%s not found", name);
762     return NULL;
763 }
764
765 void cgiRedirect (const char *url)
766 {
767     if (url && strlen(url)) {
768         printf ("Content-type: text/html\r\nContent-length: %d\r\n", 77+(strlen(url)*2));
769         printf ("Status: 302 Temporal Relocation\r\n");
770         printf ("Location: %s\r\n\r\n", url);
771         printf ("<html>\n<body>\nThe page has been moved to <a href=\"%s\">%s</a>\n</body>\n</html>\n", url, url);
772     }
773 }
774
775 void cgiFreeList (char **list)
776 {
777     int i;
778
779     for (i=0; list[i] != NULL; i++)
780         free (list[i]);
781     free (list);
782 }
783
784 void cgiFree (s_cgi *parms)
785 {
786     int i;
787
788     if (!parms)
789         return;
790     if (parms->vars) {
791         for (i=0;parms->vars[i]; i++) {
792             if (parms->vars[i]->name)
793                 free (parms->vars[i]->name);
794             if (parms->vars[i]->value)
795                 free (parms->vars[i]->value);
796             free (parms->vars[i]);
797         }
798         free (parms->vars);
799     }
800     if (parms->cookies) {
801         if (parms->cookies[0]->version)
802             free (parms->cookies[0]->version);
803         for (i=0;parms->cookies[i]; i++) {
804             if (parms->cookies[i]->name)
805                 free (parms->cookies[i]->name);
806             if (parms->cookies[i]->value)
807                 free (parms->cookies[i]->value);
808             if (parms->cookies[i]->path)
809                 free (parms->cookies[i]->path);
810             if (parms->cookies[i]->domain)
811                 free (parms->cookies[i]->domain);
812             free (parms->cookies[i]);
813         }
814         free (parms->cookies);
815     }
816     if (parms->files) {
817         for (i=0;parms->files[i]; i++) {
818             if (parms->files[i]->name)
819                 free (parms->files[i]->name);
820             if (parms->files[i]->type)
821                 free (parms->files[i]->type);
822             if (parms->files[i]->filename)
823                 free (parms->files[i]->filename);
824             if (parms->files[i]->tmpfile) {
825                 unlink (parms->files[i]->tmpfile);
826                 free (parms->files[i]->tmpfile);
827             }
828             free (parms->files[i]);
829         }
830         free (parms->files);
831     }
832     free (parms);
833
834     if (cgiHeaderString) {
835         free (cgiHeaderString);
836         cgiHeaderString = NULL;
837     }
838     if (cgiType) {
839         free (cgiType);
840         cgiType = NULL;
841     }
842 }
843
844 /*
845  * Local variables:
846  *  c-indent-level: 4
847  *  c-basic-offset: 4
848  *  tab-width: 8
849  * End:
850  */