]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/client.c
Sync changes and files from 1.1.x.
[thirdparty/cups.git] / scheduler / client.c
index 27a3331b8c30e2f969d17d236c121f6ce5124e00..de825609becaf707cdd59df3c2d563d34c4f766e 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: client.c,v 1.91.2.64 2003/07/20 03:13:08 mike Exp $"
+ * "$Id$"
  *
  *   Client routines for the Common UNIX Printing System (CUPS) scheduler.
  *
- *   Copyright 1997-2003 by Easy Software Products, all rights reserved.
+ *   Copyright 1997-2004 by Easy Software Products, all rights reserved.
  *
  *   These coded instructions, statements, and computer programs are the
  *   property of Easy Software Products and are protected by Federal
@@ -15,9 +15,9 @@
  *       Attn: CUPS Licensing Information
  *       Easy Software Products
  *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636-3111 USA
+ *       Hollywood, Maryland 20636 USA
  *
- *       Voice: (301) 373-9603
+ *       Voice: (301) 373-9600
  *       EMail: cups-info@cups.org
  *         WWW: http://www.cups.org
  *
  *   SendError()           - Send an error message via HTTP.
  *   SendFile()            - Send a file via HTTP.
  *   SendHeader()          - Send an HTTP request.
- *   ShutdownClient()      - Shutdown the receiving end of a connection.
  *   UpdateCGI()           - Read status messages from CGI scripts and programs.
  *   WriteClient()         - Write data to a client as needed.
  *   check_if_modified()   - Decode an "If-Modified-Since" line.
  *   decode_auth()         - Decode an authorization string.
  *   get_file()            - Get a filename and state info.
  *   install_conf_file()   - Install a configuration file.
+ *   is_path_absolute()    - Is a path absolute and free of relative elements.
  *   pipe_command()        - Pipe the output of a command to the remote client.
  *   CDSAReadFunc()        - Read function for CDSA decryption code.
  *   CDSAWriteFunc()       - Write function for CDSA encryption code.
@@ -64,6 +64,7 @@ static void           decode_auth(client_t *con);
 static char            *get_file(client_t *con, struct stat *filestats, 
                                  char *filename, int len);
 static http_status_t   install_conf_file(client_t *con);
+static int             is_path_absolute(const char *path);
 static int             pipe_command(client_t *con, int infile, int *outfile,
                                     char *command, char *options);
 
@@ -93,7 +94,7 @@ AcceptClient(listener_t *lis) /* I - Listener socket */
                                /* Time of last DoS attack */
 
 
-  LogMessage(L_DEBUG2, "AcceptClient(%p) %d NumClients = %d",
+  LogMessage(L_DEBUG2, "AcceptClient(lis=%p) %d NumClients = %d",
              lis, lis->fd, NumClients);
 
  /*
@@ -111,6 +112,7 @@ AcceptClient(listener_t *lis)       /* I - Listener socket */
 
   memset(con, 0, sizeof(client_t));
   con->http.activity = time(NULL);
+  con->file          = -1;
 
  /*
   * Accept the client and get the remote address...
@@ -163,7 +165,7 @@ AcceptClient(listener_t *lis)       /* I - Listener socket */
 
     return;
   }
-  
+
  /*
   * Get the hostname or format the IP address as needed...
   */
@@ -353,9 +355,10 @@ CloseAllClients(void)
  * 'CloseClient()' - Close a remote client.
  */
 
-void
+int                            /* O - 1 if partial close, 0 if fully closed */
 CloseClient(client_t *con)     /* I - Client to close */
 {
+  int          partial;        /* Do partial close for SSL? */
 #if defined(HAVE_LIBSSL)
   SSL_CTX      *context;       /* Context for encryption */
   SSL          *conn;          /* Connection for encryption */
@@ -368,16 +371,9 @@ CloseClient(client_t *con) /* I - Client to close */
 #endif /* HAVE_GNUTLS */
 
 
-  LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
-
-  if (con->http.input_set)
-    free(con->http.input_set);
+  LogMessage(L_DEBUG, "CloseClient: %d", con->http.fd);
 
-  httpClearCookie(HTTP(con));
-
-  ClearString(&con->filename);
-  ClearString(&con->command);
-  ClearString(&con->options);
+  partial = 0;
 
 #ifdef HAVE_SSL
  /*
@@ -386,6 +382,8 @@ CloseClient(client_t *con)  /* I - Client to close */
 
   if (con->http.tls)
   {
+    partial = 1;
+
 #  ifdef HAVE_LIBSSL
     conn    = (SSL *)(con->http.tls);
     context = SSL_get_SSL_CTX(conn);
@@ -439,76 +437,113 @@ CloseClient(client_t *con)       /* I - Client to close */
   * Close the socket and clear the file from the input set for select()...
   */
 
-  if (con->http.fd >= 0)
+  if (con->http.fd > 0)
   {
-    LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...",
-               con->http.fd);
-    close(con->http.fd);
-    FD_CLR(con->http.fd, InputSet);
-    FD_CLR(con->http.fd, OutputSet);
-    con->http.fd = 0;
-  }
+    if (partial)
+    {
+     /*
+      * Only do a partial close so that the encrypted client gets everything.
+      */
 
-  if (con->pipe_pid != 0)
-  {
-    LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet...",
-               con->file);
-    FD_CLR(con->file, InputSet);
+      LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from OutputSet...",
+                con->http.fd);
+      shutdown(con->http.fd, 0);
+      FD_CLR(con->http.fd, OutputSet);
+    }
+    else
+    {
+     /*
+      * Shut the socket down fully...
+      */
+
+      LogMessage(L_DEBUG2, "CloseClient: Removing fd %d from InputSet and OutputSet...",
+                con->http.fd);
+      close(con->http.fd);
+      FD_CLR(con->http.fd, InputSet);
+      FD_CLR(con->http.fd, OutputSet);
+      con->http.fd = -1;
+    }
   }
 
-  if (con->file)
+  if (con->pipe_pid != 0)
   {
    /*
-    * Close the open data file...
+    * Stop any CGI process...
     */
 
-    if (con->pipe_pid)
-      kill(con->pipe_pid, SIGKILL);
+    LogMessage(L_DEBUG2, "CloseClient: %d Killing process ID %d...",
+               con->http.fd, con->pipe_pid);
+    kill(con->pipe_pid, SIGKILL);
+  }
 
-    LogMessage(L_DEBUG2, "CloseClient() %d Closing data file %d.",
-               con->http.fd, con->file);
-    LogMessage(L_DEBUG2, "CloseClient() %d Removing fd %d from InputSet.",
+  if (con->file >= 0)
+  {
+    if (FD_ISSET(con->file, InputSet))
+    {
+      LogMessage(L_DEBUG2, "CloseClient: %d Removing fd %d from InputSet...",
+                con->http.fd, con->file);
+      FD_CLR(con->file, InputSet);
+    }
+
+    LogMessage(L_DEBUG2, "CloseClient: %d Closing data file %d.",
                con->http.fd, con->file);
 
-    FD_CLR(con->file, InputSet);
     close(con->file);
-    con->file = 0;
+    con->file = -1;
   }
 
-  if (con->request)
+  if (!partial)
   {
-    ippDelete(con->request);
-    con->request = NULL;
-  }
+   /*
+    * Free memory...
+    */
 
-  if (con->response)
-  {
-    ippDelete(con->response);
-    con->response = NULL;
-  }
+    if (con->http.input_set)
+      free(con->http.input_set);
 
-  if (con->language)
-  {
-    cupsLangFree(con->language);
-    con->language = NULL;
-  }
+    httpClearCookie(HTTP(con));
 
- /*
-  * Re-enable new client connections if we are going back under the
-  * limit...
-  */
+    ClearString(&con->filename);
+    ClearString(&con->command);
+    ClearString(&con->options);
 
-  if (NumClients == MaxClients)
-    ResumeListening();
+    if (con->request)
+    {
+      ippDelete(con->request);
+      con->request = NULL;
+    }
 
- /*
-  * Compact the list of clients as necessary...
-  */
+    if (con->response)
+    {
+      ippDelete(con->response);
+      con->response = NULL;
+    }
+
+    if (con->language)
+    {
+      cupsLangFree(con->language);
+      con->language = NULL;
+    }
+
+   /*
+    * Re-enable new client connections if we are going back under the
+    * limit...
+    */
+
+    if (NumClients == MaxClients)
+      ResumeListening();
+
+   /*
+    * Compact the list of clients as necessary...
+    */
 
-  NumClients --;
+    NumClients --;
+
+    if (con < (Clients + NumClients))
+      memmove(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
+  }
 
-  if (con < (Clients + NumClients))
-    memcpy(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
+  return (partial);
 }
 
 
@@ -539,6 +574,9 @@ EncryptClient(client_t *con)        /* I - Client to encrypt */
   SSL_set_fd(conn, con->http.fd);
   if (SSL_accept(conn) != 1)
   {
+    LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!",
+               con->http.hostname);
+
     while ((error = ERR_get_error()) != 0)
       LogMessage(L_ERROR, "EncryptClient: %s", ERR_error_string(error, NULL));
 
@@ -547,6 +585,9 @@ EncryptClient(client_t *con)        /* I - Client to encrypt */
     return (0);
   }
 
+  LogMessage(L_DEBUG, "EncryptClient: %d Connection from %s now encrypted.",
+             con->http.fd, con->http.hostname);
+
   con->http.tls = conn;
   return (1);
   
@@ -569,6 +610,10 @@ EncryptClient(client_t *con)       /* I - Client to encrypt */
                     malloc(sizeof(gnutls_certificate_server_credentials));
   if (credentials == NULL)
   {
+    LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!",
+               con->http.hostname);
+    LogMessage(L_ERROR, "EncryptClient: %s", strerror(errno));
+
     free(conn);
     return (0);
   }
@@ -586,7 +631,10 @@ EncryptClient(client_t *con)       /* I - Client to encrypt */
 
   if (error != GNUTLS_E_SUCCESS)
   {
+    LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!",
+               con->http.hostname);
     LogMessage(L_ERROR, "EncryptClient: %s", gnutls_strerror(error));
+
     gnutls_deinit(conn->session);
     gnutls_certificate_free_credentials(*credentials);
     free(conn);
@@ -594,8 +642,8 @@ EncryptClient(client_t *con)        /* I - Client to encrypt */
     return (0);
   }
 
-  LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
-             con->http.fd);
+  LogMessage(L_DEBUG, "EncryptClient: %d Connection from %s now encrypted.",
+             con->http.fd, con->http.hostname);
 
   conn->credentials = credentials;
   con->http.tls = conn;
@@ -655,7 +703,10 @@ EncryptClient(client_t *con)       /* I - Client to encrypt */
 
   if (error)
   {
-    LogMessage(L_ERROR, "EncryptClient: %d", error);
+    LogMessage(L_ERROR, "EncryptClient: Unable to encrypt connection from %s!",
+               con->http.hostname);
+
+    LogMessage(L_ERROR, "EncryptClient: CDSA error code is %d", error);
 
     con->http.error  = error;
     con->http.status = HTTP_ERROR;
@@ -666,8 +717,9 @@ EncryptClient(client_t *con)        /* I - Client to encrypt */
     return (0);
   }
 
-  LogMessage(L_DEBUG, "EncryptClient() %d Connection now encrypted.",
-             con->http.fd);
+  LogMessage(L_DEBUG, "EncryptClient: %d Connection from %s now encrypted.",
+             con->http.fd, con->http.hostname);
+
   con->http.tls = conn;
   return (1);
 
@@ -705,7 +757,7 @@ IsCGI(client_t    *con,                             /* I - Client connection */
   * Check for known types...
   */
 
-  if (strcasecmp(type->super, "application"))
+  if (!type || strcasecmp(type->super, "application"))
   {
     LogMessage(L_DEBUG2, "IsCGI: Returning 0...");
     return (0);
@@ -823,34 +875,36 @@ IsCGI(client_t    *con,                           /* I - Client connection */
  * 'ReadClient()' - Read data from a client.
  */
 
-int                            /* O - 1 on success, 0 on error */
-ReadClient(client_t *con)      /* I - Client to read from */
+int                                    /* O - 1 on success, 0 on error */
+ReadClient(client_t *con)              /* I - Client to read from */
 {
-  char         line[32768],    /* Line from client... */
-               operation[64],  /* Operation code from socket */
-               version[64];    /* HTTP version number string */
-  int          major, minor;   /* HTTP version numbers */
-  http_status_t        status;         /* Transfer status */
-  ipp_state_t   ipp_state;     /* State of IPP transfer */
-  int          bytes;          /* Number of bytes to POST */
-  char         *filename;      /* Name of file for GET/HEAD */
-  char         buf[1024];      /* Buffer for real filename */
-  struct stat  filestats;      /* File information */
-  mime_type_t  *type;          /* MIME type of file */
-  printer_t    *p;             /* Printer */
-  location_t   *best;          /* Best match for authentication */
-  static unsigned request_id = 0;/* Request ID for temp files */
+  char         line[32768],            /* Line from client... */
+               operation[64],          /* Operation code from socket */
+               version[64],            /* HTTP version number string */
+               locale[64],             /* Locale */
+               *ptr;                   /* Pointer into strings */
+  int          major, minor;           /* HTTP version numbers */
+  http_status_t        status;                 /* Transfer status */
+  ipp_state_t   ipp_state;             /* State of IPP transfer */
+  int          bytes;                  /* Number of bytes to POST */
+  char         *filename;              /* Name of file for GET/HEAD */
+  char         buf[1024];              /* Buffer for real filename */
+  struct stat  filestats;              /* File information */
+  mime_type_t  *type;                  /* MIME type of file */
+  printer_t    *p;                     /* Printer */
+  location_t   *best;                  /* Best match for authentication */
+  static unsigned request_id = 0;      /* Request ID for temp files */
 
 
   status = HTTP_CONTINUE;
 
-  LogMessage(L_DEBUG2, "ReadClient() %d, used=%d", con->http.fd,
-             con->http.used);
+  LogMessage(L_DEBUG2, "ReadClient: %d, used=%d, file=%d", con->http.fd,
+             con->http.used, con->file);
 
   if (con->http.error)
   {
-    CloseClient(con);
-    return (0);
+    LogMessage(L_DEBUG2, "ReadClient: http error seen...");
+    return (CloseClient(con));
   }
 
   switch (con->http.state)
@@ -862,8 +916,8 @@ ReadClient(client_t *con)   /* I - Client to read from */
 
         if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
        {
-          CloseClient(con);
-         return (0);
+         LogMessage(L_DEBUG2, "ReadClient: httpGets returned EOF...");
+          return (CloseClient(con));
        }
 
        /*
@@ -886,7 +940,8 @@ ReadClient(client_t *con)   /* I - Client to read from */
        con->http.data_remaining = 0;
        con->operation           = HTTP_WAITING;
        con->bytes               = 0;
-       con->file                = 0;
+       con->file                = -1;
+       con->file_ready          = 0;
        con->pipe_pid            = 0;
        con->username[0]         = '\0';
        con->password[0]         = '\0';
@@ -908,20 +963,20 @@ ReadClient(client_t *con) /* I - Client to read from */
         switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
        {
          case 1 :
-             LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
+             LogMessage(L_ERROR, "Bad request line \"%s\" from %s!", line,
+                        con->http.hostname);
              SendError(con, HTTP_BAD_REQUEST);
-             ShutdownClient(con);
-             return (1);
+             return (CloseClient(con));
          case 2 :
              con->http.version = HTTP_0_9;
              break;
          case 3 :
              if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
              {
-               LogMessage(L_ERROR, "Bad request line \"%s\"!", line);
+               LogMessage(L_ERROR, "Bad request line \"%s\" from %s!", line,
+                          con->http.hostname);
                SendError(con, HTTP_BAD_REQUEST);
-               ShutdownClient(con);
-               return (1);
+               return (CloseClient(con));
              }
 
              if (major < 2)
@@ -935,8 +990,7 @@ ReadClient(client_t *con)   /* I - Client to read from */
              else
              {
                SendError(con, HTTP_NOT_SUPPORTED);
-               ShutdownClient(con);
-               return (1);
+               return (CloseClient(con));
              }
              break;
        }
@@ -975,8 +1029,7 @@ ReadClient(client_t *con)  /* I - Client to read from */
 
            LogMessage(L_ERROR, "Bad URI \"%s\" in request!", con->uri);
            SendError(con, HTTP_METHOD_NOT_ALLOWED);
-           ShutdownClient(con);
-           return (1);
+           return (CloseClient(con));
          }
 
          /*
@@ -1009,14 +1062,13 @@ ReadClient(client_t *con)       /* I - Client to read from */
        {
          LogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
          SendError(con, HTTP_BAD_REQUEST);
-         ShutdownClient(con);
-         return (1);
+         return (CloseClient(con));
        }
 
         con->start     = time(NULL);
         con->operation = con->http.state;
 
-        LogMessage(L_DEBUG, "ReadClient() %d %s %s HTTP/%d.%d", con->http.fd,
+        LogMessage(L_DEBUG, "ReadClient: %d %s %s HTTP/%d.%d", con->http.fd,
                   operation, con->uri,
                   con->http.version / 100, con->http.version % 100);
 
@@ -1038,8 +1090,7 @@ ReadClient(client_t *con) /* I - Client to read from */
        if (status != HTTP_OK && status != HTTP_CONTINUE)
        {
          SendError(con, HTTP_BAD_REQUEST);
-         ShutdownClient(con);
-         return (1);
+         return (CloseClient(con));
        }
        break;
 
@@ -1054,9 +1105,39 @@ ReadClient(client_t *con)        /* I - Client to read from */
   if (status == HTTP_OK)
   {
     if (con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE][0])
-      con->language = cupsLangGet(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
+    {
+     /*
+      * Figure out the locale from the Accept-Language and Content-Type
+      * fields...
+      */
+
+      if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ',')) != NULL)
+        *ptr = '\0';
+
+      if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ';')) != NULL)
+        *ptr = '\0';
+
+      if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "charset=")) != NULL)
+      {
+       /*
+        * Combine language and charset, and trim any extra params in the
+       * content-type.
+       */
+
+        snprintf(locale, sizeof(locale), "%s.%s",
+                con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ptr + 8);
+
+       if ((ptr = strchr(locale, ',')) != NULL)
+         *ptr = '\0';
+      }
+      else
+        snprintf(locale, sizeof(locale), "%s.%s",
+                con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], DefaultCharset);
+        
+      con->language = cupsLangGet(locale);
+    }
     else
-      con->language = cupsLangGet(DefaultLanguage);
+      con->language = cupsLangGet(DefaultLocale);
 
     decode_auth(con);
 
@@ -1072,10 +1153,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
       */
 
       if (!SendError(con, HTTP_BAD_REQUEST))
-      {
-       CloseClient(con);
-       return (0);
-      }
+       return (CloseClient(con));
     }
     else if (con->operation == HTTP_OPTIONS)
     {
@@ -1087,10 +1165,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
           best->type != AUTH_NONE)
       {
        if (!SendHeader(con, HTTP_UNAUTHORIZED, NULL))
-       {
-         CloseClient(con);
-         return (0);
-       }
+         return (CloseClient(con));
       }
 
       if (strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") == 0 &&
@@ -1102,10 +1177,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
        */
 
        if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
-       {
-         CloseClient(con);
-         return (0);
-       }
+         return (CloseClient(con));
 
        httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
        httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
@@ -1115,10 +1187,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
         EncryptClient(con);
 #else
        if (!SendError(con, HTTP_NOT_IMPLEMENTED))
-       {
-         CloseClient(con);
-          return (0);
-       }
+         return (CloseClient(con));
 #endif /* HAVE_SSL */
       }
 
@@ -1128,26 +1197,20 @@ ReadClient(client_t *con)       /* I - Client to read from */
       }
 
       if (!SendHeader(con, HTTP_OK, NULL))
-      {
-       CloseClient(con);
-       return (0);
-      }
+       return (CloseClient(con));
 
       httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
       httpPrintf(HTTP(con), "Content-Length: 0\r\n");
       httpPrintf(HTTP(con), "\r\n");
     }
-    else if (strstr(con->uri, "..") != NULL)
+    else if (!is_path_absolute(con->uri))
     {
      /*
       * Protect against malicious users!
       */
 
       if (!SendError(con, HTTP_FORBIDDEN))
-      {
-       CloseClient(con);
-        return (0);
-      }
+       return (CloseClient(con));
     }
     else
     {
@@ -1160,10 +1223,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
        */
 
        if (!SendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
-       {
-         CloseClient(con);
-         return (0);
-       }
+         return (CloseClient(con));
 
        httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
        httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
@@ -1171,24 +1231,18 @@ ReadClient(client_t *con)       /* I - Client to read from */
        httpPrintf(HTTP(con), "\r\n");
 
         EncryptClient(con);
-
-       status = IsAuthorized(con);
 #else
        if (!SendError(con, HTTP_NOT_IMPLEMENTED))
-       {
-         CloseClient(con);
-          return (0);
-       }
+         return (CloseClient(con));
 #endif /* HAVE_SSL */
       }
 
-      if (status != HTTP_OK)
+      if ((status = IsAuthorized(con)) != HTTP_OK)
       {
         LogMessage(L_DEBUG2, "ReadClient: Unauthorized request for %s...\n",
                   con->uri);
        SendError(con, status);
-        ShutdownClient(con);
-       return (1);
+       return (CloseClient(con));
       }
 
       if (con->http.expect)
@@ -1214,10 +1268,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
              else
              {
                if (!SendError(con, HTTP_NOT_FOUND))
-               {
-                 CloseClient(con);
-                 return (0);
-               }
+                 return (CloseClient(con));
 
                break;
              }
@@ -1236,7 +1287,11 @@ ReadClient(client_t *con)        /* I - Client to read from */
               if (strncmp(con->uri, "/admin", 6) == 0)
              {
                SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
-               SetString(&con->options, con->uri + 6);
+
+               if ((ptr = strchr(con->uri + 6, '?')) != NULL)
+                 SetStringf(&con->options, "admin%s", ptr);
+               else
+                 SetString(&con->options, "admin");
              }
               else if (strncmp(con->uri, "/printers", 9) == 0)
              {
@@ -1251,19 +1306,16 @@ ReadClient(client_t *con)       /* I - Client to read from */
              else
              {
                SetStringf(&con->command, "%s/cgi-bin/jobs.cgi", ServerBin);
-               SetString(&con->options, con->uri + 5);
+                SetString(&con->options, con->uri + 5);
              }
 
-             if (con->options[0] == '/')
-               strcpy(con->options, con->options + 1);
+              if (con->options[0] == '/')
+               cups_strcpy(con->options, con->options + 1);
 
               if (!SendCommand(con, con->command, con->options))
              {
                if (!SendError(con, HTTP_NOT_FOUND))
-               {
-                 CloseClient(con);
-                 return (0);
-               }
+                 return (CloseClient(con));
               }
              else
                LogRequest(con, HTTP_OK);
@@ -1281,10 +1333,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
              */
 
              if (!SendError(con, HTTP_FORBIDDEN))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              break;
            }
@@ -1298,10 +1347,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
                                       sizeof(buf))) == NULL)
              {
                if (!SendError(con, HTTP_NOT_FOUND))
-               {
-                 CloseClient(con);
-                 return (0);
-               }
+                 return (CloseClient(con));
 
                break;
              }
@@ -1310,13 +1356,15 @@ ReadClient(client_t *con)       /* I - Client to read from */
 
               if (IsCGI(con, filename, &filestats, type))
              {
+              /*
+               * Note: con->command and con->options were set by
+               * IsCGI()...
+               */
+
                if (!SendCommand(con, con->command, con->options))
                {
                  if (!SendError(con, HTTP_NOT_FOUND))
-                 {
-                   CloseClient(con);
-                   return (0);
-                 }
+                   return (CloseClient(con));
                }
                else
                  LogRequest(con, HTTP_OK);
@@ -1329,10 +1377,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
              if (!check_if_modified(con, &filestats))
               {
                if (!SendError(con, HTTP_NOT_MODIFIED))
-               {
-                 CloseClient(con);
-                 return (0);
-               }
+                 return (CloseClient(con));
              }
              else
               {
@@ -1342,10 +1387,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
                  snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
 
                if (!SendFile(con, HTTP_OK, filename, line, &filestats))
-               {
-                 CloseClient(con);
-                 return (0);
-               }
+                 return (CloseClient(con));
              }
            }
             break;
@@ -1368,10 +1410,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
              */
 
               if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              break;
             }
@@ -1382,10 +1421,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
              */
 
               if (!SendError(con, HTTP_BAD_REQUEST))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              break;
            }
@@ -1410,7 +1446,11 @@ ReadClient(client_t *con)        /* I - Client to read from */
               if (strncmp(con->uri, "/admin", 6) == 0)
              {
                SetStringf(&con->command, "%s/cgi-bin/admin.cgi", ServerBin);
-               SetString(&con->options, con->uri + 6);
+
+               if ((ptr = strchr(con->uri + 6, '?')) != NULL)
+                 SetStringf(&con->options, "admin%s", ptr);
+               else
+                 SetString(&con->options, "admin");
              }
               else if (strncmp(con->uri, "/printers", 9) == 0)
              {
@@ -1429,9 +1469,9 @@ ReadClient(client_t *con) /* I - Client to read from */
              }
 
              if (con->options[0] == '/')
-               strcpy(con->options, con->options + 1);
+               cups_strcpy(con->options, con->options + 1);
 
-              LogMessage(L_DEBUG2, "ReadClient() %d command=\"%s\", options = \"%s\"",
+              LogMessage(L_DEBUG2, "ReadClient: %d command=\"%s\", options = \"%s\"",
                         con->http.fd, con->command, con->options);
 
              if (con->http.version <= HTTP_1_0)
@@ -1447,10 +1487,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
                                       sizeof(buf))) == NULL)
              {
                if (!SendError(con, HTTP_NOT_FOUND))
-               {
-                 CloseClient(con);
-                 return (0);
-               }
+                 return (CloseClient(con));
 
                break;
              }
@@ -1464,10 +1501,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
                */
 
                if (!SendError(con, HTTP_UNAUTHORIZED))
-               {
-                 CloseClient(con);
-                 return (0);
-               }
+                 return (CloseClient(con));
              }
            }
            break;
@@ -1487,10 +1521,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
              */
 
              if (!SendError(con, HTTP_FORBIDDEN))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              break;
            }
@@ -1512,13 +1543,21 @@ ReadClient(client_t *con)       /* I - Client to read from */
              */
 
               if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              break;
             }
+           else if (atoi(con->http.fields[HTTP_FIELD_CONTENT_LENGTH]) < 0)
+           {
+            /*
+             * Negative content lengths are invalid!
+             */
+
+              if (!SendError(con, HTTP_BAD_REQUEST))
+               return (CloseClient(con));
+
+             break;
+           }
 
            /*
            * Open a temporary file to hold the request...
@@ -1526,27 +1565,25 @@ ReadClient(client_t *con)       /* I - Client to read from */
 
             SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
            con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
-           fchmod(con->file, 0640);
-           fchown(con->file, User, Group);
 
-            LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
+            LogMessage(L_DEBUG2, "ReadClient: %d REQUEST %s=%d", con->http.fd,
                       con->filename, con->file);
 
            if (con->file < 0)
            {
              if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
            }
+
+           fchmod(con->file, 0640);
+           fchown(con->file, RunUser, Group);
+           fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
            break;
 
        case HTTP_DELETE :
        case HTTP_TRACE :
             SendError(con, HTTP_NOT_IMPLEMENTED);
-            ShutdownClient(con);
-           return (1);
+           return (CloseClient(con));
 
        case HTTP_HEAD :
             if (strncmp(con->uri, "/printers/", 10) == 0 &&
@@ -1564,10 +1601,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
              else
              {
                if (!SendError(con, HTTP_NOT_FOUND))
-               {
-                 CloseClient(con);
-                 return (0);
-               }
+                 return (CloseClient(con));
 
                break;
              }
@@ -1584,16 +1618,10 @@ ReadClient(client_t *con)       /* I - Client to read from */
              */
 
               if (!SendHeader(con, HTTP_OK, "text/html"))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              if (httpPrintf(HTTP(con), "\r\n") < 0)
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
               LogRequest(con, HTTP_OK);
            }
@@ -1607,10 +1635,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
              */
 
              if (!SendError(con, HTTP_FORBIDDEN))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              break;
            }
@@ -1618,20 +1643,14 @@ ReadClient(client_t *con)       /* I - Client to read from */
                                          sizeof(buf))) == NULL)
            {
              if (!SendHeader(con, HTTP_NOT_FOUND, "text/html"))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
               LogRequest(con, HTTP_NOT_FOUND);
            }
            else if (!check_if_modified(con, &filestats))
             {
               if (!SendError(con, HTTP_NOT_MODIFIED))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
               LogRequest(con, HTTP_NOT_MODIFIED);
            }
@@ -1648,33 +1667,21 @@ ReadClient(client_t *con)       /* I - Client to read from */
                snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
 
               if (!SendHeader(con, HTTP_OK, line))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
                             httpGetDateString(filestats.st_mtime)) < 0)
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
                             (unsigned long)filestats.st_size) < 0)
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
               LogRequest(con, HTTP_OK);
            }
 
             if (httpPrintf(HTTP(con), "\r\n") < 0)
-           {
-             CloseClient(con);
-             return (0);
-           }
+             return (CloseClient(con));
 
             con->http.state = HTTP_WAITING;
             break;
@@ -1692,21 +1699,18 @@ ReadClient(client_t *con)       /* I - Client to read from */
   switch (con->http.state)
   {
     case HTTP_PUT_RECV :
-        LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
+        LogMessage(L_DEBUG2, "ReadClient: %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
                   con->http.fd,
                   con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
                   con->http.data_remaining, con->file);
 
         if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
-       {
-         CloseClient(con);
-         return (0);
-       }
+         return (CloseClient(con));
        else if (bytes > 0)
        {
          con->bytes += bytes;
 
-          LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
+          LogMessage(L_DEBUG2, "ReadClient: %d writing %d bytes to %d",
                     con->http.fd, bytes, con->file);
 
           if (write(con->file, line, bytes) < bytes)
@@ -1718,15 +1722,12 @@ ReadClient(client_t *con)       /* I - Client to read from */
                       con->file);
 
            close(con->file);
-           con->file = 0;
+           con->file = -1;
            unlink(con->filename);
            ClearString(&con->filename);
 
             if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
-           {
-             CloseClient(con);
-             return (0);
-           }
+             return (CloseClient(con));
          }
        }
 
@@ -1738,11 +1739,11 @@ ReadClient(client_t *con)       /* I - Client to read from */
 
          fstat(con->file, &filestats);
 
-          LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
+          LogMessage(L_DEBUG2, "ReadClient: %d Closing data file %d, size = %d.",
                      con->http.fd, con->file, (int)filestats.st_size);
 
          close(con->file);
-         con->file = 0;
+         con->file = -1;
 
           if (filestats.st_size > MaxRequestSize &&
              MaxRequestSize > 0)
@@ -1751,16 +1752,13 @@ ReadClient(client_t *con)       /* I - Client to read from */
            * Request is too big; remove it and send an error...
            */
 
-            LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
+            LogMessage(L_DEBUG2, "ReadClient: %d Removing temp file %s",
                       con->http.fd, con->filename);
            unlink(con->filename);
            ClearString(&con->filename);
 
             if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
-           {
-             CloseClient(con);
-             return (0);
-           }
+             return (CloseClient(con));
          }
 
          /*
@@ -1774,15 +1772,12 @@ ReadClient(client_t *con)       /* I - Client to read from */
          */
 
           if (!SendError(con, status))
-         {
-           CloseClient(con);
-           return (0);
-         }
+           return (CloseClient(con));
        }
         break;
 
     case HTTP_POST_RECV :
-        LogMessage(L_DEBUG2, "ReadClient() %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
+        LogMessage(L_DEBUG2, "ReadClient: %d con->data_encoding = %s, con->data_remaining = %d, con->file = %d",
                   con->http.fd,
                   con->http.data_encoding == HTTP_ENCODE_CHUNKED ? "chunked" : "length",
                   con->http.data_remaining, con->file);
@@ -1795,17 +1790,11 @@ ReadClient(client_t *con)       /* I - Client to read from */
 
          if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
          {
-            LogMessage(L_ERROR, "ReadClient() %d IPP Read Error!",
+            LogMessage(L_ERROR, "ReadClient: %d IPP Read Error!",
                       con->http.fd);
 
-           if (!SendError(con, HTTP_BAD_REQUEST))
-           {
-             CloseClient(con);
-             return (0);
-           }
-
-           ShutdownClient(con);
-           return (1);
+           SendError(con, HTTP_BAD_REQUEST);
+           return (CloseClient(con));
          }
          else if (ipp_state != IPP_DATA)
            break;
@@ -1813,7 +1802,7 @@ ReadClient(client_t *con) /* I - Client to read from */
            con->bytes += ippLength(con->request);
        }
 
-        if (con->file == 0 && con->http.state != HTTP_POST_SEND)
+        if (con->file < 0 && con->http.state != HTTP_POST_SEND)
        {
          /*
          * Create a file as needed for the request data...
@@ -1821,34 +1810,30 @@ ReadClient(client_t *con)       /* I - Client to read from */
 
           SetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
          con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
-         fchmod(con->file, 0640);
-         fchown(con->file, User, Group);
 
-          LogMessage(L_DEBUG2, "ReadClient() %d REQUEST %s=%d", con->http.fd,
+          LogMessage(L_DEBUG2, "ReadClient: %d REQUEST %s=%d", con->http.fd,
                     con->filename, con->file);
 
          if (con->file < 0)
          {
            if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
-           {
-             CloseClient(con);
-             return (0);
-           }
+             return (CloseClient(con));
          }
+
+         fchmod(con->file, 0640);
+         fchown(con->file, RunUser, Group);
+          fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
        }
 
        if (con->http.state != HTTP_POST_SEND)
        {
           if ((bytes = httpRead(HTTP(con), line, sizeof(line))) < 0)
-         {
-           CloseClient(con);
-           return (0);
-         }
+           return (CloseClient(con));
          else if (bytes > 0)
          {
            con->bytes += bytes;
 
-            LogMessage(L_DEBUG2, "ReadClient() %d writing %d bytes to %d",
+            LogMessage(L_DEBUG2, "ReadClient: %d writing %d bytes to %d",
                       con->http.fd, bytes, con->file);
 
             if (write(con->file, line, bytes) < bytes)
@@ -1860,39 +1845,31 @@ ReadClient(client_t *con)       /* I - Client to read from */
                         con->file);
 
              close(con->file);
-             con->file = 0;
+             con->file = -1;
              unlink(con->filename);
              ClearString(&con->filename);
 
               if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
            }
          }
          else if (con->http.state == HTTP_POST_RECV)
-         {
-            return (0);
-         }
+            return (1); /* ??? */
          else if (con->http.state != HTTP_POST_SEND)
-         {
-           CloseClient(con);
-           return (0);
-         }
+           return (CloseClient(con));
        }
 
        if (con->http.state == HTTP_POST_SEND)
        {
-         if (con->file)
+         if (con->file >= 0)
          {
            fstat(con->file, &filestats);
 
-            LogMessage(L_DEBUG2, "ReadClient() %d Closing data file %d, size = %d.",
+            LogMessage(L_DEBUG2, "ReadClient: %d Closing data file %d, size = %d.",
                        con->http.fd, con->file, (int)filestats.st_size);
 
            close(con->file);
-           con->file = 0;
+           con->file = -1;
 
             if (filestats.st_size > MaxRequestSize &&
                MaxRequestSize > 0)
@@ -1901,7 +1878,7 @@ ReadClient(client_t *con) /* I - Client to read from */
              * Request is too big; remove it and send an error...
              */
 
-              LogMessage(L_DEBUG2, "ReadClient() %d Removing temp file %s",
+              LogMessage(L_DEBUG2, "ReadClient: %d Removing temp file %s",
                         con->http.fd, con->filename);
              unlink(con->filename);
              ClearString(&con->filename);
@@ -1917,10 +1894,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
               }
 
               if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
            }
 
            if (con->command)
@@ -1928,10 +1902,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
              if (!SendCommand(con, con->command, con->options))
              {
                if (!SendError(con, HTTP_NOT_FOUND))
-               {
-                 CloseClient(con);
-                 return (0);
-               }
+                 return (CloseClient(con));
               }
              else
                LogRequest(con, HTTP_OK);
@@ -1948,10 +1919,7 @@ ReadClient(client_t *con)        /* I - Client to read from */
   }
 
   if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
-  {
-    CloseClient(con);
-    return (0);
-  }
+    return (CloseClient(con));
   else
     return (1);
 }
@@ -1974,13 +1942,23 @@ SendCommand(client_t      *con,
   else
     fd = open("/dev/null", O_RDONLY);
 
+  if (fd < 0)
+  {
+    LogMessage(L_ERROR, "SendCommand: %d Unable to open \"%s\" for reading: %s",
+               con->http.fd, con->filename ? con->filename : "/dev/null",
+              strerror(errno));
+    return (0);
+  }
+
+  fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+
   con->pipe_pid = pipe_command(con, fd, &(con->file), command, options);
 
   close(fd);
 
   LogMessage(L_INFO, "Started \"%s\" (pid=%d)", command, con->pipe_pid);
 
-  LogMessage(L_DEBUG, "SendCommand() %d file=%d", con->http.fd, con->file);
+  LogMessage(L_DEBUG, "SendCommand: %d file=%d", con->http.fd, con->file);
 
   if (con->pipe_pid == 0)
     return (0);
@@ -2005,6 +1983,7 @@ SendCommand(client_t      *con,
       return (0);
   }
 
+  con->file_ready = 0;
   con->got_fields = 0;
   con->field_col  = 0;
 
@@ -2027,10 +2006,10 @@ SendError(client_t      *con,   /* I - Connection */
   * Put the request in the access_log file...
   */
 
-  if (con->operation > HTTP_WAITING)
-    LogRequest(con, code);
+  LogRequest(con, code);
 
-  LogMessage(L_DEBUG, "SendError() %d code=%d", con->http.fd, code);
+  LogMessage(L_DEBUG, "SendError: %d code=%d (%s)", con->http.fd, code,
+             httpStatus(code));
 
  /*
   * To work around bugs in some proxies, don't use Keep-Alive for some
@@ -2109,7 +2088,7 @@ SendFile(client_t    *con,
 {
   con->file = open(filename, O_RDONLY);
 
-  LogMessage(L_DEBUG, "SendFile() %d file=%d", con->http.fd, con->file);
+  LogMessage(L_DEBUG, "SendFile: %d file=%d", con->http.fd, con->file);
 
   if (con->file < 0)
     return (0);
@@ -2154,8 +2133,9 @@ SendHeader(client_t    *con,      /* I - Client to send to */
     return (0);
   if (httpPrintf(HTTP(con), "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
     return (0);
-  if (httpPrintf(HTTP(con), "Server: CUPS/1.1\r\n") < 0)
-    return (0);
+  if (ServerHeader)
+    if (httpPrintf(HTTP(con), "Server: %s\r\n", ServerHeader) < 0)
+      return (0);
   if (con->http.keep_alive && con->http.version >= HTTP_1_0)
   {
     if (httpPrintf(HTTP(con), "Connection: Keep-Alive\r\n") < 0)
@@ -2182,7 +2162,7 @@ SendHeader(client_t    *con,      /* I - Client to send to */
     }
     else
     {
-      if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\" "
+      if (httpPrintf(HTTP(con), "WWW-Authenticate: Digest realm=\"CUPS\", "
                                 "nonce=\"%s\"\r\n", con->http.hostname) < 0)
        return (0);
     }
@@ -2206,36 +2186,6 @@ SendHeader(client_t    *con,     /* I - Client to send to */
 }
 
 
-/*
- * 'ShutdownClient()' - Shutdown the receiving end of a connection.
- */
-
-void
-ShutdownClient(client_t *con)          /* I - Client connection */
-{
- /*
-  * Shutdown the receiving end of the socket, since the client
-  * still needs to read the error message...
-  */
-
-  shutdown(con->http.fd, 0);
-  con->http.used = 0;
-
- /*
-  * Update the activity time so that we timeout after 30 seconds rather
-  * then the current Timeout setting (300 by default).  This prevents
-  * some DoS situations...
-  */
-
-  con->http.activity = time(NULL) - Timeout + 30;
-
-  LogMessage(L_DEBUG2, "ShutdownClient: Removing fd %d from InputSet...",
-             con->http.fd);
-
-  FD_CLR(con->http.fd, InputSet);
-}
-
-
 /*
  * 'UpdateCGI()' - Read status messages from CGI scripts and programs.
  */
@@ -2243,129 +2193,23 @@ ShutdownClient(client_t *con)          /* I - Client connection */
 void
 UpdateCGI(void)
 {
-  int          bytes;          /* Number of bytes read */
-  char         *lineptr,       /* Pointer to end of line in buffer */
-               *message;       /* Pointer to message text */
-  int          loglevel;       /* Log level for message */
-  static int   bufused = 0;    /* Number of bytes used in buffer */
-  static char  buffer[1024];   /* Status buffer */
+  char         *ptr,                   /* Pointer to end of line in buffer */
+               message[1024];          /* Pointer to message text */
+  int          loglevel;               /* Log level for message */
 
 
-  if ((bytes = read(CGIPipes[0], buffer + bufused,
-                    sizeof(buffer) - bufused - 1)) > 0)
-  {
-    bufused += bytes;
-    buffer[bufused] = '\0';
-    lineptr = strchr(buffer, '\n');
-  }
-  else if (bytes < 0 && errno == EINTR)
-    return;
-  else
-  {
-    lineptr    = buffer + bufused;
-    lineptr[1] = 0;
-  }
-
-  if (bytes == 0 && bufused == 0)
-    lineptr = NULL;
-
-  while (lineptr != NULL)
-  {
-   /*
-    * Terminate each line and process it...
-    */
-
-    *lineptr++ = '\0';
-
-   /*
-    * Figure out the logging level...
-    */
-
-    if (strncmp(buffer, "EMERG:", 6) == 0)
-    {
-      loglevel = L_EMERG;
-      message  = buffer + 6;
-    }
-    else if (strncmp(buffer, "ALERT:", 6) == 0)
-    {
-      loglevel = L_ALERT;
-      message  = buffer + 6;
-    }
-    else if (strncmp(buffer, "CRIT:", 5) == 0)
-    {
-      loglevel = L_CRIT;
-      message  = buffer + 5;
-    }
-    else if (strncmp(buffer, "ERROR:", 6) == 0)
-    {
-      loglevel = L_ERROR;
-      message  = buffer + 6;
-    }
-    else if (strncmp(buffer, "WARNING:", 8) == 0)
-    {
-      loglevel = L_WARN;
-      message  = buffer + 8;
-    }
-    else if (strncmp(buffer, "NOTICE:", 6) == 0)
-    {
-      loglevel = L_NOTICE;
-      message  = buffer + 6;
-    }
-    else if (strncmp(buffer, "INFO:", 5) == 0)
-    {
-      loglevel = L_INFO;
-      message  = buffer + 5;
-    }
-    else if (strncmp(buffer, "DEBUG:", 6) == 0)
-    {
-      loglevel = L_DEBUG;
-      message  = buffer + 6;
-    }
-    else if (strncmp(buffer, "DEBUG2:", 7) == 0)
-    {
-      loglevel = L_DEBUG2;
-      message  = buffer + 7;
-    }
-    else if (strncmp(buffer, "PAGE:", 5) == 0)
-    {
-      loglevel = L_PAGE;
-      message  = buffer + 5;
-    }
-    else
-    {
-      loglevel = L_DEBUG;
-      message  = buffer;
-    }
-
-   /*
-    * Skip leading whitespace in the message...
-    */
-
-    while (isspace(*message))
-      message ++;
-
-    LogMessage(loglevel, "[CGI] %s", message);
-
-   /*
-    * Copy over the buffer data we've used up...
-    */
-
-    strcpy(buffer, lineptr);
-    bufused -= lineptr - buffer;
-
-    if (bufused < 0)
-      bufused = 0;
-
-    lineptr = strchr(buffer, '\n');
-  }
+  while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
+                                   message, sizeof(message))) != NULL)
+    if (!strchr(CGIStatusBuffer->buffer, '\n'))
+      break;
 
-  if (bytes <= 0)
+  if (ptr == NULL)
   {
    /*
     * Fatal error on pipe - should never happen!
     */
 
-    LogMessage(L_ERROR, "UpdateCGI: error reading from CGI error pipe - %s",
+    LogMessage(L_CRIT, "UpdateCGI: error reading from CGI error pipe - %s",
                strerror(errno));
   }
 }
@@ -2384,6 +2228,11 @@ WriteClient(client_t *con)               /* I - Client connection */
   ipp_state_t  ipp_state;              /* IPP state value */
 
 
+#ifdef DEBUG
+  LogMessage(L_DEBUG2, "WriteClient(con=%p) %d response=%p, file=%d pipe_pid=%d",
+             con, con->http.fd, con->response, con->file, con->pipe_pid);
+#endif /* DEBUG */
+
   if (con->http.state != HTTP_GET_SEND &&
       con->http.state != HTTP_POST_SEND)
     return (1);
@@ -2395,6 +2244,11 @@ WriteClient(client_t *con)               /* I - Client connection */
   }
   else if ((bytes = read(con->file, buf, HTTP_MAX_BUFFER)) > 0)
   {
+#ifdef DEBUG
+    LogMessage(L_DEBUG2, "WriteClient: Read %d bytes from file %d...",
+               bytes, con->file);
+#endif /* DEBUG */
+
     if (con->pipe_pid && !con->got_fields)
     {
      /*
@@ -2415,14 +2269,14 @@ WriteClient(client_t *con)              /* I - Client connection */
          *bufptr++ = '\0';
 
          httpPrintf(HTTP(con), "%s\r\n", buf);
-         LogMessage(L_DEBUG2, "WriteClient() %d %s", con->http.fd, buf);
+         LogMessage(L_DEBUG2, "WriteClient: %d %s", con->http.fd, buf);
 
          /*
          * Update buffer...
          */
 
          bytes -= (bufptr - buf);
-         memcpy(buf, bufptr, bytes + 1);
+         memmove(buf, bufptr, bytes + 1);
          bufptr = buf - 1;
 
          /*
@@ -2479,31 +2333,34 @@ WriteClient(client_t *con)              /* I - Client connection */
 
     con->http.state = HTTP_WAITING;
 
-    LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from OutputSet...",
+    LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from OutputSet...",
                con->http.fd);
 
     FD_CLR(con->http.fd, OutputSet);
 
-    if (con->file)
+    if (con->file >= 0)
     {
-      LogMessage(L_DEBUG2, "WriteClient() Removing fd %d from InputSet...",
-                 con->file);
-      FD_CLR(con->file, InputSet);
+      if (FD_ISSET(con->file, InputSet))
+      {
+       LogMessage(L_DEBUG2, "WriteClient: Removing fd %d from InputSet...",
+                   con->file);
+       FD_CLR(con->file, InputSet);
+      }
 
       if (con->pipe_pid)
        kill(con->pipe_pid, SIGTERM);
 
-      LogMessage(L_DEBUG2, "WriteClient() %d Closing data file %d.",
+      LogMessage(L_DEBUG2, "WriteClient: %d Closing data file %d.",
                  con->http.fd, con->file);
 
       close(con->file);
-      con->file     = 0;
+      con->file     = -1;
       con->pipe_pid = 0;
     }
 
     if (con->filename)
     {
-      LogMessage(L_DEBUG2, "WriteClient() %d Removing temp file %s",
+      LogMessage(L_DEBUG2, "WriteClient: %d Removing temp file %s",
                  con->http.fd, con->filename);
       unlink(con->filename);
       ClearString(&con->filename);
@@ -2530,9 +2387,19 @@ WriteClient(client_t *con)               /* I - Client connection */
       return (0);
     }
   }
+  else
+  {
+    con->file_ready = 0;
+
+    if (con->pipe_pid && !FD_ISSET(con->file, InputSet))
+    {
+      LogMessage(L_DEBUG2, "WriteClient: Adding fd %d to InputSet...", con->file);
+      FD_SET(con->file, InputSet);
+    }
+  }
 
   if (bytes >= 1024)
-    LogMessage(L_DEBUG2, "WriteClient() %d %d bytes", con->http.fd, bytes);
+    LogMessage(L_DEBUG2, "WriteClient: %d %d bytes", con->http.fd, bytes);
 
   con->http.activity = time(NULL);
 
@@ -2560,7 +2427,7 @@ check_if_modified(client_t    *con,               /* I - Client connection */
   if (*ptr == '\0')
     return (1);
 
-  LogMessage(L_DEBUG2, "check_if_modified() %d If-Modified-Since=\"%s\"",
+  LogMessage(L_DEBUG2, "check_if_modified: %d If-Modified-Since=\"%s\"",
              con->http.fd, ptr);
 
   while (*ptr != '\0')
@@ -2584,7 +2451,7 @@ check_if_modified(client_t    *con,               /* I - Client connection */
     }
   }
 
-  LogMessage(L_DEBUG2, "check_if_modified() %d sizes=%d,%d dates=%d,%d",
+  LogMessage(L_DEBUG2, "check_if_modified: %d sizes=%d,%d dates=%d,%d",
              con->http.fd, size, (int)filestats->st_size, (int)date,
             (int)filestats->st_mtime);
 
@@ -2629,7 +2496,7 @@ decode_auth(client_t *con)                /* I - Client to decode to */
 
     if ((s = strchr(value, ':')) == NULL)
     {
-      LogMessage(L_DEBUG, "decode_auth() %d no colon in auth string \"%s\"",
+      LogMessage(L_DEBUG, "decode_auth: %d no colon in auth string \"%s\"",
                 con->http.fd, value);
       return;
     }
@@ -2654,16 +2521,16 @@ decode_auth(client_t *con)              /* I - Client to decode to */
     * Get the username and password from the Digest attributes...
     */
 
-    if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "username",
+    if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "username",
                         value))
       strlcpy(con->username, value, sizeof(con->username));
 
-    if (httpGetSubField(&(con->http), HTTP_FIELD_WWW_AUTHENTICATE, "response",
+    if (httpGetSubField(&(con->http), HTTP_FIELD_AUTHORIZATION, "response",
                         value))
-      strlcpy(con->password, value, sizeof(con->password) - 1);
+      strlcpy(con->password, value, sizeof(con->password));
   }
 
-  LogMessage(L_DEBUG2, "decode_auth() %d username=\"%s\"",
+  LogMessage(L_DEBUG2, "decode_auth: %d username=\"%s\"",
              con->http.fd, con->username);
 }
 
@@ -2672,14 +2539,15 @@ decode_auth(client_t *con)              /* I - Client to decode to */
  * 'get_file()' - Get a filename and state info.
  */
 
-static char *                  /* O  - Real filename */
-get_file(client_t    *con,     /* I  - Client connection */
-         struct stat *filestats,/* O  - File information */
-         char        *filename,        /* IO - Filename buffer */
-         int         len)      /* I  - Buffer length */
+static char *                          /* O  - Real filename */
+get_file(client_t    *con,             /* I  - Client connection */
+         struct stat *filestats,       /* O  - File information */
+         char        *filename,                /* IO - Filename buffer */
+         int         len)              /* I  - Buffer length */
 {
-  int          status;         /* Status of filesystem calls */
-  char         *params;        /* Pointer to parameters in URI */
+  int          status;                 /* Status of filesystem calls */
+  char         *ptr;                   /* Pointer info filename */
+  int          plen;                   /* Remaining length after pointer */
 
 
  /*
@@ -2696,8 +2564,8 @@ get_file(client_t    *con,        /* I  - Client connection */
   else
     snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
 
-  if ((params = strchr(filename, '?')) != NULL)
-    *params = '\0';
+  if ((ptr = strchr(filename, '?')) != NULL)
+    *ptr = '\0';
 
  /*
   * Grab the status for this language; if there isn't a language-specific file
@@ -2715,6 +2583,9 @@ get_file(client_t    *con,        /* I  - Client connection */
     {
       snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
 
+      if ((ptr = strchr(filename, '?')) != NULL)
+       *ptr = '\0';
+
       status = stat(filename, filestats);
     }
   }
@@ -2725,15 +2596,55 @@ get_file(client_t    *con,      /* I  - Client connection */
 
   if (!status && S_ISDIR(filestats->st_mode))
   {
-    if (filename[strlen(filename) - 1] == '/')
-      strlcat(filename, "index.html", len);
-    else
-      strlcat(filename, "/index.html", len);
+    if (filename[strlen(filename) - 1] != '/')
+      strlcat(filename, "/", len);
+
+    ptr  = filename + strlen(filename);
+    plen = len - (ptr - filename);
 
+    strlcpy(ptr, "index.html", plen);
     status = stat(filename, filestats);
+
+#ifdef HAVE_JAVA
+    if (status)
+    {
+      strlcpy(ptr, "index.class", plen);
+      status = stat(filename, filestats);
+    }
+#endif /* HAVE_JAVA */
+
+#ifdef HAVE_PERL
+    if (status)
+    {
+      strlcpy(ptr, "index.pl", plen);
+      status = stat(filename, filestats);
+    }
+#endif /* HAVE_PERL */
+
+#ifdef HAVE_PHP
+    if (status)
+    {
+      strlcpy(ptr, "index.php", plen);
+      status = stat(filename, filestats);
+    }
+#endif /* HAVE_PHP */
+
+#ifdef HAVE_PYTHON
+    if (status)
+    {
+      strlcpy(ptr, "index.pyc", plen);
+      status = stat(filename, filestats);
+    }
+
+    if (status)
+    {
+      strlcpy(ptr, "index.py", plen);
+      status = stat(filename, filestats);
+    }
+#endif /* HAVE_PYTHON */
   }
 
-  LogMessage(L_DEBUG2, "get_file() %d filename=%s size=%d",
+  LogMessage(L_DEBUG2, "get_file: %d filename=%s size=%d",
              con->http.fd, filename, status ? -1 : (int)filestats->st_size);
 
   if (status)
@@ -2891,6 +2802,8 @@ install_conf_file(client_t *con)  /* I - Connection */
   else
     NeedReload = RELOAD_ALL;
 
+  ReloadTime = time(NULL);
+
  /*
   * Return that the file was created successfully...
   */
@@ -2899,6 +2812,41 @@ install_conf_file(client_t *con) /* I - Connection */
 }
 
 
+/*
+ * 'is_path_absolute()' - Is a path absolute and free of relative elements (i.e. "..").
+ */
+
+static int                             /* O - 0 if relative, 1 if absolute */
+is_path_absolute(const char *path)     /* I - Input path */
+{
+ /*
+  * Check for a leading slash...
+  */
+
+  if (path[0] != '/')
+    return (0);
+
+ /*
+  * Check for "/.." in the path...
+  */
+
+  while ((path = strstr(path, "/..")) != NULL)
+  {
+    if (!path[3] || path[3] == '/')
+      return (0);
+
+    path ++;
+  }
+
+ /*
+  * If we haven't found any relative paths, return 1 indicating an
+  * absolute path...
+  */
+
+  return (1);
+}
+
+
 /*
  * 'pipe_command()' - Pipe the output of a command to the remote client.
  */
@@ -2914,7 +2862,6 @@ pipe_command(client_t *con,               /* I - Client connection */
   int          pid;                    /* Process ID */
   char         *commptr;               /* Command string pointer */
   char         *uriptr;                /* URI string pointer */
-  int          fd;                     /* Looping var */
   int          fds[2];                 /* Pipe FDs */
   int          argc;                   /* Number of arguments */
   int          envc;                   /* Number of environment variables */
@@ -2934,50 +2881,142 @@ pipe_command(client_t *con,            /* I - Client connection */
                dyld_library_path[1024],/* DYLD_LIBRARY_PATH environment variable */
                shlib_path[1024],       /* SHLIB_PATH environment variable */
                nlspath[1024],          /* NLSPATH environment variable */
-               query_string[10240],    /* QUERY_STRING env variable */
+               *query_string,          /* QUERY_STRING env variable */
                remote_addr[1024],      /* REMOTE_ADDR environment variable */
                remote_host[1024],      /* REMOTE_HOST environment variable */
                remote_user[1024],      /* REMOTE_USER environment variable */
                script_name[1024],      /* SCRIPT_NAME environment variable */
                server_name[1024],      /* SERVER_NAME environment variable */
                server_port[1024],      /* SERVER_PORT environment variable */
-               tmpdir[1024];           /* TMPDIR environment variable */
+               tmpdir[1024],           /* TMPDIR environment variable */
+               vg_args[1024],          /* VG_ARGS environment variable */
+               ld_assume_kernel[1024]; /* LD_ASSUME_KERNEL environment variable */
 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
   struct sigaction action;             /* POSIX signal handler */
 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
+  static const char * const locale_encodings[] =
+               {                       /* Locale charset names */
+                 "ASCII",      "ISO8859-1",    "ISO8859-2",    "ISO8859-3",
+                 "ISO8859-4",  "ISO8859-5",    "ISO8859-6",    "ISO8859-7",
+                 "ISO8859-8",  "ISO8859-9",    "ISO8859-10",   "UTF-8",
+                 "ISO8859-13", "ISO8859-14",   "ISO8859-15",   "CP874",
+                 "CP1250",     "CP1251",       "CP1252",       "CP1253",
+                 "CP1254",     "CP1255",       "CP1256",       "CP1257",
+                 "CP1258",     "KOI8R",        "KOI8U",        "ISO8859-11",
+                 "ISO8859-16", "",             "",             "",
+
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+
+                 "CP932",      "CP936",        "CP949",        "CP950",
+                 "CP1361",     "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+                 "",           "",             "",             "",
+
+                 "EUC-CN",     "EUC-JP",       "EUC-KR",       "EUC-TW"
+               };
+  static const char * const encryptions[] =
+               {
+                 "CUPS_ENCRYPTION=IfRequested",
+                 "CUPS_ENCRYPTION=Never",
+                 "CUPS_ENCRYPTION=Required",
+                 "CUPS_ENCRYPTION=Always"
+               };
 
 
  /*
-  * Copy the command string...
+  * Parse a copy of the options string, which is of the form:
+  *
+  *     name argument+argument+argument
+  *     name?argument+argument+argument
+  *     name param=value&param=value
+  *     name?param=value&param=value
+  *
+  * If the string contains an "=" character after the initial name,
+  * then we treat it as a HTTP GET form request and make a copy of
+  * the remaining string for the environment variable.
+  *
+  * The string is always parsed out as command-line arguments, to
+  * be consistent with Apache...
   */
 
-  strlcpy(argbuf, options, sizeof(argbuf));
+  LogMessage(L_DEBUG2, "pipe_command: command=\"%s\", options=\"%s\"",
+             command, options);
 
- /*
-  * Parse the string; arguments can be separated by + and are terminated
-  * by ?...
-  */
+  strlcpy(argbuf, options, sizeof(argbuf));
 
-  argv[0] = argbuf;
+  argv[0]      = argbuf;
+  query_string = NULL;
 
   for (commptr = argbuf, argc = 1; *commptr != '\0' && argc < 99; commptr ++)
-    if (*commptr == ' ' || *commptr == '+')
+  {
+   /*
+    * Break arguments whenever we see a + or space...
+    */
+
+    if (*commptr == ' ' || *commptr == '+' || (*commptr == '?' && argc == 1))
     {
+     /*
+      * Terminate the current string and skip trailing whitespace...
+      */
+
       *commptr++ = '\0';
 
       while (*commptr == ' ')
         commptr ++;
 
-      if (*commptr != '\0')
+     /*
+      * If we don't have a blank string, save it as another argument...
+      */
+
+      if (*commptr)
       {
         argv[argc] = commptr;
        argc ++;
       }
+      else
+        break;
+
+     /*
+      * If we see an "=" in the remaining string, make a copy of it since
+      * it will be query data...
+      */
+
+      if (argc == 2 && strchr(commptr, '=') && con->operation == HTTP_GET)
+       SetStringf(&query_string, "QUERY_STRING=%s", commptr);
+
+     /*
+      * Don't skip the first non-blank character...
+      */
 
       commptr --;
     }
-    else if (*commptr == '%')
+    else if (*commptr == '%' && isxdigit(commptr[1] & 255) &&
+             isxdigit(commptr[2] & 255))
     {
+     /*
+      * Convert the %xx notation to the individual character.
+      */
+
       if (commptr[1] >= '0' && commptr[1] <= '9')
         *commptr = (commptr[1] - '0') << 4;
       else
@@ -2988,10 +3027,16 @@ pipe_command(client_t *con,             /* I - Client connection */
       else
         *commptr |= tolower(commptr[2]) - 'a' + 10;
 
-      strcpy(commptr + 1, commptr + 3);
+      cups_strcpy(commptr + 1, commptr + 3);
+
+     /*
+      * Check for a %00 and break if that is the case...
+      */
+
+      if (!*commptr)
+        break;
     }
-    else if (*commptr == '?')
-      break;
+  }
 
   argv[argc] = NULL;
 
@@ -3002,8 +3047,12 @@ pipe_command(client_t *con,              /* I - Client connection */
   * Setup the environment variables as needed...
   */
 
-  snprintf(lang, sizeof(lang), "LANG=%s",
-           con->language ? con->language->language : "C");
+  if (con->language)
+    snprintf(lang, sizeof(lang), "LANG=%s.%s", con->language->language,
+             locale_encodings[con->language->encoding]);
+  else
+    strcpy(lang, "LANG=C");
+
   sprintf(ipp_port, "IPP_PORT=%d", LocalPort);
 #ifdef AF_INET6
   if (con->http.hostaddr.addr.sa_family == AF_INET6)
@@ -3039,6 +3088,7 @@ pipe_command(client_t *con,               /* I - Client connection */
   else
     envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
   envp[envc ++] = "REDIRECT_STATUS=1";
+  envp[envc ++] = "CUPS_SERVER=localhost";
   envp[envc ++] = ipp_port;
   envp[envc ++] = server_name;
   envp[envc ++] = server_port;
@@ -3051,6 +3101,19 @@ pipe_command(client_t *con,              /* I - Client connection */
   envp[envc ++] = cups_datadir;
   envp[envc ++] = cups_serverroot;
 
+  if (getenv("VG_ARGS") != NULL)
+  {
+    snprintf(vg_args, sizeof(vg_args), "VG_ARGS=%s", getenv("VG_ARGS"));
+    envp[envc ++] = vg_args;
+  }
+
+  if (getenv("LD_ASSUME_KERNEL") != NULL)
+  {
+    snprintf(ld_assume_kernel, sizeof(ld_assume_kernel), "LD_ASSUME_KERNEL=%s",
+             getenv("LD_ASSUME_KERNEL"));
+    envp[envc ++] = ld_assume_kernel;
+  }
+
   if (getenv("LD_LIBRARY_PATH") != NULL)
   {
     snprintf(ld_library_path, sizeof(ld_library_path), "LD_LIBRARY_PATH=%s",
@@ -3110,15 +3173,12 @@ pipe_command(client_t *con,             /* I - Client connection */
       LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
     envp[envc ++] = "REQUEST_METHOD=GET";
 
-    if (*commptr)
+    if (query_string)
     {
      /*
       * Add GET form variables after ?...
       */
 
-      *commptr++ = '\0';
-
-      snprintf(query_string, sizeof(query_string), "QUERY_STRING=%s", commptr);
       envp[envc ++] = query_string;
     }
   }
@@ -3138,27 +3198,32 @@ pipe_command(client_t *con,             /* I - Client connection */
   */
 
   if (con->http.encryption == HTTP_ENCRYPT_ALWAYS)
-  {
     envp[envc ++] = "HTTPS=ON";
-    envp[envc ++] = "CUPS_ENCRYPTION=Always";
-  }
+
+  envp[envc ++] = (char *)encryptions[LocalEncryption];
+
+ /*
+  * Terminate the environment array...
+  */
 
   envp[envc] = NULL;
 
   if (LogLevel == L_DEBUG2)
   {
     for (i = 0; i < argc; i ++)
-      LogMessage(L_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
+      LogMessage(L_DEBUG2, "pipe_command: argv[%d] = \"%s\"", i, argv[i]);
     for (i = 0; i < envc; i ++)
-      LogMessage(L_DEBUG2, "envp[%d] = \"%s\"", i, envp[i]);
+      LogMessage(L_DEBUG2, "pipe_command: envp[%d] = \"%s\"", i, envp[i]);
   }
 
  /*
   * Create a pipe for the output...
   */
 
-  if (pipe(fds))
+  if (cupsdOpenPipe(fds))
   {
+    ClearString(&query_string);
+
     LogMessage(L_ERROR, "Unable to create pipes for CGI %s - %s",
                argv[0], strerror(errno));
     return (0);
@@ -3180,7 +3245,7 @@ pipe_command(client_t *con,               /* I - Client connection */
     * Child comes here...  Close stdin if necessary and dup the pipe to stdout.
     */
 
-    if (getuid() == 0)
+    if (!RunUser)
     {
      /*
       * Running as root, so change to a non-priviledged user...
@@ -3189,7 +3254,7 @@ pipe_command(client_t *con,               /* I - Client connection */
       if (setgid(Group))
         exit(errno);
 
-      if (setgroups(0, NULL))
+      if (setgroups(1, &Group))
         exit(errno);
 
       if (setuid(User))
@@ -3201,7 +3266,7 @@ pipe_command(client_t *con,               /* I - Client connection */
       * Reset group membership to just the main one we belong to.
       */
 
-      setgroups(0, NULL);
+      setgroups(1, &Group);
     }
 
    /*
@@ -3222,13 +3287,6 @@ pipe_command(client_t *con,              /* I - Client connection */
     close(2);
     dup(CGIPipes[1]);
 
-   /*
-    * Close extra file descriptors...
-    */
-
-    for (fd = 3; fd < MaxFDs; fd ++)
-      close(fd);
-
    /*
     * Change umask to restrict permissions on created files...
     */
@@ -3274,8 +3332,7 @@ pipe_command(client_t *con,               /* I - Client connection */
     LogMessage(L_ERROR, "Unable to fork for CGI %s - %s", argv[0],
                strerror(errno));
 
-    close(fds[0]);
-    close(fds[1]);
+    cupsdClosePipe(fds);
     pid = 0;
   }
   else
@@ -3294,6 +3351,8 @@ pipe_command(client_t *con,               /* I - Client connection */
 
   ReleaseSignals();
 
+  ClearString(&query_string);
+
   return (pid);
 }
 
@@ -3347,5 +3406,5 @@ CDSAWriteFunc(SSLConnectionRef connection,        /* I  - SSL/TLS connection */
 
 
 /*
- * End of "$Id: client.c,v 1.91.2.64 2003/07/20 03:13:08 mike Exp $".
+ * End of "$Id$".
  */