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