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