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