]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/client.c
Merge changes from CUPS 1.4svn-r7874.
[thirdparty/cups.git] / scheduler / client.c
index 9c0a17dc68191759b1e521f654192352769283b9..a4e249790881b47cc6fd94ba438e0c1066136161 100644 (file)
@@ -1,28 +1,19 @@
 /*
- * "$Id: client.c 6329 2007-03-12 14:48:28Z mike $"
+ * "$Id: client.c 7673 2008-06-18 22:31:26Z mike $"
  *
  *   Client routines for the Common UNIX Printing System (CUPS) scheduler.
  *
+ *   Copyright 2007-2008 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   This file contains Kerberos support code, copyright 2006 by
  *   Jelmer Vernooij.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  * Contents:
  *
@@ -36,8 +27,9 @@
  *   cupsdSendHeader()       - Send an HTTP request.
  *   cupsdUpdateCGI()        - Read status messages from CGI scripts and programs.
  *   cupsdWriteClient()      - Write data to a client as needed.
- *   cupsdWritePipe()        - Flag that data is available on the CGI pipe.
  *   check_if_modified()     - Decode an "If-Modified-Since" line.
+ *   compare_clients()       - Compare two client connections.
+ *   data_ready()            - Check whether data is available from a client.
  *   encrypt_client()        - Enable encryption for the client...
  *   get_cdsa_certificate()  - Convert a keychain name into the CFArrayRef
  *                            required by SSLSetCertificate.
  *   make_certificate()      - Make a self-signed SSL/TLS certificate.
  *   pipe_command()          - Pipe the output of a command to the remote client.
  *   write_file()            - Send a file via HTTP.
+ *   write_pipe()            - Flag that data is available on the CGI pipe.
  */
 
 /*
  * Include necessary headers...
  */
 
-#include <cups/http-private.h>
 #include "cupsd.h"
 
 #ifdef HAVE_CDSASSL
@@ -85,6 +77,10 @@ extern const char *cssmErrorString(int error);
 #  include <gnutls/x509.h>
 #endif /* HAVE_GNUTLS */
 
+#ifdef HAVE_TCPD_H
+#  include <tcpd.h>
+#endif /* HAVE_TCPD_H */
+
 
 /*
  * Local functions...
@@ -92,6 +88,9 @@ extern const char *cssmErrorString(int error);
 
 static int             check_if_modified(cupsd_client_t *con,
                                          struct stat *filestats);
+static int             compare_clients(cupsd_client_t *a, cupsd_client_t *b,
+                                       void *data);
+static int             data_ready(cupsd_client_t *con);
 #ifdef HAVE_SSL
 static int             encrypt_client(cupsd_client_t *con);
 #endif /* HAVE_SSL */
@@ -112,6 +111,7 @@ static int          pipe_command(cupsd_client_t *con, int infile, int *outfile,
 static int             write_file(cupsd_client_t *con, http_status_t code,
                                   char *filename, char *type,
                                   struct stat *filestats);
+static void            write_pipe(cupsd_client_t *con);
 
 
 /*
@@ -131,6 +131,9 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
   char                 *hostname;      /* Hostname for address */
   http_addr_t          temp;           /* Temporary address variable */
   static time_t                last_dos = 0;   /* Time of last DoS attack */
+#ifdef HAVE_TCPD_H
+  struct request_info  wrap_req;       /* TCP wrappers request information */
+#endif /* HAVE_TCPD_H */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
@@ -152,9 +155,30 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
     Clients = cupsArrayNew(NULL, NULL);
 
   if (!Clients)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to allocate memory for clients array!");
+    cupsdPauseListening();
     return;
+  }
 
-  con = calloc(1, sizeof(cupsd_client_t));
+  if (!ActiveClients)
+    ActiveClients = cupsArrayNew((cups_array_func_t)compare_clients, NULL);
+
+  if (!ActiveClients)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to allocate memory for active clients array!");
+    cupsdPauseListening();
+    return;
+  }
+
+  if ((con = calloc(1, sizeof(cupsd_client_t))) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to allocate memory for client!");
+    cupsdPauseListening();
+    return;
+  }
 
   con->http.activity = time(NULL);
   con->file          = -1;
@@ -169,9 +193,13 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
   if ((con->http.fd = accept(lis->fd, (struct sockaddr *)con->http.hostaddr,
                              &addrlen)) < 0)
   {
+    if (errno == ENFILE || errno == EMFILE)
+      cupsdPauseListening();
+
     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to accept client connection - %s.",
                     strerror(errno));
     free(con);
+
     return;
   }
 
@@ -221,7 +249,9 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
       cupsdLogMessage(CUPSD_LOG_WARN,
                       "Possible DoS attack - more than %d clients connecting "
                      "from %s!",
-                     MaxClientsPerHost, tempcon->http.hostname);
+                     MaxClientsPerHost,
+                     httpAddrString(con->http.hostaddr, con->http.hostname,
+                                    sizeof(con->http.hostname)));
     }
 
 #ifdef WIN32
@@ -303,7 +333,8 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
     * Do double lookups as needed...
     */
 
-    if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL)) != NULL)
+    if ((addrlist = httpAddrGetList(con->http.hostname, AF_UNSPEC, NULL))
+            != NULL)
     {
      /*
       * See if the hostname maps to the same IP address...
@@ -343,6 +374,34 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
     }
   }
 
+#ifdef HAVE_TCPD_H
+ /*
+  * See if the connection is denied by TCP wrappers...
+  */
+
+  request_init(&wrap_req, RQ_DAEMON, "cupsd", RQ_FILE, con->http.fd, NULL);
+  fromhost(&wrap_req);
+
+  if (!hosts_access(&wrap_req))
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                    "cupsdAcceptClient: Closing connection %d...",
+                    con->http.fd);
+
+#ifdef WIN32
+    closesocket(con->http.fd);
+#else
+    close(con->http.fd);
+#endif /* WIN32 */
+
+    cupsdLogMessage(CUPSD_LOG_WARN,
+                    "Connection from %s refused by /etc/hosts.allow and "
+                   "/etc/hosts.deny rules.", con->http.hostname);
+    free(con);
+    return;
+  }
+#endif /* HAVE_TCPD_H */
+
 #ifdef AF_INET6
   if (con->http.hostaddr->addr.sa_family == AF_INET6)
     cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAcceptClient: %d from %s:%d (IPv6)",
@@ -517,15 +576,15 @@ cupsdCloseClient(cupsd_client_t *con)     /* I - Client to close */
     switch (SSL_shutdown(conn))
     {
       case 1 :
-          cupsdLogMessage(CUPSD_LOG_INFO,
-                         "cupsdCloseClient: SSL shutdown successful!");
+          cupsdLogMessage(CUPSD_LOG_DEBUG,
+                         "SSL shutdown successful!");
          break;
       case -1 :
           cupsdLogMessage(CUPSD_LOG_ERROR,
-                         "cupsdCloseClient: Fatal error during SSL shutdown!");
+                         "Fatal error during SSL shutdown!");
       default :
          while ((error = ERR_get_error()) != 0)
-           cupsdLogMessage(CUPSD_LOG_ERROR, "cupsdCloseClient: %s",
+           cupsdLogMessage(CUPSD_LOG_ERROR, "SSL shutdown failed: %s",
                            ERR_error_string(error, NULL));
           break;
     }
@@ -541,12 +600,12 @@ cupsdCloseClient(cupsd_client_t *con)     /* I - Client to close */
     switch (error)
     {
       case GNUTLS_E_SUCCESS:
-       cupsdLogMessage(CUPSD_LOG_INFO,
-                       "cupsdCloseClient: SSL shutdown successful!");
+       cupsdLogMessage(CUPSD_LOG_DEBUG,
+                       "SSL shutdown successful!");
        break;
       default:
        cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "cupsdCloseClient: %s", gnutls_strerror(error));
+                       "SSL shutdown failed: %s", gnutls_strerror(error));
        break;
     }
 
@@ -602,8 +661,11 @@ cupsdCloseClient(cupsd_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)
   {
+    cupsArrayRemove(ActiveClients, con);
+    cupsdSetBusyState();
+
     if (partial)
     {
      /*
@@ -635,6 +697,7 @@ cupsdCloseClient(cupsd_client_t *con)       /* I - Client to close */
       free(con->http.input_set);
 
     httpClearCookie(HTTP(con));
+    httpClearFields(HTTP(con));
 
     cupsdClearString(&con->filename);
     cupsdClearString(&con->command);
@@ -833,6 +896,10 @@ cupsdReadClient(cupsd_client_t *con)       /* I - Client to read from */
          con->language = NULL;
        }
 
+#ifdef HAVE_GSSAPI
+        con->gss_have_creds = 0;
+#endif /* HAVE_GSSAPI */
+
        /*
         * Grab the request line...
        */
@@ -840,11 +907,14 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
        {
          case 1 :
-             cupsdLogMessage(CUPSD_LOG_ERROR,
-                             "Bad request line \"%s\" from %s!", line,
-                             con->http.hostname);
-             cupsdSendError(con, HTTP_BAD_REQUEST);
-             cupsdCloseClient(con);
+             if (line[0])
+             {
+               cupsdLogMessage(CUPSD_LOG_ERROR,
+                               "Bad request line \"%s\" from %s!", line,
+                               con->http.hostname);
+               cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
+               cupsdCloseClient(con);
+              }
              return;
          case 2 :
              con->http.version = HTTP_0_9;
@@ -855,7 +925,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
                cupsdLogMessage(CUPSD_LOG_ERROR,
                                "Bad request line \"%s\" from %s!", line,
                                con->http.hostname);
-               cupsdSendError(con, HTTP_BAD_REQUEST);
+               cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
                cupsdCloseClient(con);
                return;
              }
@@ -870,7 +940,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
              }
              else
              {
-               cupsdSendError(con, HTTP_NOT_SUPPORTED);
+               cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE);
                cupsdCloseClient(con);
                return;
              }
@@ -916,7 +986,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
 
            cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!",
                            con->uri);
-           cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED);
+           cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE);
            cupsdCloseClient(con);
            return;
          }
@@ -950,7 +1020,7 @@ cupsdReadClient(cupsd_client_t *con)       /* I - Client to read from */
        else
        {
          cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation);
-         cupsdSendError(con, HTTP_BAD_REQUEST);
+         cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
          cupsdCloseClient(con);
          return;
        }
@@ -964,6 +1034,9 @@ cupsdReadClient(cupsd_client_t *con)       /* I - Client to read from */
 
        con->http.status = HTTP_OK;
 
+        cupsArrayAdd(ActiveClients, con);
+       cupsdSetBusyState();
+
     case HTTP_OPTIONS :
     case HTTP_DELETE :
     case HTTP_GET :
@@ -976,13 +1049,12 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
        */
 
         while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
-         if (con->http.used == 0 ||
-             !memchr(con->http.buffer, '\n', con->http.used))
+         if (!data_ready(con))
            break;
 
        if (status != HTTP_OK && status != HTTP_CONTINUE)
        {
-         cupsdSendError(con, HTTP_BAD_REQUEST);
+         cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
          cupsdCloseClient(con);
          return;
        }
@@ -1028,7 +1100,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         snprintf(locale, sizeof(locale), "%s.%s",
                 con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], DefaultCharset);
 
-      cupsdLogMessage(CUPSD_LOG_DEBUG,
+      cupsdLogMessage(CUPSD_LOG_DEBUG2,
                       "cupsdReadClient: %d Browser asked for language \"%s\"...",
                       con->http.fd, locale);
 
@@ -1050,7 +1122,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
       * HTTP/1.1 and higher require the "Host:" field...
       */
 
-      if (!cupsdSendError(con, HTTP_BAD_REQUEST))
+      if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
       {
        cupsdCloseClient(con);
        return;
@@ -1062,9 +1134,9 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
       * Do OPTIONS command...
       */
 
-      if (con->best && con->best->type != AUTH_NONE)
+      if (con->best && con->best->type != CUPSD_AUTH_NONE)
       {
-       if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL))
+       if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1079,7 +1151,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         * Do encryption stuff...
        */
 
-       if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
+       if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1102,7 +1174,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          return;
        }
 #else
-       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
+       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1110,7 +1182,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 #endif /* HAVE_SSL */
       }
 
-      if (!cupsdSendHeader(con, HTTP_OK, NULL))
+      if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
       {
        cupsdCloseClient(con);
        return;
@@ -1132,7 +1204,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
       * Protect against malicious users!
       */
 
-      if (!cupsdSendError(con, HTTP_FORBIDDEN))
+      if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
       {
        cupsdCloseClient(con);
        return;
@@ -1148,7 +1220,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         * Do encryption stuff...
        */
 
-       if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
+       if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1171,7 +1243,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          return;
        }
 #else
-       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
+       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1182,9 +1254,9 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
       if ((status = cupsdIsAuthorized(con, NULL)) != HTTP_OK)
       {
         cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                       "cupsdReadClient: Unauthorized request for %s...\n",
+                       "cupsdReadClient: Unauthorized request for %s...",
                        con->uri);
-       cupsdSendError(con, status);
+       cupsdSendError(con, status, CUPSD_AUTH_NONE);
        cupsdCloseClient(con);
        return;
       }
@@ -1198,7 +1270,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          * Send 100-continue header...
          */
 
-         if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL))
+         if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE))
          {
            cupsdCloseClient(con);
            return;
@@ -1210,7 +1282,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          * Send 417-expectation-failed header...
          */
 
-         if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL))
+         if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL, CUPSD_AUTH_NONE))
          {
            cupsdCloseClient(con);
            return;
@@ -1244,7 +1316,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
              else
              {
-               if (!cupsdSendError(con, HTTP_NOT_FOUND))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1316,7 +1388,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
               if (!cupsdSendCommand(con, con->command, con->options, 0))
              {
-               if (!cupsdSendError(con, HTTP_NOT_FOUND))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1340,7 +1412,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * /admin/conf...
              */
 
-             if (!cupsdSendError(con, HTTP_FORBIDDEN))
+             if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1357,7 +1429,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
               if ((filename = get_file(con, &filestats, buf,
                                       sizeof(buf))) == NULL)
              {
-               if (!cupsdSendError(con, HTTP_NOT_FOUND))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1377,7 +1449,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
                if (!cupsdSendCommand(con, con->command, con->options, 0))
                {
-                 if (!cupsdSendError(con, HTTP_NOT_FOUND))
+                 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                  {
                    cupsdCloseClient(con);
                    return;
@@ -1393,7 +1465,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
              if (!check_if_modified(con, &filestats))
               {
-               if (!cupsdSendError(con, HTTP_NOT_MODIFIED))
+               if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1433,7 +1505,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * Request too large...
              */
 
-              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1441,13 +1513,15 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
 
              break;
             }
-           else if (con->http.data_remaining < 0)
+           else if (con->http.data_remaining < 0 ||
+                    (!con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
+                     con->http.data_encoding == HTTP_ENCODE_LENGTH))
            {
             /*
              * Negative content lengths are invalid!
              */
 
-              if (!cupsdSendError(con, HTTP_BAD_REQUEST))
+              if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1542,7 +1616,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
               if ((filename = get_file(con, &filestats, buf,
                                       sizeof(buf))) == NULL)
              {
-               if (!cupsdSendError(con, HTTP_NOT_FOUND))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1559,7 +1633,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                * Only POST to CGI's...
                */
 
-               if (!cupsdSendError(con, HTTP_UNAUTHORIZED))
+               if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1582,7 +1656,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * /admin/conf...
              */
 
-             if (!cupsdSendError(con, HTTP_FORBIDDEN))
+             if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1608,7 +1682,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * Request too large...
              */
 
-              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1622,7 +1696,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * Negative content lengths are invalid!
              */
 
-              if (!cupsdSendError(con, HTTP_BAD_REQUEST))
+              if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1639,19 +1713,23 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
                            request_id ++);
            con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
 
-            cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                           "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
-                           con->filename, con->file);
-
            if (con->file < 0)
            {
-             if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+             cupsdLogMessage(CUPSD_LOG_ERROR,
+                             "Unable to create request file %s: %s",
+                             con->filename, strerror(errno));
+
+             if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
              }
            }
 
+            cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                           "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
+                           con->filename, con->file);
+
            fchmod(con->file, 0640);
            fchown(con->file, RunUser, Group);
            fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
@@ -1659,7 +1737,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
        case HTTP_DELETE :
        case HTTP_TRACE :
-            cupsdSendError(con, HTTP_NOT_IMPLEMENTED);
+            cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE);
            cupsdCloseClient(con);
            return;
 
@@ -1678,7 +1756,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                snprintf(con->uri, sizeof(con->uri), "/ppd/%s.ppd", p->name);
              else
              {
-               if (!cupsdSendError(con, HTTP_NOT_FOUND))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1700,7 +1778,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * CGI output...
              */
 
-              if (!cupsdSendHeader(con, HTTP_OK, "text/html"))
+              if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1732,7 +1810,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * /admin/conf...
              */
 
-             if (!cupsdSendError(con, HTTP_FORBIDDEN))
+             if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1743,7 +1821,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            else if ((filename = get_file(con, &filestats, buf,
                                          sizeof(buf))) == NULL)
            {
-             if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html"))
+             if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html", CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1753,7 +1831,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            }
            else if (!check_if_modified(con, &filestats))
             {
-              if (!cupsdSendError(con, HTTP_NOT_MODIFIED))
+              if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1773,7 +1851,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              else
                snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
 
-              if (!cupsdSendHeader(con, HTTP_OK, line))
+              if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1862,7 +1940,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              unlink(con->filename);
              cupsdClearString(&con->filename);
 
-              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1870,7 +1948,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            }
          }
         }
-       while (con->http.state == HTTP_PUT_RECV && con->http.used > 0);
+       while (con->http.state == HTTP_PUT_RECV && data_ready(con));
 
         if (con->http.state == HTTP_WAITING)
        {
@@ -1902,7 +1980,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            unlink(con->filename);
            cupsdClearString(&con->filename);
 
-            if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+            if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
            {
              cupsdCloseClient(con);
              return;
@@ -1919,7 +1997,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          * Return the status to the client...
          */
 
-          if (!cupsdSendError(con, status))
+          if (!cupsdSendError(con, status, CUPSD_AUTH_NONE))
          {
            cupsdCloseClient(con);
            return;
@@ -1950,7 +2028,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                              "cupsdReadClient: %d IPP Read Error!",
                              con->http.fd);
 
-             cupsdSendError(con, HTTP_BAD_REQUEST);
+             cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
              cupsdCloseClient(con);
              return;
            }
@@ -1958,7 +2036,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            {
               if (con->http.state == HTTP_POST_SEND)
              {
-               cupsdSendError(con, HTTP_BAD_REQUEST);
+               cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
                cupsdCloseClient(con);
                return;
              }
@@ -1978,18 +2056,22 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
             cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
            con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
 
-            cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
-                           con->filename, con->file);
-
            if (con->file < 0)
            {
-             if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+             cupsdLogMessage(CUPSD_LOG_ERROR,
+                             "Unable to create request file %s: %s",
+                             con->filename, strerror(errno));
+
+             if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
              }
            }
 
+            cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
+                           con->filename, con->file);
+
            fchmod(con->file, 0640);
            fchown(con->file, RunUser, Group);
             fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
@@ -2025,7 +2107,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                unlink(con->filename);
                cupsdClearString(&con->filename);
 
-               if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+               if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -2041,7 +2123,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            }
          }
         }
-       while (con->http.state == HTTP_POST_RECV && con->http.used > 0);
+       while (con->http.state == HTTP_POST_RECV && data_ready(con));
 
        if (con->http.state == HTTP_POST_SEND)
        {
@@ -2081,7 +2163,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                con->request = NULL;
               }
 
-              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -2092,7 +2174,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            {
              if (!cupsdSendCommand(con, con->command, con->options, 0))
              {
-               if (!cupsdSendError(con, HTTP_NOT_FOUND))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -2106,6 +2188,16 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
           if (con->request)
          {
            cupsdProcessIPPRequest(con);
+
+           if (con->filename)
+           {
+             cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                             "cupsdReadClient: %d Removing temp file %s",
+                             con->http.fd, con->filename);
+             unlink(con->filename);
+             cupsdClearString(&con->filename);
+           }
+
            return;
          }
        }
@@ -2115,8 +2207,16 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
         break; /* Anti-compiler-warning-code */
   }
 
-  if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
-    cupsdCloseClient(con);
+  if (con->http.state == HTTP_WAITING)
+  {
+    if (!con->http.keep_alive)
+      cupsdCloseClient(con);
+    else
+    {
+      cupsArrayRemove(ActiveClients, con);
+      cupsdSetBusyState();
+    }
+  }
 }
 
 
@@ -2168,7 +2268,7 @@ cupsdSendCommand(
 
   fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
 
-  cupsdAddSelect(con->file, (cupsd_selfunc_t)cupsdWritePipe, NULL, con);
+  cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
 
   con->sent_header = 0;
   con->file_ready  = 0;
@@ -2185,7 +2285,8 @@ cupsdSendCommand(
 
 int                                    /* O - 1 if successful, 0 otherwise */
 cupsdSendError(cupsd_client_t *con,    /* I - Connection */
-               http_status_t  code)    /* I - Error code */
+               http_status_t  code,    /* I - Error code */
+              int            auth_type)/* I - Authentication type */
 {
 #ifdef HAVE_SSL
  /*
@@ -2221,7 +2322,7 @@ cupsdSendError(cupsd_client_t *con,       /* I - Connection */
   * never disable it in that case.
   */
 
-  if (code >= HTTP_BAD_REQUEST && con->http.auth_type != AUTH_KERBEROS)
+  if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE)
     con->http.keep_alive = HTTP_KEEPALIVE_OFF;
 
  /*
@@ -2229,7 +2330,7 @@ cupsdSendError(cupsd_client_t *con,       /* I - Connection */
   * 400 or 500 series, make sure the message contains some text, too!
   */
 
-  if (!cupsdSendHeader(con, code, NULL))
+  if (!cupsdSendHeader(con, code, NULL, auth_type))
     return (0);
 
 #ifdef HAVE_SSL
@@ -2334,11 +2435,17 @@ cupsdSendError(cupsd_client_t *con,     /* I - Connection */
  */
 
 int                                    /* O - 1 on success, 0 on failure */
-cupsdSendHeader(cupsd_client_t *con,   /* I - Client to send to */
-                http_status_t  code,   /* I - HTTP status code */
-               char           *type)   /* I - MIME type of document */
+cupsdSendHeader(
+    cupsd_client_t *con,               /* I - Client to send to */
+    http_status_t  code,               /* I - HTTP status code */
+    char           *type,              /* I - MIME type of document */
+    int            auth_type)          /* I - Type of authentication */
 {
-  char auth_str[1024];                 /* Authorization string */
+  char         auth_str[1024];         /* Authorization string */
+#ifdef HAVE_GSSAPI
+  static char  *gss_buf = NULL;        /* Kerberos auth data buffer */
+  static int   gss_bufsize = 0;        /* Size of Kerberos auth data buffer */
+#endif /* HAVE_GSSAPI */
 
 
  /*
@@ -2381,29 +2488,28 @@ cupsdSendHeader(cupsd_client_t *con,    /* I - Client to send to */
 
   if (code == HTTP_UNAUTHORIZED)
   {
-    int        auth_type;                      /* Authentication type */
-
-
-    if (!con->best || con->best->type == AUTH_NONE)
-      auth_type = DefaultAuthType;
-    else
-      auth_type = con->best->type;
+    if (auth_type == CUPSD_AUTH_NONE)
+    {
+      if (!con->best || con->best->type <= CUPSD_AUTH_NONE)
+       auth_type = DefaultAuthType;
+      else
+       auth_type = con->best->type;
+    }
 
     auth_str[0] = '\0';
 
-    if (auth_type == AUTH_BASIC || auth_type == AUTH_BASICDIGEST)
+    if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
       strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
-    else if (auth_type == AUTH_DIGEST)
+    else if (auth_type == CUPSD_AUTH_DIGEST)
       snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
               con->http.hostname);
 #ifdef HAVE_GSSAPI
-    else if (auth_type == AUTH_KERBEROS && !con->no_negotiate &&
-            con->gss_output_token.length == 0)
+    else if (auth_type == CUPSD_AUTH_NEGOTIATE && con->gss_output_token.length == 0)
       strlcpy(auth_str, "Negotiate", sizeof(auth_str));
 #endif /* HAVE_GSSAPI */
 
 #ifdef HAVE_AUTHORIZATION_H
-    if (con->best)
+    if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE)
     {
       int       i;                     /* Looping var */
       char     *auth_key;              /* Auth key buffer */
@@ -2432,9 +2538,14 @@ cupsdSendHeader(cupsd_client_t *con,     /* I - Client to send to */
     }
 #endif /* HAVE_AUTHORIZATION_H */
 
-    if (auth_str[0] &&
-       httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
-      return (0);
+    if (auth_str[0])
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendHeader: WWW-Authenticate: %s",
+                      auth_str);
+
+      if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
+        return (0);
+    }
   }
 
 #ifdef HAVE_GSSAPI
@@ -2443,20 +2554,57 @@ cupsdSendHeader(cupsd_client_t *con,    /* I - Client to send to */
   * non-401 replies...
   */
 
-  if (con->gss_output_token.length > 0)
+  if (con->gss_output_token.length > 0 && con->gss_output_token.length <= 65536)
   {
-    char       buf[2048];              /* Output token buffer */
     OM_uint32  minor_status;           /* Minor status code */
+    int                bufsize;                /* Size of output token buffer */
+
 
+    bufsize = con->gss_output_token.length * 4 / 3 + 2;
 
-    httpEncode64_2(buf, sizeof(buf),
-                  con->gss_output_token.value,
+    if (bufsize > gss_bufsize)
+    {
+      char     *buf;                   /* New buffer */
+
+
+      bufsize = (bufsize + 1023) & 1023;/* Round up */
+
+      if (gss_buf)
+        buf = realloc(gss_buf, bufsize);
+      else
+        buf = malloc(bufsize);
+
+      if (!buf)
+      {
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Unable to allocate %d bytes for Kerberos credentials!",
+                       bufsize);
+       return (0);
+      }
+
+      gss_buf     = buf;
+      gss_bufsize = bufsize;
+    }
+
+    httpEncode64_2(gss_buf, gss_bufsize,
+                  con->gss_output_token.value,
                   con->gss_output_token.length);
     gss_release_buffer(&minor_status, &con->gss_output_token);
 
-    if (httpPrintf(HTTP(con), "WWW-Authenticate: Negotiate %s\r\n", buf) < 0)
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "cupsdSendHeader: WWW-Authenticate: Negotiate %s", gss_buf);
+
+    if (httpPrintf(HTTP(con), "WWW-Authenticate: Negotiate %s\r\n",
+                   gss_buf) < 0)
       return (0);
   }
+  else if (con->gss_output_token.length > 65536)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Kerberos credentials larger than 64k (%d)!",
+                   (int)con->gss_output_token.length);
+    return (0);
+  }
 #endif /* HAVE_GSSAPI */
 
   if (con->language && strcmp(con->language->language, "C"))
@@ -2527,10 +2675,11 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
 
 #ifdef DEBUG
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdWriteClient(con=%p) %d response=%p, file=%d "
+                  "cupsdWriteClient(con=%p) %d response=%p(%d), file=%d "
                  "pipe_pid=%d state=%d",
-                  con, con->http.fd, con->response, con->file, con->pipe_pid,
-                 con->http.state);
+                  con, con->http.fd, con->response,
+                 con->response ? con->response->state : -1,
+                 con->file, con->pipe_pid, con->http.state);
 #endif /* DEBUG */
 
   if (con->http.state != HTTP_GET_SEND &&
@@ -2543,7 +2692,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
     * Make sure we select on the CGI output...
     */
 
-    cupsdAddSelect(con->file, (cupsd_selfunc_t)cupsdWritePipe, NULL, con);
+    cupsdAddSelect(con->file, (cupsd_selfunc_t)write_pipe, NULL, con);
 
     if (!con->file_ready)
     {
@@ -2558,10 +2707,11 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
     con->file_ready = 0;
   }
 
-  if (con->response)
+  if (con->response && con->response->state != IPP_DATA)
   {
     ipp_state = ippWrite(HTTP(con), con->response);
-    bytes     = ipp_state != IPP_ERROR && ipp_state != IPP_DATA;
+    bytes     = ipp_state != IPP_ERROR &&
+                (con->file >= 0 || ipp_state != IPP_DATA);
   }
   else if ((bytes = read(con->file, buf, sizeof(buf) - 1)) > 0)
   {
@@ -2598,7 +2748,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
 
             if (!strncasecmp(buf, "Location:", 9))
            {
-             cupsdSendHeader(con, HTTP_SEE_OTHER, NULL);
+             cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE);
              con->sent_header = 2;
 
              if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
@@ -2606,12 +2756,12 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
            }
            else if (!strncasecmp(buf, "Status:", 7))
            {
-             cupsdSendError(con, (http_status_t)atoi(buf + 7));
+             cupsdSendError(con, (http_status_t)atoi(buf + 7), CUPSD_AUTH_NONE);
              con->sent_header = 2;
            }
            else
            {
-             cupsdSendHeader(con, HTTP_OK, NULL);
+             cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE);
              con->sent_header = 1;
 
              if (con->http.version == HTTP_1_1)
@@ -2675,7 +2825,10 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
         return;
       }
       else if (bytes == 0)
+      {
         con->http.activity = time(NULL);
+        return;
+      }
     }
 
     if (bytes > 0)
@@ -2690,6 +2843,9 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
        return;
       }
 
+      if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
+        httpFlushWrite(HTTP(con));
+
       con->bytes += bytes;
 
       if (con->http.state == HTTP_WAITING)
@@ -2771,23 +2927,6 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
 }
 
 
-/*
- * 'cupsdWritePipe()' - Flag that data is available on the CGI pipe.
- */
-
-void
-cupsdWritePipe(cupsd_client_t *con)    /* I - Client connection */
-{
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWritePipe: CGI output on fd %d...",
-                  con->file);
-
-  con->file_ready = 1;
-
-  cupsdRemoveSelect(con->file);
-  cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
-}
-
-
 /*
  * 'check_if_modified()' - Decode an "If-Modified-Since" line.
  */
@@ -2849,6 +2988,58 @@ check_if_modified(
 }
 
 
+/*
+ * 'compare_clients()' - Compare two client connections.
+ */
+
+static int                             /* O - Result of comparison */
+compare_clients(cupsd_client_t *a,     /* I - First client */
+                cupsd_client_t *b,     /* I - Second client */
+                void           *data)  /* I - User data (not used) */
+{
+  (void)data;
+
+  if (a == b)
+    return (0);
+  else if (a < b)
+    return (-1);
+  else
+    return (1);
+}
+
+
+/*
+ * 'data_ready()' - Check whether data is available from a client.
+ */
+
+static int                             /* O - 1 if data is ready, 0 otherwise */
+data_ready(cupsd_client_t *con)                /* I - Client */
+{
+  if (con->http.used > 0)
+    return (1);
+#ifdef HAVE_SSL
+  else if (con->http.tls)
+  {
+#  ifdef HAVE_LIBSSL
+    if (SSL_pending((SSL *)(con->http.tls)))
+      return (1);
+#  elif defined(HAVE_GNUTLS)
+    if (gnutls_record_check_pending(((http_tls_t *)(con->http.tls))->session))
+      return (1);
+#  elif defined(HAVE_CDSASSL)
+    size_t bytes;                      /* Bytes that are available */
+
+    if (!SSLGetBufferedReadSize(((http_tls_t *)(con->http.tls))->session,
+                                &bytes) && bytes > 0)
+      return (1);
+#  endif /* HAVE_LIBSSL */
+  }
+#endif /* HAVE_SSL */
+
+  return (0);
+}
+
+
 #ifdef HAVE_SSL
 /*
  * 'encrypt_client()' - Enable encryption for the client...
@@ -2886,7 +3077,8 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
 
   SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
   SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
-  SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM);
+  SSL_CTX_use_certificate_chain_file(context, ServerCertificate,
+                                     SSL_FILETYPE_PEM);
 
   bio = BIO_new(_httpBIOMethods());
   BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
@@ -3127,15 +3319,15 @@ get_cdsa_certificate(cupsd_client_t *con)       /* I - Client connection */
                        ssl_options;    /* SSL Option for hostname */
 
 
-  if ((err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL, 
-                             NULL, &policy_search)))
+  if (SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL, 
+                           NULL, &policy_search))
   {
     cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference");
     CFRelease(keychain);
     return (NULL);
   }
 
-  if ((err = SecPolicySearchCopyNext(policy_search, &policy)))
+  if (SecPolicySearchCopyNext(policy_search, &policy))
   {
     cupsdLogMessage(CUPSD_LOG_ERROR, 
                    "Cannot find a policy to use for searching");
@@ -3152,7 +3344,7 @@ get_cdsa_certificate(cupsd_client_t *con) /* I - Client connection */
   options.Data = (uint8 *)&ssl_options;
   options.Length = sizeof(ssl_options);
 
-  if ((err = SecPolicySetValue(policy, &options)))
+  if (SecPolicySetValue(policy, &options))
   {
     cupsdLogMessage(CUPSD_LOG_ERROR, 
                    "Cannot set policy value to use for searching");
@@ -3224,12 +3416,15 @@ get_file(cupsd_client_t *con,           /* I  - Client connection */
   int          status;                 /* Status of filesystem calls */
   char         *ptr;                   /* Pointer info filename */
   int          plen;                   /* Remaining length after pointer */
+  char         language[7];            /* Language subdirectory, if any */
 
 
  /*
   * Figure out the real filename...
   */
 
+  language[0] = '\0';
+
   if (!strncmp(con->uri, "/ppd/", 5))
     snprintf(filename, len, "%s%s", ServerRoot, con->uri);
   else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
@@ -3238,18 +3433,20 @@ get_file(cupsd_client_t *con,           /* I  - Client connection */
     snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
   else if (!strncmp(con->uri, "/admin/log/", 11))
   {
-    if (!strcmp(con->uri + 11, "access_log") && AccessLog[0] == '/')
+    if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/')
       strlcpy(filename, AccessLog, len);
-    else if (!strcmp(con->uri + 11, "error_log") && ErrorLog[0] == '/')
+    else if (!strncmp(con->uri + 11, "error_log", 9) && ErrorLog[0] == '/')
       strlcpy(filename, ErrorLog, len);
-    else if (!strcmp(con->uri + 11, "page_log") && PageLog[0] == '/')
+    else if (!strncmp(con->uri + 11, "page_log", 8) && PageLog[0] == '/')
       strlcpy(filename, PageLog, len);
     else
       return (NULL);
   }
   else if (con->language)
-    snprintf(filename, len, "%s/%s%s", DocumentRoot, con->language->language,
-             con->uri);
+  {
+    snprintf(language, sizeof(language), "/%s", con->language->language);
+    snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
+  }
   else
     snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
 
@@ -3261,7 +3458,7 @@ get_file(cupsd_client_t *con,             /* I  - Client connection */
   * then fallback to the default one...
   */
 
-  if ((status = stat(filename, filestats)) != 0 && con->language &&
+  if ((status = stat(filename, filestats)) != 0 && language[0] &&
       strncmp(con->uri, "/ppd/", 5) &&
       strncmp(con->uri, "/admin/conf/", 12) &&
       strncmp(con->uri, "/admin/log/", 11))
@@ -3270,11 +3467,8 @@ get_file(cupsd_client_t *con,            /* I  - Client connection */
     * Drop the country code...
     */
 
-    char       ll[3];                  /* Short language name */
-
-
-    strlcpy(ll, con->language->language, sizeof(ll));
-    snprintf(filename, len, "%s/%s%s", DocumentRoot, ll, con->uri);
+    language[3] = '\0';
+    snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
 
     if ((ptr = strchr(filename, '?')) != NULL)
       *ptr = '\0';
@@ -3285,6 +3479,7 @@ get_file(cupsd_client_t *con,             /* I  - Client connection */
       * Drop the language prefix and try the root directory...
       */
 
+      language[0] = '\0';
       snprintf(filename, len, "%s%s", DocumentRoot, con->uri);
 
       if ((ptr = strchr(filename, '?')) != NULL)
@@ -3300,52 +3495,86 @@ get_file(cupsd_client_t *con,           /* I  - Client connection */
 
   if (!status && S_ISDIR(filestats->st_mode))
   {
-    if (filename[strlen(filename) - 1] != '/')
-      strlcat(filename, "/", len);
+   /*
+    * Make sure the URI ends with a slash...
+    */
 
-    ptr  = filename + strlen(filename);
-    plen = len - (ptr - filename);
+    if (con->uri[strlen(con->uri) - 1] != '/')
+      strlcat(con->uri, "/", sizeof(con->uri));
 
-    strlcpy(ptr, "index.html", plen);
-    status = stat(filename, filestats);
+   /*
+    * Find the directory index file, trying every language...
+    */
 
-#ifdef HAVE_JAVA
-    if (status)
+    do
     {
-      strlcpy(ptr, "index.class", plen);
+      if (status && language[0])
+      {
+       /*
+        * Try a different language subset...
+       */
+
+       if (language[3])
+         language[0] = '\0';           /* Strip country code */
+       else
+         language[0] = '\0';           /* Strip language */
+      }
+
+     /*
+      * Look for the index file...
+      */
+
+      snprintf(filename, len, "%s%s%s", DocumentRoot, language, con->uri);
+
+      if ((ptr = strchr(filename, '?')) != NULL)
+       *ptr = '\0';
+
+      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);
-    }
+      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);
-    }
+      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.pyc", plen);
+       status = stat(filename, filestats);
+      }
 
-    if (status)
-    {
-      strlcpy(ptr, "index.py", plen);
-      status = stat(filename, filestats);
-    }
+      if (status)
+      {
+       strlcpy(ptr, "index.py", plen);
+       status = stat(filename, filestats);
+      }
 #endif /* HAVE_PYTHON */
+
+    }
+    while (status && language[0]);
   }
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_file: %d filename=%s size=%d",
@@ -3539,7 +3768,7 @@ is_cgi(cupsd_client_t *con,               /* I - Client connection */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "is_cgi(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)\n",
+                  "is_cgi(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)",
                  con, filename, filestats, type ? type->super : "unknown",
                  type ? type->type : "unknown");
 
@@ -3550,9 +3779,7 @@ is_cgi(cupsd_client_t *con,               /* I - Client connection */
   if ((options = strchr(con->uri, '?')) != NULL)
   {
     options ++;
-
-    if (strchr(options, '='))
-      cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
+    cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
   }
 
  /*
@@ -3574,9 +3801,8 @@ is_cgi(cupsd_client_t *con,               /* I - Client connection */
 
     cupsdSetString(&con->command, filename);
 
-    filename = strrchr(filename, '/') + 1; /* Filename always absolute */
-
-    cupsdSetString(&con->options, options);
+    if (options)
+      cupsdSetStringf(&con->options, " %s", options);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
                     "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
@@ -3594,9 +3820,9 @@ is_cgi(cupsd_client_t *con,               /* I - Client connection */
     cupsdSetString(&con->command, CUPS_JAVA);
 
     if (options)
-      cupsdSetStringf(&con->options, "%s %s", filename, options);
+      cupsdSetStringf(&con->options, " %s %s", filename, options);
     else
-      cupsdSetString(&con->options, filename);
+      cupsdSetStringf(&con->options, " %s", filename);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
                     "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
@@ -3615,9 +3841,9 @@ is_cgi(cupsd_client_t *con,               /* I - Client connection */
     cupsdSetString(&con->command, CUPS_PERL);
 
     if (options)
-      cupsdSetStringf(&con->options, "%s %s", filename, options);
+      cupsdSetStringf(&con->options, " %s %s", filename, options);
     else
-      cupsdSetString(&con->options, filename);
+      cupsdSetStringf(&con->options, " %s", filename);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
                     "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
@@ -3636,9 +3862,9 @@ is_cgi(cupsd_client_t *con,               /* I - Client connection */
     cupsdSetString(&con->command, CUPS_PHP);
 
     if (options)
-      cupsdSetStringf(&con->options, "%s %s", filename, options);
+      cupsdSetStringf(&con->options, " %s %s", filename, options);
     else
-      cupsdSetString(&con->options, filename);
+      cupsdSetStringf(&con->options, " %s", filename);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
                     "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
@@ -3657,9 +3883,9 @@ is_cgi(cupsd_client_t *con,               /* I - Client connection */
     cupsdSetString(&con->command, CUPS_PYTHON);
 
     if (options)
-      cupsdSetStringf(&con->options, "%s %s", filename, options);
+      cupsdSetStringf(&con->options, " %s %s", filename, options);
     else
-      cupsdSetString(&con->options, filename);
+      cupsdSetStringf(&con->options, " %s", filename);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
                     "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
@@ -3798,7 +4024,8 @@ make_certificate(cupsd_client_t *con)     /* I - Client connection */
     envp[envc++] = home;
     envp[envc]   = NULL;
 
-    if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, &pid))
+    if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL,
+                           &pid))
     {
       unlink(seedfile);
       return (0);
@@ -3875,7 +4102,8 @@ make_certificate(cupsd_client_t *con)     /* I - Client connection */
 
   infofd = open(infofile, O_RDONLY);
 
-  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, &pid))
+  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
+                         &pid))
   {
     close(infofd);
     unlink(infofile);
@@ -4108,7 +4336,8 @@ make_certificate(cupsd_client_t *con)     /* I - Client connection */
 
   infofd = open(infofile, O_RDONLY);
 
-  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, &pid))
+  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, NULL,
+                         &pid))
   {
     close(infofd);
     unlink(infofile);
@@ -4178,8 +4407,9 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
   int          envc;                   /* Number of environment variables */
   char         argbuf[10240],          /* Argument buffer */
                *argv[100],             /* Argument strings */
-               *envp[MAX_ENV + 18];    /* Environment variables */
-  char         content_length[1024],   /* CONTENT_LENGTH environment variable */
+               *envp[MAX_ENV + 20];    /* Environment variables */
+  char         auth_type[256],         /* CUPSD_AUTH_TYPE environment variable */
+               content_length[1024],   /* CONTENT_LENGTH environment variable */
                content_type[1024],     /* CONTENT_TYPE environment variable */
                http_cookie[32768],     /* HTTP_COOKIE environment variable */
                http_referer[1024],     /* HTTP_REFERER environment variable */
@@ -4189,9 +4419,15 @@ pipe_command(cupsd_client_t *con,        /* I - Client connection */
                remote_addr[1024],      /* REMOTE_ADDR environment variable */
                remote_host[1024],      /* REMOTE_HOST environment variable */
                remote_user[1024],      /* REMOTE_USER environment variable */
+               script_filename[1024],  /* SCRIPT_FILENAME environment variable */
                script_name[1024],      /* SCRIPT_NAME environment variable */
                server_name[1024],      /* SERVER_NAME environment variable */
                server_port[1024];      /* SERVER_PORT environment variable */
+  ipp_attribute_t *attr;               /* attributes-natural-language attribute */
+#ifdef HAVE_GSSAPI
+  krb5_ccache  ccache = NULL;          /* Kerberos credentials */
+  char         krb5ccname[1024];       /* KRB5CCNAME environment variable */
+#endif /* HAVE_GSSAPI */
 
 
  /*
@@ -4241,9 +4477,10 @@ pipe_command(cupsd_client_t *con,        /* I - Client connection */
   {
     commptr      = argbuf;
     path_info[0] = '\0';
-  }
 
-  cupsdLogMessage(CUPSD_LOG_INFO, "commptr=\"%s\"", commptr);
+    if (*commptr == ' ')
+      commptr ++;
+  }
 
   if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
   {
@@ -4315,8 +4552,56 @@ pipe_command(cupsd_client_t *con,        /* I - Client connection */
   * Setup the environment variables as needed...
   */
 
-  if (con->language)
-    snprintf(lang, sizeof(lang), "LANG=%s.UTF-8", con->language->language);
+  if (con->username[0])
+  {
+    snprintf(auth_type, sizeof(auth_type), "CUPSD_AUTH_TYPE=%s",
+             httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION));
+
+    if ((uriptr = strchr(auth_type + 10, ' ')) != NULL)
+      *uriptr = '\0';
+  }
+  else
+    auth_type[0] = '\0';
+
+  if (con->request &&
+      (attr = ippFindAttribute(con->request, "attributes-natural-language",
+                               IPP_TAG_LANGUAGE)) != NULL)
+  {
+    switch (strlen(attr->values[0].string.text))
+    {
+      default :
+        /*
+         * This is an unknown or badly formatted language code; use
+         * the POSIX locale...
+         */
+
+         strcpy(lang, "LANG=C");
+         break;
+
+      case 2 :
+        /*
+         * Just the language code (ll)...
+         */
+
+         snprintf(lang, sizeof(lang), "LANG=%s.UTF8",
+                  attr->values[0].string.text);
+         break;
+
+      case 5 :
+        /*
+         * Language and country code (ll-cc)...
+         */
+
+         snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF8",
+                  attr->values[0].string.text[0],
+                  attr->values[0].string.text[1],
+                  toupper(attr->values[0].string.text[3] & 255),
+                  toupper(attr->values[0].string.text[4] & 255));
+         break;
+    }
+  }
+  else if (con->language)
+    snprintf(lang, sizeof(lang), "LANG=%s.UTF8", con->language->language);
   else
     strcpy(lang, "LANG=C");
 
@@ -4331,6 +4616,9 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
   if ((uriptr = strchr(script_name, '?')) != NULL)
     *uriptr = '\0';
 
+  snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s",
+           DocumentRoot, script_name + 12);
+
   sprintf(server_port, "SERVER_PORT=%d", con->serverport);
 
   snprintf(server_name, sizeof(server_name), "SERVER_NAME=%s",
@@ -4338,13 +4626,18 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
 
   envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
 
+  if (auth_type[0])
+    envp[envc ++] = auth_type;
+
   envp[envc ++] = lang;
   envp[envc ++] = "REDIRECT_STATUS=1";
+  envp[envc ++] = "GATEWAY_INTERFACE=CGI/1.1";
   envp[envc ++] = server_name;
   envp[envc ++] = server_port;
   envp[envc ++] = remote_addr;
   envp[envc ++] = remote_host;
   envp[envc ++] = script_name;
+  envp[envc ++] = script_filename;
 
   if (path_info[0])
     envp[envc ++] = path_info;
@@ -4354,6 +4647,120 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
     snprintf(remote_user, sizeof(remote_user), "REMOTE_USER=%s", con->username);
 
     envp[envc ++] = remote_user;
+
+   /*
+    * Save Kerberos credentials, if any...
+    */
+
+#ifdef HAVE_GSSAPI
+    if (con->gss_have_creds)
+    {
+#  if !defined(HAVE_KRB5_CC_NEW_UNIQUE) && !defined(HAVE_HEIMDAL)
+      cupsdLogMessage(CUPSD_LOG_INFO,
+                     "Sorry, your version of Kerberos does not support "
+                     "delegated credentials!");
+
+#  else
+      krb5_error_code  error;          /* Kerberos error code */
+      OM_uint32                major_status,   /* Major status code */
+                       minor_status;   /* Minor status code */
+      krb5_principal   principal;      /* Kerberos principal */
+
+
+#   ifdef __APPLE__
+     /*
+      * If the weak-linked GSSAPI/Kerberos library is not present, don't try
+      * to use it...
+      */
+
+      if (krb5_init_context != NULL)
+      {
+#    endif /* __APPLE__ */
+
+     /*
+      * We MUST create a file-based cache because memory-based caches are
+      * only valid for the current process/address space.
+      *
+      * Due to various bugs/features in different versions of Kerberos, we
+      * need either the krb5_cc_new_unique() function or Heimdal's version
+      * of krb5_cc_gen_new() to create a new FILE: credential cache that
+      * can be passed to the backend.  These functions create a temporary
+      * file (typically in /tmp) containing the cached credentials, which
+      * are removed when we have successfully printed a job.
+      */
+
+#    ifdef HAVE_KRB5_CC_NEW_UNIQUE
+      if ((error = krb5_cc_new_unique(KerberosContext, "FILE", NULL,
+                                     &ccache)) != 0)
+#    else /* HAVE_HEIMDAL */
+      if ((error = krb5_cc_gen_new(KerberosContext, &krb5_fcc_ops,
+                                  &ccache)) != 0)
+#    endif /* HAVE_KRB5_CC_NEW_UNIQUE */
+      {
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Unable to create new credentials cache (%d/%s)",
+                       error, strerror(errno));
+       ccache = NULL;
+      }
+      else if ((error = krb5_parse_name(KerberosContext, con->username,
+                                       &principal)) != 0)
+      {
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Unable to parse kerberos username (%d/%s)", error,
+                       strerror(errno));
+       krb5_cc_destroy(KerberosContext, ccache);
+       ccache = NULL;
+      }
+      else if ((error = krb5_cc_initialize(KerberosContext, ccache,
+                                           principal)))
+      {
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "Unable to initialize credentials cache (%d/%s)", error,
+                       strerror(errno));
+       krb5_cc_destroy(KerberosContext, ccache);
+       krb5_free_principal(KerberosContext, principal);
+       ccache = NULL;
+      }
+      else
+      {
+       krb5_free_principal(KerberosContext, principal);
+
+       /*
+       * Copy the user's credentials to the new cache file...
+       */
+
+       major_status = gss_krb5_copy_ccache(&minor_status,
+                                           con->gss_delegated_cred, ccache);
+
+       if (GSS_ERROR(major_status))
+       {
+         cupsdLogGSSMessage(CUPSD_LOG_ERROR, major_status, minor_status,
+                            "Unable to import client credentials cache");
+         krb5_cc_destroy(KerberosContext, ccache);
+         ccache = NULL;
+       }
+       else
+       {
+        /*
+         * Add the KRB5CCNAME environment variable to the job so that the
+         * backend can use the credentials when printing.
+         */
+
+         snprintf(krb5ccname, sizeof(krb5ccname), "KRB5CCNAME=FILE:%s",
+                  krb5_cc_get_name(KerberosContext, ccache));
+          envp[envc++] = krb5ccname;
+
+         if (!RunUser)
+           chown(krb5_cc_get_name(KerberosContext, ccache), User, Group);
+        }
+     }
+#    ifdef __APPLE__
+     }
+#    endif /* __APPLE__ */
+#  endif /* HAVE_KRB5_CC_NEW_UNIQUE || HAVE_HEIMDAL */
+    }
+#endif /* HAVE_GSSAPI */
+
   }
 
   if (con->http.version == HTTP_1_1)
@@ -4396,6 +4803,8 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
 
       envp[envc ++] = con->query_string;
     }
+    else
+      envp[envc ++] = "QUERY_STRING=";
   }
   else
   {
@@ -4448,7 +4857,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
   */
 
   if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
-                       -1, -1, root, &pid) < 0)
+                       -1, -1, root, DefaultProfile, &pid) < 0)
   {
    /*
     * Error - can't fork!
@@ -4467,9 +4876,14 @@ pipe_command(cupsd_client_t *con,        /* I - Client connection */
     */
 
     if (con->username[0])
-      cupsdAddCert(pid, con->username);
+#ifdef HAVE_GSSAPI
+      cupsdAddCert(pid, con->username, ccache);
+#else
+      cupsdAddCert(pid, con->username, NULL);
+#endif /* HAVE_GSSAPI */
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG, "CGI %s started - PID = %d", command, pid);
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "[CGI] %s started - PID = %d",
+                    command, pid);
 
     *outfile = fds[0];
     close(fds[1]);
@@ -4492,7 +4906,7 @@ write_file(cupsd_client_t *con,           /* I - Client connection */
 {
   con->file = open(filename, O_RDONLY);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "write_file: %d file=%d", con->http.fd,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "write_file: %d file=%d", con->http.fd,
                   con->file);
 
   if (con->file < 0)
@@ -4502,7 +4916,7 @@ write_file(cupsd_client_t *con,           /* I - Client connection */
 
   con->pipe_pid = 0;
 
-  if (!cupsdSendHeader(con, code, type))
+  if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
     return (0);
 
   if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
@@ -4533,5 +4947,22 @@ write_file(cupsd_client_t *con,          /* I - Client connection */
 
 
 /*
- * End of "$Id: client.c 6329 2007-03-12 14:48:28Z mike $".
+ * 'write_pipe()' - Flag that data is available on the CGI pipe.
+ */
+
+static void
+write_pipe(cupsd_client_t *con)                /* I - Client connection */
+{
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "write_pipe: CGI output on fd %d...",
+                  con->file);
+
+  con->file_ready = 1;
+
+  cupsdRemoveSelect(con->file);
+  cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
+}
+
+
+/*
+ * End of "$Id: client.c 7673 2008-06-18 22:31:26Z mike $".
  */