]> 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 9bf53adb2a311323510d718c10a39360da4ab964..de825609becaf707cdd59df3c2d563d34c4f766e 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: client.c,v 1.91.2.76 2004/02/25 16:22:01 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
  *
@@ -39,6 +39,7 @@
  *   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.
@@ -63,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);
 
@@ -110,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...
@@ -162,7 +165,7 @@ AcceptClient(listener_t *lis)       /* I - Listener socket */
 
     return;
   }
-  
+
  /*
   * Get the hostname or format the IP address as needed...
   */
@@ -352,7 +355,7 @@ 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? */
@@ -368,7 +371,7 @@ CloseClient(client_t *con)  /* I - Client to close */
 #endif /* HAVE_GNUTLS */
 
 
-  LogMessage(L_DEBUG, "CloseClient() %d", con->http.fd);
+  LogMessage(L_DEBUG, "CloseClient: %d", con->http.fd);
 
   partial = 0;
 
@@ -458,7 +461,7 @@ CloseClient(client_t *con)  /* I - Client to close */
       close(con->http.fd);
       FD_CLR(con->http.fd, InputSet);
       FD_CLR(con->http.fd, OutputSet);
-      con->http.fd = 0;
+      con->http.fd = -1;
     }
   }
 
@@ -539,6 +542,8 @@ CloseClient(client_t *con)  /* I - Client to close */
     if (con < (Clients + NumClients))
       memmove(con, con + 1, (Clients + NumClients - con) * sizeof(client_t));
   }
+
+  return (partial);
 }
 
 
@@ -569,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));
 
@@ -577,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);
   
@@ -599,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);
   }
@@ -616,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);
@@ -624,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;
@@ -685,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;
@@ -696,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);
 
@@ -735,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);
@@ -876,13 +898,13 @@ ReadClient(client_t *con)         /* I - Client to read from */
 
   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)
@@ -894,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));
        }
 
        /*
@@ -941,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);
-             CloseClient(con);
-             return (0);
+             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);
-               CloseClient(con);
-               return (0);
+               return (CloseClient(con));
              }
 
              if (major < 2)
@@ -968,8 +990,7 @@ ReadClient(client_t *con)           /* I - Client to read from */
              else
              {
                SendError(con, HTTP_NOT_SUPPORTED);
-               CloseClient(con);
-               return (0);
+               return (CloseClient(con));
              }
              break;
        }
@@ -1008,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);
-           CloseClient(con);
-           return (0);
+           return (CloseClient(con));
          }
 
          /*
@@ -1042,14 +1062,13 @@ ReadClient(client_t *con)               /* I - Client to read from */
        {
          LogMessage(L_ERROR, "Bad operation \"%s\"!", operation);
          SendError(con, HTTP_BAD_REQUEST);
-         CloseClient(con);
-         return (0);
+         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);
 
@@ -1071,8 +1090,7 @@ ReadClient(client_t *con)         /* I - Client to read from */
        if (status != HTTP_OK && status != HTTP_CONTINUE)
        {
          SendError(con, HTTP_BAD_REQUEST);
-         CloseClient(con);
-         return (0);
+         return (CloseClient(con));
        }
        break;
 
@@ -1135,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)
     {
@@ -1150,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 &&
@@ -1165,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");
@@ -1178,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 */
       }
 
@@ -1191,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
     {
@@ -1223,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");
@@ -1234,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);
-        CloseClient(con);
-       return (0);
+       return (CloseClient(con));
       }
 
       if (con->http.expect)
@@ -1277,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;
              }
@@ -1299,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)
              {
@@ -1314,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] == '/')
-               cups_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);
@@ -1344,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;
            }
@@ -1361,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;
              }
@@ -1373,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);
@@ -1392,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
               {
@@ -1405,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;
@@ -1431,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;
             }
@@ -1445,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;
            }
@@ -1473,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)
              {
@@ -1494,7 +1471,7 @@ ReadClient(client_t *con)         /* I - Client to read from */
              if (con->options[0] == '/')
                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)
@@ -1510,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;
              }
@@ -1527,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;
@@ -1550,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;
            }
@@ -1575,10 +1543,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;
             }
@@ -1589,10 +1554,7 @@ ReadClient(client_t *con)                /* I - Client to read from */
              */
 
               if (!SendError(con, HTTP_BAD_REQUEST))
-             {
-               CloseClient(con);
-               return (0);
-             }
+               return (CloseClient(con));
 
              break;
            }
@@ -1603,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, getuid(), 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);
-            CloseClient(con);
-           return (0);
+           return (CloseClient(con));
 
        case HTTP_HEAD :
             if (strncmp(con->uri, "/printers/", 10) == 0 &&
@@ -1641,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;
              }
@@ -1661,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);
            }
@@ -1684,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;
            }
@@ -1695,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);
            }
@@ -1725,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;
@@ -1769,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)
@@ -1800,10 +1727,7 @@ ReadClient(client_t *con)                /* I - Client to read from */
            ClearString(&con->filename);
 
             if (!SendError(con, HTTP_REQUEST_TOO_LARGE))
-           {
-             CloseClient(con);
-             return (0);
-           }
+             return (CloseClient(con));
          }
        }
 
@@ -1815,7 +1739,7 @@ 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);
@@ -1828,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));
          }
 
          /*
@@ -1851,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);
@@ -1872,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);
-           }
-
-           CloseClient(con);
-           return (0);
+           SendError(con, HTTP_BAD_REQUEST);
+           return (CloseClient(con));
          }
          else if (ipp_state != IPP_DATA)
            break;
@@ -1898,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, getuid(), 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)
@@ -1942,21 +1850,13 @@ ReadClient(client_t *con)               /* I - Client to read from */
              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)
@@ -1965,7 +1865,7 @@ 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);
@@ -1978,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);
@@ -1994,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)
@@ -2005,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);
@@ -2025,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);
 }
@@ -2051,6 +1942,16 @@ 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);
@@ -2105,10 +2006,9 @@ 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 (%s)", con->http.fd, code,
+  LogMessage(L_DEBUG, "SendError: %d code=%d (%s)", con->http.fd, code,
              httpStatus(code));
 
  /*
@@ -2188,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);
@@ -2233,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)
@@ -2261,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);
     }
@@ -2292,129 +2193,23 @@ SendHeader(client_t    *con,   /* I - Client to send to */
 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 */
-
-
-  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 ++;
+  char         *ptr,                   /* Pointer to end of line in buffer */
+               message[1024];          /* Pointer to message text */
+  int          loglevel;               /* Log level for 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));
   }
 }
@@ -2551,11 +2346,11 @@ WriteClient(client_t *con)              /* I - Client connection */
                    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);
@@ -2565,7 +2360,7 @@ WriteClient(client_t *con)                /* I - Client connection */
 
     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);
@@ -2632,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')
@@ -2656,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);
 
@@ -2701,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;
     }
@@ -2726,20 +2521,24 @@ 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);
 }
 
 
+/*
+ * '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 */
@@ -2845,7 +2644,7 @@ get_file(client_t    *con,                /* I  - Client connection */
 #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)
@@ -3013,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.
  */
@@ -3028,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 */
@@ -3048,7 +2881,7 @@ 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 */
@@ -3101,39 +2934,89 @@ pipe_command(client_t *con,             /* I - Client connection */
 
                  "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
@@ -3145,9 +3028,15 @@ pipe_command(client_t *con,              /* I - Client connection */
         *commptr |= tolower(commptr[2]) - 'a' + 10;
 
       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;
 
@@ -3163,7 +3052,7 @@ pipe_command(client_t *con,               /* I - Client connection */
              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)
@@ -3284,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;
     }
   }
@@ -3312,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);
@@ -3354,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...
@@ -3396,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...
     */
@@ -3448,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
@@ -3468,6 +3351,8 @@ pipe_command(client_t *con,               /* I - Client connection */
 
   ReleaseSignals();
 
+  ClearString(&query_string);
+
   return (pid);
 }
 
@@ -3521,5 +3406,5 @@ CDSAWriteFunc(SSLConnectionRef connection,        /* I  - SSL/TLS connection */
 
 
 /*
- * End of "$Id: client.c,v 1.91.2.76 2004/02/25 16:22:01 mike Exp $".
+ * End of "$Id$".
  */