Terminate a freshly allocated string as well.
[infodrom/cgilib] / cgi.c
diff --git a/cgi.c b/cgi.c
index 6c5b40a..e390b37 100644 (file)
--- a/cgi.c
+++ b/cgi.c
@@ -1,6 +1,6 @@
 /*
     cgi.c - Some simple routines for CGI programming
-    Copyright (c) 1996-9,2007  Martin Schulze <joey@infodrom.org>
+    Copyright (c) 1996-9,2007,8  Martin Schulze <joey@infodrom.org>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -13,8 +13,8 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+    along with this program; if not, write to the Free Software Foundation
+    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
 #define _GNU_SOURCE 1
 #include <cgi.h>
 #include "aux.h"
 
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n);
+#endif
+
 char *cgiHeaderString = NULL;
 char *cgiType = NULL;
 
 extern s_cookie **cgiReadCookies();
 
-int cgiSetHeader (char *name, char *value)
+int cgiSetHeader (const char *name, const char *value)
 {
-    char *cp, *vp, *pivot;
+    const char *cp, *vp;
+    char *pivot;
     int len;
 
     if (!name || !strlen (name) || !value || !strlen (value))
@@ -45,8 +50,11 @@ int cgiSetHeader (char *name, char *value)
     for (cp=name;*cp && *cp!=' ' && *cp!='\r' && *cp!='\n' && *cp!=':';cp++);
     for (vp=value;*vp && *vp!='\r' && *vp!='\n';vp++);
 
+    if (cp-name == 0 || vp-value == 0)
+       return 0;
+
     if (cgiHeaderString) {
-       len = (strlen (cgiHeaderString) + cp-name + vp-value + 5) * sizeof (char);
+       len = (strlen (cgiHeaderString) + cp-name + vp-value + 4) * sizeof (char);
        if ((pivot = (char *)realloc (cgiHeaderString,len)) == NULL)
            return 0;
        cgiHeaderString = pivot;
@@ -56,7 +64,10 @@ int cgiSetHeader (char *name, char *value)
        if ((cgiHeaderString = (char *)malloc (len)) == NULL)
            return 0;
        pivot = cgiHeaderString;
+       *pivot = '\0';
     }
+    memset(pivot+1, 0, (cp-name + vp-value + 4));
+
     strncpy (pivot, name, cp-name);
     strncat (pivot, ": ", 2);
     strncat (pivot, value, vp-value);
@@ -65,10 +76,10 @@ int cgiSetHeader (char *name, char *value)
     return 1;
 }
 
-int cgiSetType (char *type)
+int cgiSetType (const char *type)
 {
     int len;
-    char *cp;
+    const char *cp;
 
     if (!type || !strlen (type))
        return 0;
@@ -261,6 +272,7 @@ s_cgi *cgiReadMultipart (char *boundary)
                if ((xp = strchr (cp, '\"')) == NULL)
                    continue;
                name = strndup (cp, xp-cp);
+               cgiDecodeString (name);
                cgiDebugOutput (2, "Found field name %s", name);
 
                if ((cp = strstr (line, "filename=\"")) == NULL)
@@ -269,8 +281,8 @@ s_cgi *cgiReadMultipart (char *boundary)
                if ((xp = strchr (cp, '\"')) == NULL)
                    continue;
                fname = strndup (cp, xp-cp);
+               cgiDecodeString (fname);
                cgiDebugOutput (2, "Found filename %s", fname);
-               /* TODO: filename */
            }
        } else if (!strncasecmp (line, "Content-Type: ", 14)) {
            if (!type) {
@@ -415,6 +427,7 @@ s_cgi *cgiReadMultipart (char *boundary)
                    cgiDebugOutput (3, "Set #%d to %s=%s", index, name, line);
                    result[index]->name = name; name = NULL;
                    result[index]->value = strdup (line);
+                   cgiDecodeString (result[index]->value);
                    if (type) {
                        free (type);
                        type = NULL;
@@ -435,16 +448,21 @@ s_cgi *cgiReadMultipart (char *boundary)
                }
            } else {
                if (index > 0) {
-                   if ((name = (char *)malloc (strlen(result[index]->value)+strlen(line)+3)) == NULL) {
+                   xp = strdup (line);
+                   cgiDecodeString (xp);
+
+                   if ((name = (char *)malloc (strlen(result[index]->value)+strlen(xp)+3)) == NULL) {
                        for (index=0; result[index]; index++)
                            free (result[index]);
                        free (result);
+                       free (xp);
                        return NULL;
                    }
-                   sprintf (name, "%s\r\n%s", result[index]->value, line);
+                   sprintf (name, "%s\r\n%s", result[index]->value, xp);
                    free (result[index]->value);
                    result[index]->value = name;
                    name = NULL;
+                   free (xp);
                }
            }
        }
@@ -477,16 +495,23 @@ s_cgi *cgiReadVariables ()
     s_cgi *res;
 
     cp = getenv("CONTENT_TYPE");
-    cgiDebugOutput (2, "Content-Type: %s", cp);
+    if (cp)
+       cgiDebugOutput (2, "Content-Type: %s", cp);
     if (cp && strstr(cp, "multipart/form-data") && strstr(cp, "boundary=")) {
        cp = strstr(cp, "boundary=") + strlen ("boundary=") - 2;
        *cp = *(cp+1) = '-';
        return cgiReadMultipart (cp);
     }
 
+    if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
+       return NULL;
+
     cp = getenv("REQUEST_METHOD");
-    cgiDebugOutput (2, "REQUEST_METHOD: %s", cp);
+    if (cp)
+       cgiDebugOutput (2, "REQUEST_METHOD: %s", cp);
     ip = getenv("CONTENT_LENGTH");
+    if (ip)
+       cgiDebugOutput (2, "CONTENT_LENGTH: %s", ip);
 
     if (cp && !strcmp(cp, "POST")) {
        if (ip) {
@@ -581,43 +606,53 @@ s_cgi *cgiReadVariables ()
        }
 
        if (i<numargs) {
+           char *name;
+           char *value;
+
+           if ((name = (char *)malloc((esp-cp+1) * sizeof (char))) == NULL)
+               return NULL;
+           strncpy(name, cp, esp-cp);
+           name[esp-cp] = '\0';
+           cgiDecodeString (name);
+
+           cp = ++esp;
+
+           if ((value = (char *)malloc((ip-esp+1) * sizeof (char))) == NULL) {
+               free (name);
+               return NULL;
+           }
+           strncpy(value, cp, ip-esp);
+           value[ip-esp] = '\0';
+           cgiDecodeString (value);
 
            /* try to find out if there's already such a variable */
-           for (k=0; k<i && (strncmp (result[k]->name,cp, esp-cp) || !(strlen (result[k]->name) == esp-cp)); k++);
+           for (k=0; k<i && strcmp (result[k]->name, name); k++);
 
            if (k == i) {       /* No such variable yet */
                if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
                    return NULL;
-               if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
-                   return NULL;
-               memset (result[i]->name, 0, esp-cp+1);
-               strncpy(result[i]->name, cp, esp-cp);
-               cp = ++esp;
-               if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
-                   return NULL;
-               memset (result[i]->value, 0, ip-esp+1);
-               strncpy(result[i]->value, cp, ip-esp);
-               result[i]->value = cgiDecodeString(result[i]->value);
+               result[i]->name = name;
+               result[i]->value = value;
                cgiDebugOutput (1, "%s: %s", result[i]->name, result[i]->value);
                i++;
            } else {    /* There is already such a name, suppose a mutiple field */
-               cp = ++esp;
-               len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
-               if ((sptr = (char *)malloc(len)) == NULL)
+               free (name);
+               len = (strlen(result[k]->value)+strlen(value)+2) * sizeof (char);
+               if ((sptr = (char *)malloc(len)) == NULL) {
+                   free (value);
                    return NULL;
+               }
                memset (sptr, 0, len);
-               sprintf (sptr, "%s\n", result[k]->value);
-               strncat(sptr, cp, ip-esp);
-               free(result[k]->value);
-               result[k]->value = cgiDecodeString (sptr);
+               sprintf (sptr, "%s\n%s", result[k]->value, value);
+               free (result[k]->value);
+               free (value);
+               result[k]->value = sptr;
+               cgiDebugOutput (1, "%s: %s", result[i]->name, result[i]->value);
            }
        }
        cp = ++ip;
     }
 
-    if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
-       return NULL;
-
     res->vars = result;
     res->cookies = NULL;
     res->files = NULL;
@@ -635,7 +670,26 @@ s_cgi *cgiInit()
     s_cgi *res;
 
     res = cgiReadVariables ();
-    res->cookies = cgiReadCookies ();
+    if (res)
+       res->cookies = cgiReadCookies ();
+    else
+       {
+           /* In some cases, we might have no other CGI results.
+              But we still have cookies! */
+           s_cookie **cookies;
+           cookies = cgiReadCookies();
+           if (cookies) {
+               /* We need to create a s_cgi structure. */
+               if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
+                   return NULL;
+               res->vars = NULL;
+               res->cookies = cookies;
+               res->files = NULL;
+               
+           } else {
+               return NULL;
+           }
+       }
 
     if (!res->vars && !res->cookies && !res->files) {
        free (res);
@@ -686,6 +740,54 @@ char **cgiGetVariables (s_cgi *parms)
     return res;
 }
 
+/* cgiGetFiles
+ *
+ * Returns a list of names of all files.
+ */
+char **cgiGetFiles (s_cgi *parms)
+{
+    int i;
+    char **res = NULL;
+    int len;
+
+    if (!parms || !parms->files)
+       return NULL;
+
+    for (i=0;parms->files[i]; i++);
+    len = sizeof (char *) * ++i;
+    if ((res = (char **)malloc (len)) == NULL)
+       return NULL;
+    memset (res, 0, len);
+    for (i=0;parms->files[i]; i++) {
+       len = strlen (parms->files[i]->name) +1;
+       if ((res[i] = (char *)malloc (len)) == NULL)
+           return NULL;
+       memset (res[i], 0, len);
+       strcpy (res[i], parms->files[i]->name);
+    }
+    return res;
+}
+
+/* cgiGetFile
+ *
+ * Return data structure for CGI file variable
+ */
+s_file *cgiGetFile (s_cgi *parms, const char *name)
+{
+    int i;
+
+    if (!parms || !parms->files)
+       return NULL;
+
+    for (i=0;parms->files[i]; i++)
+       if (!strcmp(name,parms->files[i]->name)) {
+           cgiDebugOutput (1, "%s found as %s", name, parms->files[i]->filename);
+           return parms->files[i];
+       }
+    cgiDebugOutput (1, "%s not found", name);
+    return NULL;
+}
+
 void cgiRedirect (const char *url)
 {
     if (url && strlen(url)) {
@@ -702,7 +804,7 @@ void cgiFreeList (char **list)
 
     for (i=0; list[i] != NULL; i++)
        free (list[i]);
-       free (list);
+    free (list);
 }
 
 void cgiFree (s_cgi *parms)
@@ -737,6 +839,22 @@ void cgiFree (s_cgi *parms)
        }
        free (parms->cookies);
     }
+    if (parms->files) {
+       for (i=0;parms->files[i]; i++) {
+           if (parms->files[i]->name)
+               free (parms->files[i]->name);
+           if (parms->files[i]->type)
+               free (parms->files[i]->type);
+           if (parms->files[i]->filename)
+               free (parms->files[i]->filename);
+           if (parms->files[i]->tmpfile) {
+               unlink (parms->files[i]->tmpfile);
+               free (parms->files[i]->tmpfile);
+           }
+           free (parms->files[i]);
+       }
+       free (parms->files);
+    }
     free (parms);
 
     if (cgiHeaderString) {
@@ -749,6 +867,18 @@ void cgiFree (s_cgi *parms)
     }
 }
 
+#ifndef HAVE_STRNDUP
+char *strndup(const char *s, size_t n) {
+    char *fnval;
+
+    if ((fnval = (char *)malloc (n + 1)) == NULL)
+       return NULL;
+    strncpy(fnval, s, n);
+    fnval[n] = '\0';
+    return fnval;
+}
+#endif
+
 /*
  * Local variables:
  *  c-indent-level: 4