]> 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 61cb4f9555edb6ca5c794f5435906ba12d667a99..a4e249790881b47cc6fd94ba438e0c1066136161 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: client.c 6758 2007-08-02 00:13:44Z mike $"
+ * "$Id: client.c 7673 2008-06-18 22:31:26Z mike $"
  *
  *   Client routines for the Common UNIX Printing System (CUPS) scheduler.
  *
- *   Copyright 2007 by Apple Inc.
+ *   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
@@ -28,6 +28,8 @@
  *   cupsdUpdateCGI()        - Read status messages from CGI scripts and programs.
  *   cupsdWriteClient()      - Write data to a client as needed.
  *   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.
@@ -45,7 +47,6 @@
  * Include necessary headers...
  */
 
-#include <cups/http-private.h>
 #include "cupsd.h"
 
 #ifdef HAVE_CDSASSL
@@ -76,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...
@@ -83,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 */
@@ -123,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,
@@ -144,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;
@@ -161,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;
   }
 
@@ -213,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
@@ -295,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...
@@ -335,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)",
@@ -509,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;
     }
@@ -533,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;
     }
 
@@ -594,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)
     {
      /*
@@ -627,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);
@@ -836,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, AUTH_NONE);
-             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;
@@ -851,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, AUTH_NONE);
+               cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
                cupsdCloseClient(con);
                return;
              }
@@ -866,7 +940,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
              }
              else
              {
-               cupsdSendError(con, HTTP_NOT_SUPPORTED, AUTH_NONE);
+               cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE);
                cupsdCloseClient(con);
                return;
              }
@@ -912,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, AUTH_NONE);
+           cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE);
            cupsdCloseClient(con);
            return;
          }
@@ -946,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, AUTH_NONE);
+         cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
          cupsdCloseClient(con);
          return;
        }
@@ -960,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 :
@@ -972,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, AUTH_NONE);
+         cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
          cupsdCloseClient(con);
          return;
        }
@@ -1046,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, AUTH_NONE))
+      if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
       {
        cupsdCloseClient(con);
        return;
@@ -1058,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, AUTH_NONE))
+       if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1075,7 +1151,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         * Do encryption stuff...
        */
 
-       if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, AUTH_NONE))
+       if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1098,7 +1174,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          return;
        }
 #else
-       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, AUTH_NONE))
+       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1106,7 +1182,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 #endif /* HAVE_SSL */
       }
 
-      if (!cupsdSendHeader(con, HTTP_OK, NULL, AUTH_NONE))
+      if (!cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE))
       {
        cupsdCloseClient(con);
        return;
@@ -1128,7 +1204,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
       * Protect against malicious users!
       */
 
-      if (!cupsdSendError(con, HTTP_FORBIDDEN, AUTH_NONE))
+      if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
       {
        cupsdCloseClient(con);
        return;
@@ -1144,7 +1220,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         * Do encryption stuff...
        */
 
-       if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, AUTH_NONE))
+       if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1167,7 +1243,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          return;
        }
 #else
-       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, AUTH_NONE))
+       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1178,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, AUTH_NONE);
+       cupsdSendError(con, status, CUPSD_AUTH_NONE);
        cupsdCloseClient(con);
        return;
       }
@@ -1194,7 +1270,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          * Send 100-continue header...
          */
 
-         if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, AUTH_NONE))
+         if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL, CUPSD_AUTH_NONE))
          {
            cupsdCloseClient(con);
            return;
@@ -1206,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, AUTH_NONE))
+         if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL, CUPSD_AUTH_NONE))
          {
            cupsdCloseClient(con);
            return;
@@ -1240,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, AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1312,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, AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1336,7 +1412,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * /admin/conf...
              */
 
-             if (!cupsdSendError(con, HTTP_FORBIDDEN, AUTH_NONE))
+             if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1353,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, AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1373,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, AUTH_NONE))
+                 if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                  {
                    cupsdCloseClient(con);
                    return;
@@ -1389,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, AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1429,7 +1505,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * Request too large...
              */
 
-              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, AUTH_NONE))
+              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1437,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, AUTH_NONE))
+              if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1538,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, AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1555,7 +1633,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                * Only POST to CGI's...
                */
 
-               if (!cupsdSendError(con, HTTP_UNAUTHORIZED, AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_UNAUTHORIZED, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1578,7 +1656,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * /admin/conf...
              */
 
-             if (!cupsdSendError(con, HTTP_FORBIDDEN, AUTH_NONE))
+             if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1604,7 +1682,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * Request too large...
              */
 
-              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, AUTH_NONE))
+              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1618,7 +1696,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * Negative content lengths are invalid!
              */
 
-              if (!cupsdSendError(con, HTTP_BAD_REQUEST, AUTH_NONE))
+              if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1635,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, AUTH_NONE))
+             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);
@@ -1655,7 +1737,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
        case HTTP_DELETE :
        case HTTP_TRACE :
-            cupsdSendError(con, HTTP_NOT_IMPLEMENTED, AUTH_NONE);
+            cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE);
            cupsdCloseClient(con);
            return;
 
@@ -1674,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, AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -1696,7 +1778,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * CGI output...
              */
 
-              if (!cupsdSendHeader(con, HTTP_OK, "text/html", AUTH_NONE))
+              if (!cupsdSendHeader(con, HTTP_OK, "text/html", CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1728,7 +1810,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * /admin/conf...
              */
 
-             if (!cupsdSendError(con, HTTP_FORBIDDEN, AUTH_NONE))
+             if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1739,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", AUTH_NONE))
+             if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html", CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1749,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, AUTH_NONE))
+              if (!cupsdSendError(con, HTTP_NOT_MODIFIED, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1769,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, AUTH_NONE))
+              if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1858,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, AUTH_NONE))
+              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1866,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)
        {
@@ -1898,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, AUTH_NONE))
+            if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
            {
              cupsdCloseClient(con);
              return;
@@ -1915,7 +1997,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          * Return the status to the client...
          */
 
-          if (!cupsdSendError(con, status, AUTH_NONE))
+          if (!cupsdSendError(con, status, CUPSD_AUTH_NONE))
          {
            cupsdCloseClient(con);
            return;
@@ -1946,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, AUTH_NONE);
+             cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
              cupsdCloseClient(con);
              return;
            }
@@ -1954,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, AUTH_NONE);
+               cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
                cupsdCloseClient(con);
                return;
              }
@@ -1974,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, AUTH_NONE))
+             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);
@@ -2021,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, AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -2037,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)
        {
@@ -2077,7 +2163,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                con->request = NULL;
               }
 
-              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, AUTH_NONE))
+              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -2088,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, AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -2121,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();
+    }
+  }
 }
 
 
@@ -2228,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_NEGOTIATE)
+  if (code >= HTTP_BAD_REQUEST && con->http.auth_type != CUPSD_AUTH_NEGOTIATE)
     con->http.keep_alive = HTTP_KEEPALIVE_OFF;
 
  /*
@@ -2347,7 +2441,11 @@ cupsdSendHeader(
     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 */
 
 
  /*
@@ -2390,9 +2488,9 @@ cupsdSendHeader(
 
   if (code == HTTP_UNAUTHORIZED)
   {
-    if (auth_type == AUTH_NONE)
+    if (auth_type == CUPSD_AUTH_NONE)
     {
-      if (!con->best || con->best->type == AUTH_NONE)
+      if (!con->best || con->best->type <= CUPSD_AUTH_NONE)
        auth_type = DefaultAuthType;
       else
        auth_type = con->best->type;
@@ -2400,18 +2498,18 @@ cupsdSendHeader(
 
     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_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 && auth_type != AUTH_NEGOTIATE)
+    if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE)
     {
       int       i;                     /* Looping var */
       char     *auth_key;              /* Auth key buffer */
@@ -2456,23 +2554,57 @@ cupsdSendHeader(
   * 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;
+
+    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);
 
-    httpEncode64_2(buf, sizeof(buf),
-                  con->gss_output_token.value,
+      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);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG,
-                    "cupsdSendHeader: WWW-Authenticate: Negotiate %s", buf);
+                   "cupsdSendHeader: WWW-Authenticate: Negotiate %s", gss_buf);
 
-    if (httpPrintf(HTTP(con), "WWW-Authenticate: Negotiate %s\r\n", buf) < 0)
+    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"))
@@ -2545,7 +2677,8 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "cupsdWriteClient(con=%p) %d response=%p(%d), file=%d "
                  "pipe_pid=%d state=%d",
-                  con, con->http.fd, con->response, con->response->state,
+                  con, con->http.fd, con->response,
+                 con->response ? con->response->state : -1,
                  con->file, con->pipe_pid, con->http.state);
 #endif /* DEBUG */
 
@@ -2615,7 +2748,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
 
             if (!strncasecmp(buf, "Location:", 9))
            {
-             cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, AUTH_NONE);
+             cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE);
              con->sent_header = 2;
 
              if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
@@ -2623,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), AUTH_NONE);
+             cupsdSendError(con, (http_status_t)atoi(buf + 7), CUPSD_AUTH_NONE);
              con->sent_header = 2;
            }
            else
            {
-             cupsdSendHeader(con, HTTP_OK, NULL, AUTH_NONE);
+             cupsdSendHeader(con, HTTP_OK, NULL, CUPSD_AUTH_NONE);
              con->sent_header = 1;
 
              if (con->http.version == HTTP_1_1)
@@ -2692,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)
@@ -2707,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)
@@ -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\"",
@@ -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);
@@ -4179,7 +4408,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
   char         argbuf[10240],          /* Argument buffer */
                *argv[100],             /* Argument strings */
                *envp[MAX_ENV + 20];    /* Environment variables */
-  char         auth_type[256],         /* AUTH_TYPE environment variable */
+  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 */
@@ -4194,6 +4423,11 @@ pipe_command(cupsd_client_t *con,        /* I - Client connection */
                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 */
 
 
  /*
@@ -4248,8 +4482,6 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
       commptr ++;
   }
 
-  cupsdLogMessage(CUPSD_LOG_INFO, "commptr=\"%s\"", commptr);
-
   if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
   {
     commptr ++;
@@ -4322,7 +4554,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
 
   if (con->username[0])
   {
-    snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s",
+    snprintf(auth_type, sizeof(auth_type), "CUPSD_AUTH_TYPE=%s",
              httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION));
 
     if ((uriptr = strchr(auth_type + 10, ' ')) != NULL)
@@ -4331,8 +4563,45 @@ pipe_command(cupsd_client_t *con,        /* I - Client connection */
   else
     auth_type[0] = '\0';
 
-  if (con->language)
-    snprintf(lang, sizeof(lang), "LANG=%s.UTF-8", con->language->language);
+  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");
 
@@ -4378,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)
@@ -4420,6 +4803,8 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
 
       envp[envc ++] = con->query_string;
     }
+    else
+      envp[envc ++] = "QUERY_STRING=";
   }
   else
   {
@@ -4472,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!
@@ -4491,7 +4876,11 @@ 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);
@@ -4527,7 +4916,7 @@ write_file(cupsd_client_t *con,           /* I - Client connection */
 
   con->pipe_pid = 0;
 
-  if (!cupsdSendHeader(con, code, type, AUTH_NONE))
+  if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
     return (0);
 
   if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
@@ -4575,5 +4964,5 @@ write_pipe(cupsd_client_t *con)           /* I - Client connection */
 
 
 /*
- * End of "$Id: client.c 6758 2007-08-02 00:13:44Z mike $".
+ * End of "$Id: client.c 7673 2008-06-18 22:31:26Z mike $".
  */