2 cgi.c - Some simple routines for CGI programming
3 Copyright (c) 1996-9,2007 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
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
32 char *cgiHeaderString = NULL;
35 extern s_cookie **cgiReadCookies();
37 int cgiSetHeader (char *name, char *value)
39 char *cp, *vp, *pivot;
42 if (!name || !strlen (name) || !value || !strlen (value))
45 for (cp=name;*cp && *cp!=' ' && *cp!='\r' && *cp!='\n' && *cp!=':';cp++);
46 for (vp=value;*vp && *vp!='\r' && *vp!='\n';vp++);
48 if (cgiHeaderString) {
49 len = (strlen (cgiHeaderString) + cp-name + vp-value + 5) * sizeof (char);
50 if ((pivot = (char *)realloc (cgiHeaderString,len)) == NULL)
52 cgiHeaderString = pivot;
53 pivot += strlen (cgiHeaderString);
55 len = (cp-name + vp-value + 5) * sizeof (char);
56 if ((cgiHeaderString = (char *)malloc (len)) == NULL)
58 pivot = cgiHeaderString;
60 strncpy (pivot, name, cp-name);
61 strncat (pivot, ": ", 2);
62 strncat (pivot, value, vp-value);
63 strncat (pivot, "\r\n", 2);
68 int cgiSetType (char *type)
73 if (!type || !strlen (type))
78 for (cp=type;*cp && *cp!='\r' && *cp!='\n';cp++);
80 len = (cp-type+1) * sizeof (char);
81 if ((cgiType = (char *)malloc (len+20)) == NULL)
83 memset (cgiType, 0, len);
84 strncpy (cgiType, type, cp-type);
92 printf ("Content-type: %s\r\n", cgiType);
94 printf ("Content-type: text/html\r\n");
96 printf ("%s", cgiHeaderString);
100 void cgiDebug (int level, int where)
103 cgiDebugLevel = level;
108 cgiDebugType = where;
110 openlog("cgilib", LOG_PID, LOG_USER);
116 char *cgiDecodeString (char *text)
120 for (cp=text,xp=text; *cp; cp++) {
122 if (strchr("0123456789ABCDEFabcdef", *(cp+1))
123 && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
124 if (islower(*(cp+1)))
125 *(cp+1) = toupper(*(cp+1));
126 if (islower(*(cp+2)))
127 *(cp+2) = toupper(*(cp+2));
128 *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
129 + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
136 memset(xp, 0, cp-xp);
142 * Read and save a file fro a multipart request
145 char *cgiReadFile (FILE *stream, char *boundary)
147 char *crlfboundary, *buf;
152 char template[]= "/tmp/cgilibXXXXXX";
156 boundarylen = strlen(boundary)+3;
157 if ((crlfboundary = (char *)malloc (boundarylen)) == NULL)
159 sprintf (crlfboundary, "\r\n%s", boundary);
161 if ((buf = (char *)malloc (boundarylen)) == NULL) {
165 memset (buf, 0, boundarylen);
168 if ((fd = mkstemp (template)) == -1) {
174 if ((tmpfile = fdopen (fd, "w")) == NULL) {
181 while (!feof (stream)) {
186 for (cp=buf; *cp; cp++)
188 memset (buf, 0, boundarylen);
196 if (crlfboundary[pivot+1] == c) {
199 if (strlen (buf) == strlen (crlfboundary))
204 for (cp=buf; *cp; cp++)
206 memset (buf, 0, boundarylen);
211 if (crlfboundary[0] == c) {
219 fgets (buf, boundarylen, stream);
226 return strdup (template);
229 /* cgiReadMultipart()
231 * Decode multipart/form-data
233 #define MULTIPART_DELTA 5
234 s_cgi *cgiReadMultipart (char *boundary)
238 char *name = NULL, *type = NULL;
242 s_var **result = NULL;
244 int numresults = 0, current = 0;
245 s_file **files = NULL;
252 while ((line = cgiGetLine (stdin)) != NULL) {
254 if (!strncmp (line, boundary, strlen(boundary))) {
256 } else if (!strncasecmp (line, "Content-Disposition: form-data; ", 32)) {
258 if ((cp = strstr (line, "name=\"")) == NULL)
261 if ((xp = strchr (cp, '\"')) == NULL)
263 name = strndup (cp, xp-cp);
264 cgiDebugOutput (2, "Found field name %s", name);
266 if ((cp = strstr (line, "filename=\"")) == NULL)
269 if ((xp = strchr (cp, '\"')) == NULL)
271 fname = strndup (cp, xp-cp);
272 cgiDebugOutput (2, "Found filename %s", fname);
274 } else if (!strncasecmp (line, "Content-Type: ", 14)) {
278 cgiDebugOutput (2, "Found mime type %s", type);
286 tmpfile = cgiReadFile (stdin, boundary);
293 name = fname = type = NULL;
296 cgiDebugOutput (2, "Wrote %s (%s) to file: %s", name, fname, tmpfile);
298 if (!strlen (fname)) {
299 cgiDebugOutput (3, "Found empty filename, removing");
306 name = fname = type = NULL;
308 if ((file = (s_file *)malloc (sizeof (s_file))) == NULL) {
309 cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
316 name = fname = type = NULL;
322 file->tmpfile = tmpfile;
323 if ((cp = rindex (fname, '/')) == NULL)
324 file->filename = fname;
326 file->filename = strdup (++cp);
329 name = type = fname = NULL;
332 if ((files = (s_file **)malloc(2*sizeof (s_file *))) == NULL) {
333 cgiDebugOutput (3, "malloc failed, ignoring %s=%s", name, fname);
342 free (file->filename);
346 memset (files, 0, 2*sizeof (s_file *));
349 for (index=0; files[index]; index++);
350 if ((tmpf = (s_file **)realloc(files, (index+2)*sizeof (s_file *))) == NULL) {
351 cgiDebugOutput (3, "realloc failed, ignoring %s=%s", name, fname);
357 free (file->filename);
359 name = type = fname = NULL;
363 memset (files + index, 0, 2*sizeof (s_file *));
372 /* try to find out if there's already such a variable */
373 for (index=0; index<current && strcmp (result[index]->name,name); index++);
375 if (index == current) {
377 len = MULTIPART_DELTA * sizeof (s_var *);
378 if ((result = (s_var **)malloc (len)) == NULL) {
384 numresults = MULTIPART_DELTA;
385 memset (result, 0, len);
388 if (current+2 > numresults) {
389 len = (numresults + MULTIPART_DELTA) * sizeof(s_var *);
390 if ((tmp = (s_var **)realloc (result, len)) == NULL) {
391 for (index=0; result[index]; index++)
392 free (result[index]);
400 memset (result + numresults, 0, len - numresults*sizeof(s_var *));
401 numresults += MULTIPART_DELTA;
404 if ((result[current] = (s_var *)malloc(sizeof(s_var))) == NULL) {
405 for (index=0; result[index]; index++)
406 free (result[index]);
414 cgiDebugOutput (3, "Set #%d to %s=%s", index, name, line);
415 result[index]->name = name; name = NULL;
416 result[index]->value = strdup (line);
422 cgiDebugOutput (3, "Set #%d to %s=%s", index, name, line);
425 if ((cp = (char *)realloc (result[index]->value, strlen(result[index]->value)+strlen(line)+2)) != NULL) {
428 result[index]->value = cp;
437 if ((name = (char *)malloc (strlen(result[index]->value)+strlen(line)+3)) == NULL) {
438 for (index=0; result[index]; index++)
439 free (result[index]);
443 sprintf (name, "%s\r\n%s", result[index]->value, line);
444 free (result[index]->value);
445 result[index]->value = name;
452 if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
462 /* cgiReadVariables()
464 * Read from stdin if no string is provided via CGI. Variables that
465 * doesn't have a value associated with it doesn't get stored.
467 s_cgi *cgiReadVariables ()
472 char *cp, *ip, *esp, *sptr;
478 cp = getenv("CONTENT_TYPE");
480 cgiDebugOutput (2, "Content-Type: %s", cp);
481 if (cp && strstr(cp, "multipart/form-data") && strstr(cp, "boundary=")) {
482 cp = strstr(cp, "boundary=") + strlen ("boundary=") - 2;
484 return cgiReadMultipart (cp);
487 if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
490 cp = getenv("REQUEST_METHOD");
492 cgiDebugOutput (2, "REQUEST_METHOD: %s", cp);
493 ip = getenv("CONTENT_LENGTH");
495 cgiDebugOutput (2, "CONTENT_LENGTH: %s", ip);
497 if (cp && !strcmp(cp, "POST")) {
502 if ((line = (char *)malloc (length+2)) == NULL)
504 fgets(line, length+1, stdin);
507 } else if (cp && !strcmp(cp, "GET")) {
508 esp = getenv("QUERY_STRING");
509 if (esp && strlen(esp)) {
510 if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
512 sprintf (line, "%s", esp);
517 printf ("(offline mode: enter name=value pairs on standard input)\n");
518 memset (tmp, 0, sizeof(tmp));
519 while((cp = fgets (tmp, 100, stdin)) != NULL) {
521 if (tmp[strlen(tmp)-1] == '\n')
522 tmp[strlen(tmp)-1] = '&';
524 length += strlen(tmp);
525 len = (length+1) * sizeof(char);
526 if ((line = (char *)realloc (line, len)) == NULL)
530 length = strlen(tmp);
531 len = (length+1) * sizeof(char);
532 if ((line = (char *)malloc (len)) == NULL)
534 memset (line, 0, len);
538 memset (tmp, 0, sizeof(tmp));
542 if (line[strlen(line)-1] == '&')
543 line[strlen(line)-1] = '\0';
547 * From now on all cgi variables are stored in the variable line
548 * and look like foo=bar&foobar=barfoo&foofoo=
551 cgiDebugOutput (1, "Received CGI input: %s", line);
553 for (cp=line; *cp; cp++)
558 for (numargs=1,cp=line; *cp; cp++)
559 if (*cp == '&' || *cp == ';' ) numargs++;
562 cgiDebugOutput (1, "%d cgi variables found.", numargs);
564 len = (numargs+1) * sizeof(s_var *);
565 if ((result = (s_var **)malloc (len)) == NULL)
567 memset (result, 0, len);
572 if ((ip = (char *)strchr(cp, '&')) != NULL) {
574 } else if ((ip = (char *)strchr(cp, ';')) != NULL) {
577 ip = cp + strlen(cp);
579 if ((esp=(char *)strchr(cp, '=')) == NULL) {
591 /* try to find out if there's already such a variable */
592 for (k=0; k<i && (strncmp (result[k]->name,cp, esp-cp) || !(strlen (result[k]->name) == esp-cp)); k++);
594 if (k == i) { /* No such variable yet */
595 if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
597 if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
599 memset (result[i]->name, 0, esp-cp+1);
600 strncpy(result[i]->name, cp, esp-cp);
602 if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
604 memset (result[i]->value, 0, ip-esp+1);
605 strncpy(result[i]->value, cp, ip-esp);
606 result[i]->value = cgiDecodeString(result[i]->value);
607 cgiDebugOutput (1, "%s: %s", result[i]->name, result[i]->value);
609 } else { /* There is already such a name, suppose a mutiple field */
611 len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
612 if ((sptr = (char *)malloc(len)) == NULL)
614 memset (sptr, 0, len);
615 sprintf (sptr, "%s\n", result[k]->value);
616 strncat(sptr, cp, ip-esp);
617 free(result[k]->value);
618 result[k]->value = cgiDecodeString (sptr);
633 * Read from stdin if no string is provided via CGI. Variables that
634 * doesn't have a value associated with it doesn't get stored.
640 res = cgiReadVariables ();
642 res->cookies = cgiReadCookies ();
646 if (!res->vars && !res->cookies && !res->files) {
654 char *cgiGetValue (s_cgi *parms, const char *name)
658 if (!parms || !parms->vars)
660 for (i=0;parms->vars[i]; i++)
661 if (!strcmp(name,parms->vars[i]->name)) {
662 cgiDebugOutput (1, "%s found as %s", name, parms->vars[i]->value);
663 if (strlen(parms->vars[i]->value) > 0)
664 return parms->vars[i]->value;
668 cgiDebugOutput (1, "%s not found", name);
672 char **cgiGetVariables (s_cgi *parms)
678 if (!parms || !parms->vars)
680 for (i=0;parms->vars[i]; i++);
681 len = sizeof (char *) * ++i;
682 if ((res = (char **)malloc (len)) == NULL)
684 memset (res, 0, len);
685 for (i=0;parms->vars[i]; i++) {
686 len = strlen (parms->vars[i]->name) +1;
687 if ((res[i] = (char *)malloc (len)) == NULL)
689 memset (res[i], 0, len);
690 strcpy (res[i], parms->vars[i]->name);
697 * Returns a list of names of all files.
699 char **cgiGetFiles (s_cgi *parms)
705 if (!parms || !parms->files)
708 for (i=0;parms->files[i]; i++);
709 len = sizeof (char *) * ++i;
710 if ((res = (char **)malloc (len)) == NULL)
712 memset (res, 0, len);
713 for (i=0;parms->files[i]; i++) {
714 len = strlen (parms->files[i]->name) +1;
715 if ((res[i] = (char *)malloc (len)) == NULL)
717 memset (res[i], 0, len);
718 strcpy (res[i], parms->files[i]->name);
725 * Return data structure for CGI file variable
727 s_file *cgiGetFile (s_cgi *parms, const char *name)
731 if (!parms || !parms->files)
734 for (i=0;parms->files[i]; i++)
735 if (!strcmp(name,parms->files[i]->name)) {
736 cgiDebugOutput (1, "%s found as %s", name, parms->files[i]->filename);
737 return parms->files[i];
739 cgiDebugOutput (1, "%s not found", name);
743 void cgiRedirect (const char *url)
745 if (url && strlen(url)) {
746 printf ("Content-type: text/html\r\nContent-length: %d\r\n", 77+(strlen(url)*2));
747 printf ("Status: 302 Temporal Relocation\r\n");
748 printf ("Location: %s\r\n\r\n", url);
749 printf ("<html>\n<body>\nThe page has been moved to <a href=\"%s\">%s</a>\n</body>\n</html>\n", url, url);
753 void cgiFreeList (char **list)
757 for (i=0; list[i] != NULL; i++)
762 void cgiFree (s_cgi *parms)
769 for (i=0;parms->vars[i]; i++) {
770 if (parms->vars[i]->name)
771 free (parms->vars[i]->name);
772 if (parms->vars[i]->value)
773 free (parms->vars[i]->value);
774 free (parms->vars[i]);
778 if (parms->cookies) {
779 if (parms->cookies[0]->version)
780 free (parms->cookies[0]->version);
781 for (i=0;parms->cookies[i]; i++) {
782 if (parms->cookies[i]->name)
783 free (parms->cookies[i]->name);
784 if (parms->cookies[i]->value)
785 free (parms->cookies[i]->value);
786 if (parms->cookies[i]->path)
787 free (parms->cookies[i]->path);
788 if (parms->cookies[i]->domain)
789 free (parms->cookies[i]->domain);
790 free (parms->cookies[i]);
792 free (parms->cookies);
795 for (i=0;parms->files[i]; i++) {
796 if (parms->files[i]->name)
797 free (parms->files[i]->name);
798 if (parms->files[i]->type)
799 free (parms->files[i]->type);
800 if (parms->files[i]->filename)
801 free (parms->files[i]->filename);
802 if (parms->files[i]->tmpfile) {
803 unlink (parms->files[i]->tmpfile);
804 free (parms->files[i]->tmpfile);
806 free (parms->files[i]);
812 if (cgiHeaderString) {
813 free (cgiHeaderString);
814 cgiHeaderString = NULL;