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