2 cgi.c - Some simple routines for CGI programming
3 Copyright (c) 1996-9,2007,8 Martin Schulze <joey@infodrom.org>
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.
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.
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.
33 char *strndup(const char *s, size_t n);
36 char *cgiHeaderString = NULL;
39 extern s_cookie **cgiReadCookies();
41 int cgiSetHeader (const char *name, const char *value)
47 if (!name || !strlen (name) || !value || !strlen (value))
50 for (cp=name;*cp && *cp!=' ' && *cp!='\r' && *cp!='\n' && *cp!=':';cp++);
51 for (vp=value;*vp && *vp!='\r' && *vp!='\n';vp++);
53 if (cp-name == 0 || vp-value == 0)
56 if (cgiHeaderString) {
57 len = (strlen (cgiHeaderString) + cp-name + vp-value + 5) * sizeof (char);
58 if ((pivot = (char *)realloc (cgiHeaderString,len)) == NULL)
60 cgiHeaderString = pivot;
61 pivot += strlen (cgiHeaderString);
63 len = (cp-name + vp-value + 5) * sizeof (char);
64 if ((cgiHeaderString = (char *)malloc (len)) == NULL)
66 pivot = cgiHeaderString;
69 memset(pivot, 0, (cp-name + vp-value + 5) * sizeof (char));
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);
80 int cgiSetType (const char *type)
85 if (!type || !strlen (type))
90 for (cp=type;*cp && *cp!='\r' && *cp!='\n';cp++);
92 len = (cp-type+1) * sizeof (char);
93 if ((cgiType = (char *)malloc (len+20)) == NULL)
95 memset (cgiType, 0, len);
96 strncpy (cgiType, type, cp-type);
105 printf ("Content-type: %s\r\n", cgiType);
110 printf ("Content-type: text/html\r\n");
113 printf ("%s", cgiHeaderString);
114 free(cgiHeaderString);
115 cgiHeaderString = NULL;
120 void cgiDebug (int level, int where)
123 cgiDebugLevel = level;
128 cgiDebugType = where;
130 openlog("cgilib", LOG_PID, LOG_USER);
136 char *cgiDecodeString (char *text)
140 for (cp=text,xp=text; *cp; 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');
156 memset(xp, 0, cp-xp);
162 * Read and save a file fro a multipart request
165 char *cgiReadFile (FILE *stream, char *boundary)
167 char *crlfboundary, *buf;
172 char template[]= "/tmp/cgilibXXXXXX";
176 boundarylen = strlen(boundary)+3;
177 if ((crlfboundary = (char *)malloc (boundarylen)) == NULL)
179 sprintf (crlfboundary, "\r\n%s", boundary);
181 if ((buf = (char *)malloc (boundarylen)) == NULL) {
185 memset (buf, 0, boundarylen);
188 if ((fd = mkstemp (template)) == -1) {
194 if ((tmpfile = fdopen (fd, "w")) == NULL) {
201 while (!feof (stream)) {
206 for (cp=buf; *cp; cp++)
208 memset (buf, 0, boundarylen);
216 if (crlfboundary[pivot+1] == c) {
219 if (strlen (buf) == strlen (crlfboundary))
224 for (cp=buf; *cp; cp++)
226 memset (buf, 0, boundarylen);
231 if (crlfboundary[0] == c) {
239 fgets (buf, boundarylen, stream);
246 return strdup (template);
249 /* cgiReadMultipart()
251 * Decode multipart/form-data
253 #define MULTIPART_DELTA 5
254 s_cgi *cgiReadMultipart (char *boundary)
258 char *name = NULL, *type = NULL;
262 s_var **result = NULL;
264 int numresults = 0, current = 0;
265 s_file **files = NULL;
272 while ((line = cgiGetLine (stdin)) != NULL) {
274 if (!strncmp (line, boundary, strlen(boundary))) {
276 } else if (!strncasecmp (line, "Content-Disposition: form-data; ", 32)) {
278 if ((cp = strstr (line, "name=\"")) == NULL)
281 if ((xp = strchr (cp, '\"')) == NULL)
283 name = strndup (cp, xp-cp);
284 cgiDecodeString (name);
285 cgiDebugOutput (2, "Found field name %s", name);
287 if ((cp = strstr (line, "filename=\"")) == NULL)
290 if ((xp = strchr (cp, '\"')) == NULL)
292 fname = strndup (cp, xp-cp);
293 cgiDecodeString (fname);
294 cgiDebugOutput (2, "Found filename %s", fname);
296 } else if (!strncasecmp (line, "Content-Type: ", 14)) {
300 cgiDebugOutput (2, "Found mime type %s", type);
308 tmpfile = cgiReadFile (stdin, boundary);
315 name = fname = type = NULL;
318 cgiDebugOutput (2, "Wrote %s (%s) to file: %s", name, fname, tmpfile);
320 if (!strlen (fname)) {
321 cgiDebugOutput (3, "Found empty filename, removing");
328 name = fname = type = NULL;
330 if ((file = (s_file *)malloc (sizeof (s_file))) == NULL) {
331 cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
338 name = fname = type = NULL;
344 file->tmpfile = tmpfile;
345 if ((cp = rindex (fname, '/')) == NULL)
346 file->filename = fname;
348 file->filename = strdup (++cp);
351 name = type = fname = NULL;
354 if ((files = (s_file **)malloc(2*sizeof (s_file *))) == NULL) {
355 cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
364 free (file->filename);
368 memset (files, 0, 2*sizeof (s_file *));
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);
379 free (file->filename);
381 name = type = fname = NULL;
385 memset (files + index, 0, 2*sizeof (s_file *));
394 /* try to find out if there's already such a variable */
395 for (index=0; index<current && strcmp (result[index]->name,name); index++);
397 if (index == current) {
399 len = MULTIPART_DELTA * sizeof (s_var *);
400 if ((result = (s_var **)malloc (len)) == NULL) {
406 numresults = MULTIPART_DELTA;
407 memset (result, 0, len);
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]);
422 memset (result + numresults, 0, len - numresults*sizeof(s_var *));
423 numresults += MULTIPART_DELTA;
426 if ((result[current] = (s_var *)malloc(sizeof(s_var))) == NULL) {
427 for (index=0; result[index]; index++)
428 free (result[index]);
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);
445 cgiDebugOutput (3, "Set #%d to %s=%s", index, name, line);
448 if ((cp = (char *)realloc (result[index]->value, strlen(result[index]->value)+strlen(line)+2)) != NULL) {
451 result[index]->value = cp;
461 cgiDecodeString (xp);
463 if ((name = (char *)malloc (strlen(result[index]->value)+strlen(xp)+3)) == NULL) {
464 for (index=0; result[index]; index++)
465 free (result[index]);
470 sprintf (name, "%s\r\n%s", result[index]->value, xp);
471 free (result[index]->value);
472 result[index]->value = name;
480 if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
490 /* cgiReadVariables()
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.
495 s_cgi *cgiReadVariables ()
500 char *cp, *ip, *esp, *sptr;
506 cp = getenv("CONTENT_TYPE");
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;
512 return cgiReadMultipart (cp);
515 if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
518 cp = getenv("REQUEST_METHOD");
520 cgiDebugOutput (2, "REQUEST_METHOD: %s", cp);
521 ip = getenv("CONTENT_LENGTH");
523 cgiDebugOutput (2, "CONTENT_LENGTH: %s", ip);
525 if (cp && !strcmp(cp, "POST")) {
530 if ((line = (char *)malloc (length+2)) == NULL)
532 fgets(line, length+1, stdin);
535 } else if (cp && !strcmp(cp, "GET")) {
536 esp = getenv("QUERY_STRING");
538 if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
540 sprintf (line, "%s", esp);
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) {
549 if (tmp[strlen(tmp)-1] == '\n')
550 tmp[strlen(tmp)-1] = '&';
552 length += strlen(tmp);
553 len = (length+1) * sizeof(char);
554 if ((line = (char *)realloc (line, len)) == NULL)
558 length = strlen(tmp);
559 len = (length+1) * sizeof(char);
560 if ((line = (char *)malloc (len)) == NULL)
562 memset (line, 0, len);
566 memset (tmp, 0, sizeof(tmp));
570 if (line[strlen(line)-1] == '&')
571 line[strlen(line)-1] = '\0';
575 * From now on all cgi variables are stored in the variable line
576 * and look like foo=bar&foobar=barfoo&foofoo=
579 cgiDebugOutput (1, "Received CGI input: %s", line);
581 for (cp=line; *cp; cp++)
586 for (numargs=1,cp=line; *cp; cp++)
587 if (*cp == '&' || *cp == ';' ) numargs++;
590 cgiDebugOutput (1, "%d cgi variables found.", numargs);
592 len = (numargs+1) * sizeof(s_var *);
593 if ((result = (s_var **)malloc (len)) == NULL)
595 memset (result, 0, len);
600 if ((ip = (char *)strchr(cp, '&')) != NULL) {
602 } else if ((ip = (char *)strchr(cp, ';')) != NULL) {
605 ip = cp + strlen(cp);
607 if ((esp=(char *)strchr(cp, '=')) == NULL) {
621 if ((name = (char *)malloc((esp-cp+1) * sizeof (char))) == NULL)
623 strncpy(name, cp, esp-cp);
625 cgiDecodeString (name);
629 if ((value = (char *)malloc((ip-esp+1) * sizeof (char))) == NULL) {
633 strncpy(value, cp, ip-esp);
634 value[ip-esp] = '\0';
635 cgiDecodeString (value);
637 /* try to find out if there's already such a variable */
638 for (k=0; k<i && strcmp (result[k]->name, name); k++);
640 if (k == i) { /* No such variable yet */
641 if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
643 result[i]->name = name;
644 result[i]->value = value;
645 cgiDebugOutput (1, "%s: %s", result[i]->name, result[i]->value);
647 } else { /* There is already such a name, suppose a mutiple field */
649 len = (strlen(result[k]->value)+strlen(value)+2) * sizeof (char);
650 if ((sptr = (char *)malloc(len)) == NULL) {
654 memset (sptr, 0, len);
655 sprintf (sptr, "%s\n%s", result[k]->value, value);
656 free (result[k]->value);
658 result[k]->value = sptr;
659 cgiDebugOutput (1, "%s: %s", result[i]->name, result[i]->value);
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.
682 res = cgiReadVariables ();
684 res->cookies = cgiReadCookies ();
687 /* In some cases, we might have no other CGI results.
688 But we still have cookies! */
690 cookies = cgiReadCookies();
692 /* We need to create a s_cgi structure. */
693 if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
696 res->cookies = cookies;
704 if (!res->vars && !res->cookies && !res->files) {
712 char *cgiGetValue (s_cgi *parms, const char *name)
716 if (!parms || !parms->vars)
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;
726 cgiDebugOutput (1, "%s not found", name);
730 char **cgiGetVariables (s_cgi *parms)
736 if (!parms || !parms->vars)
738 for (i=0;parms->vars[i]; i++);
739 len = sizeof (char *) * ++i;
740 if ((res = (char **)malloc (len)) == 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)
747 memset (res[i], 0, len);
748 strcpy (res[i], parms->vars[i]->name);
755 * Returns a list of names of all files.
757 char **cgiGetFiles (s_cgi *parms)
763 if (!parms || !parms->files)
766 for (i=0;parms->files[i]; i++);
767 len = sizeof (char *) * ++i;
768 if ((res = (char **)malloc (len)) == 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)
775 memset (res[i], 0, len);
776 strcpy (res[i], parms->files[i]->name);
783 * Return data structure for CGI file variable
785 s_file *cgiGetFile (s_cgi *parms, const char *name)
789 if (!parms || !parms->files)
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];
797 cgiDebugOutput (1, "%s not found", name);
801 void cgiRedirect (const char *url)
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);
811 void cgiFreeList (char **list)
815 for (i=0; list[i] != NULL; i++)
820 void cgiFree (s_cgi *parms)
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]);
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]);
850 free (parms->cookies);
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);
864 free (parms->files[i]);
870 if (cgiHeaderString) {
871 free (cgiHeaderString);
872 cgiHeaderString = NULL;
881 char *strndup(const char *s, size_t n) {
884 if ((fnval = (char *)malloc (n + 1)) == NULL)
886 strncpy(fnval, s, n);