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