]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cgi-bin/var.c
SIGSEGV in CUPS web ui when adding a printer
[thirdparty/cups.git] / cgi-bin / var.c
index 17d507756d8928723f9c7dfc5076d2876db3c3fa..fb9d051c0ba7e1e83fb4998e16b659ed478ea727 100644 (file)
@@ -1,53 +1,27 @@
 /*
- * "$Id: var.c 5549 2006-05-19 19:39:28Z mike $"
+ * CGI form variable and array functions for CUPS.
  *
- *   CGI form variable and array functions.
+ * Copyright © 2007-2019 by Apple Inc.
+ * Copyright © 1997-2005 by Easy Software Products.
  *
- *   Copyright 1997-2005 by Easy Software Products.
- *
- *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
- *
- * Contents:
- *
- *   cgiCheckVariables()        - Check for the presence of "required" variables.
- *   cgiGetArray()              - Get an element from a form array...
- *   cgiGetFile()               - Get the file (if any) that was submitted in the form.
- *   cgiGetSize()               - Get the size of a form array value.
- *   cgiGetVariable()           - Get a CGI variable from the database...
- *   cgiInitialize()            - Initialize the CGI variable "database"...
- *   cgiIsPOST()                - Determine whether this page was POSTed.
- *   cgiSetArray()              - Set array element N to the specified string.
- *   cgiSetSize()               - Set the array size.
- *   cgiSetVariable()           - Set a CGI variable in the database...
- *   cgi_add_variable()         - Add a form variable.
- *   cgi_compare_variables()    - Compare two variables.
- *   cgi_find_variable()        - Find a variable...
- *   cgi_initialize_get()       - Initialize form variables using the GET method.
- *   cgi_initialize_multipart() - Initialize variables and file using the POST method.
- *   cgi_initialize_post()      - Initialize variables using the POST method.
- *   cgi_initialize_string()    - Initialize form variables from a string.
- *   cgi_passwd()               - Catch authentication requests and notify the server.
- *   cgi_sort_variables()       - Sort all form variables for faster lookup.
- *   cgi_unlink_file()          - Remove the uploaded form.
+ * Licensed under Apache License v2.0.  See the file "LICENSE" for more
+ * information.
+ */
+
+/*
+ * Include necessary headers...
  */
 
 /*#define DEBUG*/
 #include "cgi-private.h"
-#include <errno.h>
+#include <cups/http.h>
+
+
+/*
+ * Session ID name
+ */
+
+#define CUPS_SID       "org.cups.sid"
 
 
 /*
 
 typedef struct                         /**** Form variable structure ****/
 {
-  const char   *name;                  /* Name of variable */
+  char         *name;                  /* Name of variable */
   int          nvalues,                /* Number of values */
                avalues;                /* Number of values allocated */
-  const char   **values;               /* Value(s) of variable */
+  char         **values;               /* Value(s) of variable */
 } _cgi_var_t;
 
 
@@ -67,6 +41,8 @@ typedef struct                                /**** Form variable structure ****/
  * Local globals...
  */
 
+static int             num_cookies = 0;/* Number of cookies */
+static cups_option_t   *cookies = NULL;/* Cookies */
 static int             form_count = 0, /* Form variable count */
                        form_alloc = 0; /* Number of variables allocated */
 static _cgi_var_t      *form_vars = NULL;
@@ -84,11 +60,13 @@ static void         cgi_add_variable(const char *name, int element,
 static int             cgi_compare_variables(const _cgi_var_t *v1,
                                              const _cgi_var_t *v2);
 static _cgi_var_t      *cgi_find_variable(const char *name);
+static void            cgi_initialize_cookies(void);
 static int             cgi_initialize_get(void);
 static int             cgi_initialize_multipart(const char *boundary);
 static int             cgi_initialize_post(void);
 static int             cgi_initialize_string(const char *data);
 static const char      *cgi_passwd(const char *prompt);
+static const char      *cgi_set_sid(void);
 static void            cgi_sort_variables(void);
 static void            cgi_unlink_file(void);
 
@@ -136,7 +114,12 @@ cgiCheckVariables(const char *names)       /* I - Variables to look for */
       return (0);
 
     if (*val == '\0')
+    {
+      free((void *)val);
       return (0);      /* Can't be blank, either! */
+    }
+
+    free((void *)val);
   }
 
   return (1);
@@ -144,10 +127,37 @@ cgiCheckVariables(const char *names)      /* I - Variables to look for */
 
 
 /*
- * 'cgiGetArray()' - Get an element from a form array...
+ * 'cgiClearVariables()' - Clear all form variables.
+ */
+
+void
+cgiClearVariables(void)
+{
+  int          i, j;                   /* Looping vars */
+  _cgi_var_t   *v;                     /* Current variable */
+
+
+  fputs("DEBUG: cgiClearVariables called.\n", stderr);
+
+  for (v = form_vars, i = form_count; i > 0; v ++, i --)
+  {
+    free(v->name);
+    for (j = 0; j < v->nvalues; j ++)
+      if (v->values[j])
+        free(v->values[j]);
+  }
+
+  form_count = 0;
+
+  cgi_unlink_file();
+}
+
+
+/*
+ * 'cgiGetArray()' - Get an element from a form array.
  */
 
-const char *                           /* O - Element value or NULL */
+char *                                 /* O - Element value or NULL */
 cgiGetArray(const char *name,          /* I - Name of array variable */
             int        element)                /* I - Element number (0 to N) */
 {
@@ -157,13 +167,24 @@ cgiGetArray(const char *name,             /* I - Name of array variable */
   if ((var = cgi_find_variable(name)) == NULL)
     return (NULL);
 
-  if (var->nvalues == 1)
-    return (var->values[0]);
-
   if (element < 0 || element >= var->nvalues)
     return (NULL);
 
-  return (var->values[element]);
+  if (var->values[element] == NULL)
+    return (NULL);
+
+  return (strdup(var->values[element]));
+}
+
+
+/*
+ * 'cgiGetCookie()' - Get a cookie value.
+ */
+
+const char *                           /* O - Value or NULL */
+cgiGetCookie(const char *name)         /* I - Name of cookie */
+{
+  return (cupsGetOption(name, num_cookies, cookies));
 }
 
 
@@ -196,13 +217,13 @@ cgiGetSize(const char *name)              /* I - Name of variable */
 
 
 /*
- * 'cgiGetVariable()' - Get a CGI variable from the database...
+ * 'cgiGetVariable()' - Get a CGI variable from the database.
  *
  * Returns NULL if the variable doesn't exist.  If the variable is an
- * array of values, returns the last element...
+ * array of values, returns the last element.
  */
 
-const char *                           /* O - Value of variable */
+char *                                 /* O - Value of variable */
 cgiGetVariable(const char *name)       /* I - Name of variable */
 {
   const _cgi_var_t     *var;           /* Returned variable */
@@ -210,27 +231,21 @@ cgiGetVariable(const char *name)  /* I - Name of variable */
 
   var = cgi_find_variable(name);
 
-#ifdef DEBUG
-  if (var == NULL)
-    printf("cgiGetVariable(\"%s\") is returning NULL...\n", name);
-  else
-    printf("cgiGetVariable(\"%s\") is returning \"%s\"...\n", name,
-           var->values[var->nvalues - 1]);
-#endif /* DEBUG */
-
-  return ((var == NULL) ? NULL : var->values[var->nvalues - 1]);
+  return ((var == NULL) ? NULL : strdup(var->values[var->nvalues - 1]));
 }
 
 
 /*
- * 'cgiInitialize()' - Initialize the CGI variable "database"...
+ * 'cgiInitialize()' - Initialize the CGI variable "database".
  */
 
 int                                    /* O - Non-zero if there was form data */
 cgiInitialize(void)
 {
-  const char   *method;                /* Form posting method */
-  const char   *content_type;          /* Content-Type of post data */
+  const char   *method,                /* Form posting method */
+               *content_type,          /* Content-Type of post data */
+               *cups_sid_cookie,       /* SID cookie */
+               *cups_sid_form;         /* SID form variable */
 
 
  /*
@@ -251,9 +266,22 @@ cgiInitialize(void)
   */
 
   setbuf(stdout, NULL);
-  puts("Content-type: text/plain\n");
 #endif /* DEBUG */
 
+ /*
+  * Get cookies...
+  */
+
+  cgi_initialize_cookies();
+
+  if ((cups_sid_cookie = cgiGetCookie(CUPS_SID)) == NULL)
+  {
+    fputs("DEBUG: " CUPS_SID " cookie not found, initializing!\n", stderr);
+    cups_sid_cookie = cgi_set_sid();
+  }
+
+  fprintf(stderr, "DEBUG: " CUPS_SID " cookie is \"%s\"\n", cups_sid_cookie);
+
  /*
   * Get the request method (GET or POST)...
   */
@@ -267,9 +295,9 @@ cgiInitialize(void)
   * Grab form data from the corresponding location...
   */
 
-  if (!strcasecmp(method, "GET"))
+  if (!_cups_strcasecmp(method, "GET"))
     return (cgi_initialize_get());
-  else if (!strcasecmp(method, "POST") && content_type)
+  else if (!_cups_strcasecmp(method, "POST") && content_type)
   {
     const char *boundary = strstr(content_type, "boundary=");
 
@@ -277,9 +305,34 @@ cgiInitialize(void)
       boundary += 9;
 
     if (content_type && !strncmp(content_type, "multipart/form-data; ", 21))
-      return (cgi_initialize_multipart(boundary));
+    {
+      if (!cgi_initialize_multipart(boundary))
+        return (0);
+    }
+    else if (!cgi_initialize_post())
+      return (0);
+
+    if ((cups_sid_form = cgiGetVariable(CUPS_SID)) == NULL ||
+       strcmp(cups_sid_cookie, cups_sid_form))
+    {
+      if (cups_sid_form)
+       fprintf(stderr, "DEBUG: " CUPS_SID " form variable is \"%s\"\n",
+               cups_sid_form);
+      else
+       fputs("DEBUG: " CUPS_SID " form variable is not present.\n", stderr);
+
+      free((void *)cups_sid_form);
+
+      cgiClearVariables();
+
+      return (0);
+    }
     else
-      return (cgi_initialize_post());
+    {
+      free((void *)cups_sid_form);
+
+      return (1);
+    }
   }
   else
     return (0);
@@ -322,6 +375,8 @@ cgiSetArray(const char *name,               /* I - Name of variable */
   if (name == NULL || value == NULL || element < 0 || element > 100000)
     return;
 
+  fprintf(stderr, "DEBUG: cgiSetArray: %s[%d]=\"%s\"\n", name, element, value);
+
   if ((var = cgi_find_variable(name)) == NULL)
   {
     cgi_add_variable(name, element, value);
@@ -331,9 +386,14 @@ cgiSetArray(const char *name,              /* I - Name of variable */
   {
     if (element >= var->avalues)
     {
+      char **temp;                     /* Temporary pointer */
+
+      temp = (char **)realloc((void *)(var->values), sizeof(char *) * (size_t)(element + 16));
+      if (!temp)
+        return;
+
       var->avalues = element + 16;
-      var->values  = (const char **)realloc((void *)(var->values),
-                                            sizeof(char *) * var->avalues);
+      var->values  = temp;
     }
 
     if (element >= var->nvalues)
@@ -351,6 +411,38 @@ cgiSetArray(const char *name,              /* I - Name of variable */
 }
 
 
+/*
+ * 'cgiSetCookie()' - Set a cookie value.
+ */
+
+void
+cgiSetCookie(const char *name,         /* I - Name */
+             const char *value,                /* I - Value */
+             const char *path,         /* I - Path (typically "/") */
+            const char *domain,        /* I - Domain name */
+            time_t     expires,        /* I - Expiration date (0 for session) */
+            int        secure)         /* I - Require SSL */
+{
+  num_cookies = cupsAddOption(name, value, num_cookies, &cookies);
+
+  printf("Set-Cookie: %s=%s;", name, value);
+  if (path)
+    printf(" path=%s;", path);
+  if (domain)
+    printf(" domain=%s;", domain);
+  if (expires)
+  {
+    char       date[256];              /* Date string */
+
+    printf(" expires=%s;", httpGetDateString2(expires, date, sizeof(date)));
+  }
+  if (secure)
+    puts(" httponly; secure;");
+  else
+    puts(" httponly;");
+}
+
+
 /*
  * 'cgiSetSize()' - Set the array size.
  */
@@ -371,9 +463,14 @@ cgiSetSize(const char *name,               /* I - Name of variable */
 
   if (size >= var->avalues)
   {
+    char **temp;                       /* Temporary pointer */
+
+    temp = (char **)realloc((void *)(var->values), sizeof(char *) * (size_t)(size + 16));
+    if (!temp)
+      return;
+
     var->avalues = size + 16;
-    var->values  = (const char **)realloc((void *)(var->values),
-                                          sizeof(char *) * var->avalues);
+    var->values  = temp;
   }
 
   if (size > var->nvalues)
@@ -393,7 +490,7 @@ cgiSetSize(const char *name,                /* I - Name of variable */
 
 
 /*
- * 'cgiSetVariable()' - Set a CGI variable in the database...
+ * 'cgiSetVariable()' - Set a CGI variable in the database.
  *
  * If the variable is an array, this truncates the array to a single element.
  */
@@ -409,6 +506,8 @@ cgiSetVariable(const char *name,    /* I - Name of variable */
   if (name == NULL || value == NULL)
     return;
 
+  fprintf(stderr, "cgiSetVariable: %s=\"%s\"\n", name, value);
+
   if ((var = cgi_find_variable(name)) == NULL)
   {
     cgi_add_variable(name, 0, value);
@@ -435,31 +534,37 @@ cgi_add_variable(const char *name,        /* I - Variable name */
                 int        element,    /* I - Array element number */
                  const char *value)    /* I - Variable value */
 {
-  _cgi_var_t   *var;                           /* New variable */
+  _cgi_var_t   *var;                   /* New variable */
 
 
   if (name == NULL || value == NULL || element < 0 || element > 100000)
     return;
 
-#ifdef DEBUG
-  printf("Adding variable \'%s\' with value \'%s\'...\n", name, value);
-#endif /* DEBUG */
-
   if (form_count >= form_alloc)
   {
+    _cgi_var_t *temp_vars;             /* Temporary form pointer */
+
+
     if (form_alloc == 0)
-      form_vars = malloc(sizeof(_cgi_var_t) * 16);
+      temp_vars = malloc(sizeof(_cgi_var_t) * 16);
     else
-      form_vars = realloc(form_vars, (form_alloc + 16) * sizeof(_cgi_var_t));
+      temp_vars = realloc(form_vars, (size_t)(form_alloc + 16) * sizeof(_cgi_var_t));
+
+    if (!temp_vars)
+      return;
 
+    form_vars  = temp_vars;
     form_alloc += 16;
   }
 
-  var                  = form_vars + form_count;
+  var = form_vars + form_count;
+
+  if ((var->values = calloc((size_t)element + 1, sizeof(char *))) == NULL)
+    return;
+
   var->name            = strdup(name);
   var->nvalues         = element + 1;
   var->avalues         = element + 1;
-  var->values          = calloc(element + 1, sizeof(char *));
   var->values[element] = strdup(value);
 
   form_count ++;
@@ -475,12 +580,12 @@ cgi_compare_variables(
     const _cgi_var_t *v1,              /* I - First variable */
     const _cgi_var_t *v2)              /* I - Second variable */
 {
-  return (strcasecmp(v1->name, v2->name));
+  return (_cups_strcasecmp(v1->name, v2->name));
 }
 
 
 /*
- * 'cgi_find_variable()' - Find a variable...
+ * 'cgi_find_variable()' - Find a variable.
  */
 
 static _cgi_var_t *                    /* O - Variable pointer or NULL */
@@ -492,13 +597,117 @@ cgi_find_variable(const char *name)      /* I - Name of variable */
   if (form_count < 1 || name == NULL)
     return (NULL);
 
-  key.name = name;
+  key.name = (char *)name;
 
-  return ((_cgi_var_t *)bsearch(&key, form_vars, form_count, sizeof(_cgi_var_t),
+  return ((_cgi_var_t *)bsearch(&key, form_vars, (size_t)form_count, sizeof(_cgi_var_t),
                            (int (*)(const void *, const void *))cgi_compare_variables));
 }
 
 
+/*
+ * 'cgi_initialize_cookies()' - Initialize cookies.
+ */
+
+static void
+cgi_initialize_cookies(void)
+{
+  const char   *cookie;                /* HTTP_COOKIE environment variable */
+  char         name[128],              /* Name string */
+               value[512],             /* Value string */
+               *ptr;                   /* Pointer into name/value */
+
+
+  if ((cookie = getenv("HTTP_COOKIE")) == NULL)
+    return;
+
+  while (*cookie)
+  {
+    int        skip = 0;                       /* Skip this cookie? */
+
+   /*
+    * Skip leading whitespace...
+    */
+
+    while (isspace(*cookie & 255))
+      cookie ++;
+    if (!*cookie)
+      break;
+
+   /*
+    * Copy the name...
+    */
+
+    for (ptr = name; *cookie && *cookie != '=';)
+      if (ptr < (name + sizeof(name) - 1))
+      {
+        *ptr++ = *cookie++;
+      }
+      else
+      {
+        skip = 1;
+       cookie ++;
+      }
+
+    if (*cookie != '=')
+      break;
+
+    *ptr = '\0';
+    cookie ++;
+
+   /*
+    * Then the value...
+    */
+
+    if (*cookie == '\"')
+    {
+      for (cookie ++, ptr = value; *cookie && *cookie != '\"';)
+        if (ptr < (value + sizeof(value) - 1))
+       {
+         *ptr++ = *cookie++;
+       }
+       else
+       {
+         skip = 1;
+         cookie ++;
+       }
+
+      if (*cookie == '\"')
+        cookie ++;
+      else
+        skip = 1;
+    }
+    else
+    {
+      for (ptr = value; *cookie && *cookie != ';';)
+        if (ptr < (value + sizeof(value) - 1))
+       {
+         *ptr++ = *cookie++;
+       }
+       else
+       {
+         skip = 1;
+         cookie ++;
+       }
+    }
+
+    if (*cookie == ';')
+      cookie ++;
+    else if (*cookie)
+      skip = 1;
+
+    *ptr = '\0';
+
+   /*
+    * Then add the cookie to an array as long as the name doesn't start with
+    * "$"...
+    */
+
+    if (name[0] != '$' && !skip)
+      num_cookies = cupsAddOption(name, value, num_cookies, &cookies);
+  }
+}
+
+
 /*
  * 'cgi_initialize_get()' - Initialize form variables using the GET method.
  */
@@ -509,10 +718,6 @@ cgi_initialize_get(void)
   char *data;                          /* Pointer to form data string */
 
 
-#ifdef DEBUG
-  puts("Initializing variables using GET method...");
-#endif /* DEBUG */
-
  /*
   * Check to see if there is anything for us to read...
   */
@@ -530,7 +735,8 @@ cgi_initialize_get(void)
 
 
 /*
- * 'cgi_initialize_multipart()' - Initialize variables and file using the POST method.
+ * 'cgi_initialize_multipart()' - Initialize variables and file using the POST
+ *                                method.
  *
  * TODO: Update to support files > 2GB.
  */
@@ -547,12 +753,10 @@ cgi_initialize_multipart(
                *ptr,                   /* Pointer into name/filename */
                *end;                   /* End of buffer */
   int          ch,                     /* Character from file */
-               fd,                     /* Temporary file descriptor */
-               blen;                   /* Length of boundary string */
+               fd;                     /* Temporary file descriptor */
+  size_t       blen;                   /* Length of boundary string */
 
 
-  DEBUG_printf(("cgi_initialize_multipart(boundary=\"%s\")\n", boundary));
-
  /*
   * Read multipart form data until we run out...
   */
@@ -608,27 +812,27 @@ cgi_initialize_multipart(
        /*
         * Copy file data to the temp file...
        */
-       
+
         ptr = line;
 
        while ((ch = getchar()) != EOF)
        {
-         *ptr++ = ch;
+         *ptr++ = (char)ch;
 
-          if ((ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
+          if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
          {
            ptr -= blen;
            break;
          }
 
-          if ((ptr - line - blen) >= 8192)
+          if ((ptr - line - (int)blen) >= 8192)
          {
           /*
            * Write out the first 8k of the buffer...
            */
 
            write(fd, line, 8192);
-           memmove(line, line + 8192, ptr - line - 8192);
+           memmove(line, line + 8192, (size_t)(ptr - line - 8192));
            ptr -= 8192;
          }
        }
@@ -638,7 +842,7 @@ cgi_initialize_multipart(
        */
 
        if (ptr > line)
-          write(fd, line, ptr - line);
+          write(fd, line, (size_t)(ptr - line));
 
        close(fd);
       }
@@ -655,9 +859,9 @@ cgi_initialize_multipart(
        while ((ch = getchar()) != EOF)
        {
          if (ptr < end)
-           *ptr++ = ch;
+           *ptr++ = (char)ch;
 
-          if ((ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
+          if ((size_t)(ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
          {
            ptr -= blen;
            break;
@@ -680,12 +884,13 @@ cgi_initialize_multipart(
          if (line[0])
             cgiSetArray(name, atoi(ptr) - 1, line);
        }
-       else if (cgiGetVariable(name))
+       else if ((ptr = cgiGetVariable(name)) != NULL)
        {
         /*
          * Add another element in the array...
          */
 
+          free(ptr);
          cgiSetArray(name, cgiGetSize(name), line);
        }
        else
@@ -712,7 +917,7 @@ cgi_initialize_multipart(
       filename[0] = '\0';
       mimetype[0] = '\0';
     }
-    else if (!strncasecmp(line, "Content-Disposition:", 20))
+    else if (!_cups_strncasecmp(line, "Content-Disposition:", 20))
     {
       if ((ptr = strstr(line + 20, " name=\"")) != NULL)
       {
@@ -730,7 +935,7 @@ cgi_initialize_multipart(
          *ptr = '\0';
       }
     }
-    else if (!strncasecmp(line, "Content-Type:", 13))
+    else if (!_cups_strncasecmp(line, "Content-Type:", 13))
     {
       for (ptr = line + 13; isspace(*ptr & 255); ptr ++);
 
@@ -757,17 +962,13 @@ cgi_initialize_multipart(
 static int                             /* O - 1 if form data was read */
 cgi_initialize_post(void)
 {
-  char *content_length,                /* Length of input data (string) */
-       *data;                          /* Pointer to form data string */
-  int  length,                         /* Length of input data */
-       nbytes,                         /* Number of bytes read this read() */
-       tbytes,                         /* Total number of bytes read */
-       status;                         /* Return status */
-
+  char         *content_length,        /* Length of input data (string) */
+               *data;                  /* Pointer to form data string */
+  size_t       length,                 /* Length of input data */
+               tbytes;                 /* Total number of bytes read */
+  ssize_t      nbytes;                 /* Number of bytes read this read() */
+  int          status;                 /* Return status */
 
-#ifdef DEBUG
-  puts("Initializing variables using POST method...");
-#endif /* DEBUG */
 
  /*
   * Check to see if there is anything for us to read...
@@ -781,7 +982,7 @@ cgi_initialize_post(void)
   * Get the length of the input stream and allocate a buffer for it...
   */
 
-  length = atoi(content_length);
+  length = (size_t)strtol(content_length, NULL, 10);
   data   = malloc(length + 1);
 
   if (data == NULL)
@@ -791,13 +992,29 @@ cgi_initialize_post(void)
   * Read the data into the buffer...
   */
 
-  for (tbytes = 0; tbytes < length; tbytes += nbytes)
-    if ((nbytes = read(0, data + tbytes, length - tbytes)) < 0)
+  for (tbytes = 0; tbytes < length; tbytes += (size_t)nbytes)
+    if ((nbytes = read(0, data + tbytes, (size_t)(length - tbytes))) < 0)
+    {
       if (errno != EAGAIN)
       {
         free(data);
         return (0);
       }
+      else
+        nbytes = 0;
+    }
+    else if (nbytes == 0)
+    {
+     /*
+      * CUPS STR #3176: OpenBSD: Early end-of-file on POST data causes 100% CPU
+      *
+      * This should never happen, but does on OpenBSD.  If we see early end-of-
+      * file, treat this as an error and process no data.
+      */
+
+      free(data);
+      return (0);
+    }
 
   data[length] = '\0';
 
@@ -828,7 +1045,8 @@ cgi_initialize_string(const char *data)    /* I - Form data string */
   char *s,                             /* Pointer to current form string */
        ch,                             /* Temporary character */
        name[255],                      /* Name of form variable */
-       value[65536];                   /* Variable value... */
+       value[65536],                   /* Variable value */
+       *temp;                          /* Temporary pointer */
 
 
  /*
@@ -881,13 +1099,16 @@ cgi_initialize_string(const char *data)  /* I - Form data string */
            * Read the hex code...
            */
 
+            if (!isxdigit(data[1] & 255) || !isxdigit(data[2] & 255))
+             return (0);
+
             if (s < (value + sizeof(value) - 1))
            {
               data ++;
               ch = *data - '0';
               if (ch > 9)
                ch -= 7;
-              *s = ch << 4;
+              *s = (char)(ch << 4);
 
               data ++;
               ch = *data - '0';
@@ -914,7 +1135,7 @@ cgi_initialize_string(const char *data)    /* I - Form data string */
     if (s > value)
       s --;
 
-    while (s >= value && *s == ' ')
+    while (s >= value && isspace(*s & 255))
       *s-- = '\0';
 
    /*
@@ -927,8 +1148,11 @@ cgi_initialize_string(const char *data)   /* I - Form data string */
       if (value[0])
         cgiSetArray(name, atoi(s) - 1, value);
     }
-    else if (cgiGetVariable(name) != NULL)
+    else if ((temp = cgiGetVariable(name)) != NULL)
+    {
+      free(temp);
       cgiSetArray(name, cgiGetSize(name), value);
+    }
     else
       cgiSetVariable(name, value);
   }
@@ -969,31 +1193,56 @@ cgi_passwd(const char *prompt)           /* I - Prompt (not used) */
 
 
 /*
- * 'cgi_sort_variables()' - Sort all form variables for faster lookup.
+ * 'cgi_set_sid()' - Set the CUPS session ID.
  */
 
-static void
-cgi_sort_variables(void)
+static const char *                    /* O - New session ID */
+cgi_set_sid(void)
 {
-#ifdef DEBUG
-  int  i;
+  char                 buffer[512],    /* SID data */
+                       sid[33];        /* SID string */
+  unsigned char                sum[16];        /* MD5 sum */
+  const char           *remote_addr,   /* REMOTE_ADDR */
+                       *server_name,   /* SERVER_NAME */
+                       *server_port;   /* SERVER_PORT */
+  struct timeval       curtime;        /* Current time */
+
+
+  if ((remote_addr = getenv("REMOTE_ADDR")) == NULL)
+    remote_addr = "REMOTE_ADDR";
+  if ((server_name = getenv("SERVER_NAME")) == NULL)
+    server_name = "SERVER_NAME";
+  if ((server_port = getenv("SERVER_PORT")) == NULL)
+    server_port = "SERVER_PORT";
+
+  gettimeofday(&curtime, NULL);
+  CUPS_SRAND(curtime.tv_sec + curtime.tv_usec);
+  snprintf(buffer, sizeof(buffer), "%s:%s:%s:%02X%02X%02X%02X%02X%02X%02X%02X",
+           remote_addr, server_name, server_port,
+          (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255,
+          (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255,
+          (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255,
+          (unsigned)CUPS_RAND() & 255, (unsigned)CUPS_RAND() & 255);
+  cupsHashData("md5", (unsigned char *)buffer, strlen(buffer), sum, sizeof(sum));
+
+  cgiSetCookie(CUPS_SID, cupsHashString(sum, sizeof(sum), sid, sizeof(sid)), "/", NULL, 0, 0);
+
+  return (cupsGetOption(CUPS_SID, num_cookies, cookies));
+}
 
 
-  puts("Sorting variables...");
-#endif /* DEBUG */
+/*
+ * 'cgi_sort_variables()' - Sort all form variables for faster lookup.
+ */
 
+static void
+cgi_sort_variables(void)
+{
   if (form_count < 2)
     return;
 
-  qsort(form_vars, form_count, sizeof(_cgi_var_t),
+  qsort(form_vars, (size_t)form_count, sizeof(_cgi_var_t),
         (int (*)(const void *, const void *))cgi_compare_variables);
-
-#ifdef DEBUG
-  puts("Sorted variable list is:");
-  for (i = 0; i < form_count; i ++)
-    printf("%d: %s (%d) = \"%s\" ...\n", i, form_vars[i].name,
-           form_vars[i].nvalues, form_vars[i].values[0]);
-#endif /* DEBUG */
 }
 
 
@@ -1024,8 +1273,3 @@ cgi_unlink_file(void)
     form_file = NULL;
   }
 }
-
-
-/*
- * End of "$Id: var.c 5549 2006-05-19 19:39:28Z mike $".
- */