]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/client.c
Merge changes from CUPS 1.5svn-r9400
[thirdparty/cups.git] / scheduler / client.c
index 8a37842c789be38bd4fd57765e0ed2700f22f744..1d0dcf12eb60cd6933e97e5680b465b3a56741af 100644 (file)
@@ -1,9 +1,9 @@
 /*
- * "$Id: client.c 6999 2007-09-28 19:46:53Z mike $"
+ * "$Id: client.c 7950 2008-09-17 00:21:59Z mike $"
  *
- *   Client routines for the Common UNIX Printing System (CUPS) scheduler.
+ *   Client routines for the CUPS scheduler.
  *
- *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 2007-2010 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   This file contains Kerberos support code, copyright 2006 by
  *
  * Contents:
  *
- *   cupsdAcceptClient()     - Accept a new client.
- *   cupsdCloseAllClients()  - Close all remote clients immediately.
- *   cupsdCloseClient()      - Close a remote client.
- *   cupsdFlushHeader()      - Flush the header fields to the client.
- *   cupsdReadClient()       - Read data from a client.
- *   cupsdSendCommand()      - Send output from a command via HTTP.
- *   cupsdSendError()        - Send an error message via HTTP.
- *   cupsdSendHeader()       - Send an HTTP request.
- *   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.
- *   encrypt_client()        - Enable encryption for the client...
- *   get_cdsa_certificate()  - Convert a keychain name into the CFArrayRef
- *                            required by SSLSetCertificate.
- *   get_file()              - Get a filename and state info.
- *   install_conf_file()     - Install a configuration file.
- *   is_cgi()                - Is the resource a CGI script/program?
- *   is_path_absolute()      - Is a path absolute and free of relative elements.
- *   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.
+ *   cupsdAcceptClient()    - Accept a new client.
+ *   cupsdCloseAllClients() - Close all remote clients immediately.
+ *   cupsdCloseClient()     - Close a remote client.
+ *   cupsdFlushHeader()     - Flush the header fields to the client.
+ *   cupsdReadClient()      - Read data from a client.
+ *   cupsdSendCommand()     - Send output from a command via HTTP.
+ *   cupsdSendError()       - Send an error message via HTTP.
+ *   cupsdSendHeader()      - Send an HTTP request.
+ *   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() - Get a SSL/TLS certificate from the System
+ *                            keychain.
+ *   get_file()             - Get a filename and state info.
+ *   install_conf_file()    - Install a configuration file.
+ *   is_cgi()               - Is the resource a CGI script/program?
+ *   is_path_absolute()     - Is a path absolute and free of relative elements
+ *                            (i.e. "..").
+ *   make_certificate()     - Make a self-signed SSL/TLS certificate.
+ *   pipe_command()         - Pipe the output of a command to the remote client.
+ *   valid_host()           - Is the Host: field valid?
+ *   write_file()           - Send a file via HTTP.
+ *   write_pipe()           - Flag that data is available on the CGI pipe.
  */
 
 /*
 
 #include "cupsd.h"
 
-#ifdef HAVE_CDSASSL
-#  include <Security/Security.h>
-#  ifdef HAVE_SECIDENTITYSEARCHPRIV_H
-#    include <Security/SecIdentitySearchPriv.h>
-#  else /* Declare prototype for function in that header... */
-extern OSStatus SecIdentitySearchCreateWithPolicy(SecPolicyRef policy, 
-                               CFStringRef idString, CSSM_KEYUSE keyUsage, 
-                               CFTypeRef keychainOrArray, 
-                               Boolean returnOnlyValidIdentities, 
-                               SecIdentitySearchRef* searchRef);
-#  endif /* HAVE_SECIDENTITYSEARCHPRIV_H */
-#  ifdef HAVE_SECPOLICYPRIV_H
-#    include <Security/SecPolicyPriv.h>
-#  else /* Declare prototype for function in that header... */
-extern OSStatus SecPolicySetValue(SecPolicyRef policyRef, 
-                                  const CSSM_DATA *value);
-#  endif /* HAVE_SECPOLICYPRIV_H */
-#  ifdef HAVE_SECBASEPRIV_H
-#    include <Security/SecBasePriv.h>
-#  else /* Declare prototype for function in that header... */
-extern const char *cssmErrorString(int error);
-#  endif /* HAVE_SECBASEPRIV_H */
-#endif /* HAVE_CDSASSL */
-
-#ifdef HAVE_GNUTLS
-#  include <gnutls/x509.h>
-#endif /* HAVE_GNUTLS */
+#ifdef HAVE_TCPD_H
+#  include <tcpd.h>
+#endif /* HAVE_TCPD_H */
 
 
 /*
@@ -85,6 +65,7 @@ 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 */
@@ -102,6 +83,7 @@ static int           make_certificate(cupsd_client_t *con);
 #endif /* HAVE_SSL */
 static int             pipe_command(cupsd_client_t *con, int infile, int *outfile,
                                     char *command, char *options, int root);
+static int             valid_host(cupsd_client_t *con);
 static int             write_file(cupsd_client_t *con, http_status_t code,
                                   char *filename, char *type,
                                   struct stat *filestats);
@@ -125,10 +107,13 @@ 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,
-                  "cupsdAcceptClient(lis=%p) %d Clients = %d",
+                  "cupsdAcceptClient(lis=%p(%d)) Clients=%d",
                   lis, lis->fd, cupsArrayCount(Clients));
 
  /*
@@ -240,7 +225,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
@@ -272,16 +259,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
     * Map accesses from the same host to the server name.
     */
 
-    for (addr = ServerAddrs; addr; addr = addr->next)
-      if (httpAddrEqual(con->http.hostaddr, &(addr->addr)))
-        break;
-
-    if (addr)
-    {
-      strlcpy(con->http.hostname, ServerName, sizeof(con->http.hostname));
-      hostname = con->http.hostname;
-    }
-    else if (HostNameLookups)
+    if (HostNameLookups)
       hostname = httpAddrLookup(con->http.hostaddr, con->http.hostname,
                                 sizeof(con->http.hostname));
     else
@@ -298,10 +276,6 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
     * Can't have an unresolved IP address with double-lookups enabled...
     */
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdAcceptClient: Closing connection %d...",
-                    con->http.fd);
-
 #ifdef WIN32
     closesocket(con->http.fd);
 #else
@@ -322,7 +296,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...
@@ -344,10 +319,6 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
       * with double-lookups enabled...
       */
 
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdAcceptClient: Closing connection %d...",
-                     con->http.fd);
-
 #ifdef WIN32
       closesocket(con->http.fd);
 #else
@@ -362,6 +333,30 @@ 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))
+  {
+#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)",
@@ -397,14 +392,26 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
 #ifdef AF_INET6
     if (temp.addr.sa_family == AF_INET6)
     {
-      httpAddrLookup(&temp, con->servername, sizeof(con->servername));
+      if (httpAddrLocalhost(&temp))
+        strlcpy(con->servername, "localhost", sizeof(con->servername));
+      else if (HostNameLookups || RemotePort)
+        httpAddrLookup(&temp, con->servername, sizeof(con->servername));
+      else
+        httpAddrString(&temp, con->servername, sizeof(con->servername));
+
       con->serverport = ntohs(lis->address.ipv6.sin6_port);
     }
     else
 #endif /* AF_INET6 */
     if (temp.addr.sa_family == AF_INET)
     {
-      httpAddrLookup(&temp, con->servername, sizeof(con->servername));
+      if (httpAddrLocalhost(&temp))
+        strlcpy(con->servername, "localhost", sizeof(con->servername));
+      else if (HostNameLookups || RemotePort)
+        httpAddrLookup(&temp, con->servername, sizeof(con->servername));
+      else
+        httpAddrString(&temp, con->servername, sizeof(con->servername));
+
       con->serverport = ntohs(lis->address.ipv4.sin_port);
     }
     else
@@ -416,15 +423,10 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
 
   cupsArrayAdd(Clients, con);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdAcceptClient: %d connected to server on %s:%d",
-                  con->http.fd, con->servername, con->serverport);
-
  /*
-  * Using TCP_NODELAY improves responsiveness, especially on systems
-  * with a slow loopback interface...  Since we write large buffers
-  * when sending print files and requests, there shouldn't be any
-  * performance penalty for this...
+  * Using TCP_NODELAY improves responsiveness, especially on systems with a slow
+  * loopback interface.  Since we write large buffers when sending print files
+  * and requests there shouldn't be any performance penalty for this...
   */
 
   val = 1;
@@ -437,7 +439,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
   fcntl(con->http.fd, F_SETFD, fcntl(con->http.fd, F_GETFD) | FD_CLOEXEC);
 
  /*
-  * Add the socket to the select() input mask.
+  * Add the socket to the server select.
   */
 
   cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
@@ -481,10 +483,14 @@ cupsdCloseAllClients(void)
   cupsd_client_t       *con;           /* Current client */
 
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d",
+                  cupsArrayCount(Clients));
+
   for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
        con;
        con = (cupsd_client_t *)cupsArrayNext(Clients))
-    cupsdCloseClient(con);
+    if (cupsdCloseClient(con))
+      cupsdCloseClient(con);
 }
 
 
@@ -498,15 +504,12 @@ cupsdCloseClient(cupsd_client_t *con)     /* I - Client to close */
   int          partial;                /* Do partial close for SSL? */
 #ifdef HAVE_LIBSSL
   SSL_CTX      *context;               /* Context for encryption */
-  SSL          *conn;                  /* Connection for encryption */
   unsigned long        error;                  /* Error code */
 #elif defined(HAVE_GNUTLS)
-  http_tls_t   *conn;                  /* TLS connection information */
   int          error;                  /* Error code */
   gnutls_certificate_server_credentials *credentials;
                                        /* TLS credentials */
 #  elif defined(HAVE_CDSASSL)
-  http_tls_t   *conn;                  /* CDSA connection information */
 #endif /* HAVE_LIBSSL */
 
 
@@ -530,62 +533,56 @@ cupsdCloseClient(cupsd_client_t *con)     /* I - Client to close */
     partial = 1;
 
 #  ifdef HAVE_LIBSSL
-    conn    = (SSL *)(con->http.tls);
-    context = SSL_get_SSL_CTX(conn);
+    context = SSL_get_SSL_CTX(con->http.tls);
 
-    switch (SSL_shutdown(conn))
+    switch (SSL_shutdown(con->http.tls))
     {
       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;
     }
 
     SSL_CTX_free(context);
-    SSL_free(conn);
+    SSL_free(con->http.tls);
 
 #  elif defined(HAVE_GNUTLS)
-    conn        = (http_tls_t *)(con->http.tls);
-    credentials = (gnutls_certificate_server_credentials *)(conn->credentials);
+    credentials = (gnutls_certificate_server_credentials *)(con->http.tls_credentials);
 
-    error = gnutls_bye(conn->session, GNUTLS_SHUT_WR);
+    error = gnutls_bye(con->http.tls, GNUTLS_SHUT_WR);
     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;
     }
 
-    gnutls_deinit(conn->session);
+    gnutls_deinit(con->http.tls);
     gnutls_certificate_free_credentials(*credentials);
     free(credentials);
-    free(conn);
 
 #  elif defined(HAVE_CDSASSL)
-    conn = (http_tls_t *)(con->http.tls);
-
-    while (SSLClose(conn->session) == errSSLWouldBlock)
+    while (SSLClose(con->http.tls) == errSSLWouldBlock)
       usleep(1000);
 
-    SSLDisposeContext(conn->session);
+    SSLDisposeContext(con->http.tls);
 
-    if (conn->certsArray)
-      CFRelease(conn->certsArray);
+    if (con->http.tls_credentials)
+      CFRelease(con->http.tls_credentials);
 
-    free(conn);
 #  endif /* HAVE_LIBSSL */
 
     con->http.tls = NULL;
@@ -598,9 +595,6 @@ cupsdCloseClient(cupsd_client_t *con)       /* I - Client to close */
     * Stop any CGI process...
     */
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdCloseClient: %d Killing process ID %d...",
-                    con->http.fd, con->pipe_pid);
     cupsdEndProcess(con->pipe_pid, 1);
     con->pipe_pid = 0;
   }
@@ -609,10 +603,6 @@ cupsdCloseClient(cupsd_client_t *con)      /* I - Client to close */
   {
     cupsdRemoveSelect(con->file);
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdCloseClient: %d Closing data file %d.",
-                    con->http.fd, con->file);
-
     close(con->file);
     con->file = -1;
   }
@@ -657,6 +647,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);
@@ -752,15 +743,18 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
   status = HTTP_CONTINUE;
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdReadClient: %d, used=%d, file=%d state=%d",
-                  con->http.fd, con->http.used, con->file, con->http.state);
-
-  if (con->http.error)
-  {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: http error seen...");
-    cupsdCloseClient(con);
-    return;
-  }
+                 "cupsdReadClient(con=%p(%d)) "
+                 "con->http.error=%d "
+                 "con->http.used=%d, "
+                 "con->http.state=%d "
+                 "con->data_encoding=HTTP_ENCODE_%s, "
+                 "con->data_remaining=" CUPS_LLFMT ", "
+                 "con->file=%d",
+                 con, con->http.fd, con->http.error, con->http.used,
+                 con->http.state,
+                 con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
+                     "CHUNKED" : "LENGTH",
+                 CUPS_LLCAST con->http.data_remaining, con->file);
 
 #ifdef HAVE_SSL
   if (con->auto_ssl)
@@ -779,8 +773,8 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
       */
 
       cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdReadClient: Saw first byte %02X, auto-negotiating SSL/TLS session...",
-                      buf[0] & 255);
+                      "cupsdReadClient: Saw first byte %02X, auto-negotiating "
+                     "SSL/TLS session...", buf[0] & 255);
 
       if (!encrypt_client(con))
         cupsdCloseClient(con);
@@ -799,8 +793,16 @@ cupsdReadClient(cupsd_client_t *con)       /* I - Client to read from */
 
         if (httpGets(line, sizeof(line) - 1, HTTP(con)) == NULL)
        {
-         cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                         "cupsdReadClient: httpGets returned EOF...");
+         if (con->http.error && con->http.error != EPIPE)
+           cupsdLogMessage(CUPSD_LOG_DEBUG,
+                           "cupsdReadClient: %d WAITING Closing for error %d "
+                           "(%s)", con->http.fd, con->http.error,
+                           strerror(con->http.error));
+         else
+           cupsdLogMessage(CUPSD_LOG_DEBUG,
+                           "cupsdReadClient: %d WAITING Closing on EOF",
+                           con->http.fd);
+
          cupsdCloseClient(con);
          return;
        }
@@ -856,7 +858,13 @@ cupsdReadClient(cupsd_client_t *con)       /* I - Client to read from */
        }
 
 #ifdef HAVE_GSSAPI
-        con->gss_have_creds = 0;
+        con->have_gss = 0;
+
+       if (con->gss_creds)
+       {
+         OM_uint32 minor_status;
+         gss_release_cred(&minor_status, &con->gss_creds);
+       }
 #endif /* HAVE_GSSAPI */
 
        /*
@@ -866,11 +874,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, CUPSD_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;
@@ -896,6 +907,9 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
              }
              else
              {
+               cupsdLogMessage(CUPSD_LOG_ERROR,
+                               "Unsupported request line \"%s\" from %s!",
+                               line, con->http.hostname);
                cupsdSendError(con, HTTP_NOT_SUPPORTED, CUPSD_AUTH_NONE);
                cupsdCloseClient(con);
                return;
@@ -909,7 +923,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
 
         if (strcmp(con->uri, "*"))
        {
-         char  method[HTTP_MAX_URI],   /* Method/scheme */
+         char  scheme[HTTP_MAX_URI],   /* Method/scheme */
                userpass[HTTP_MAX_URI], /* Username:password */
                hostname[HTTP_MAX_URI], /* Hostname */
                resource[HTTP_MAX_URI]; /* Resource path */
@@ -921,7 +935,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
          */
 
           httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
-                         method, sizeof(method),
+                         scheme, sizeof(scheme),
                          userpass, sizeof(userpass),
                          hostname, sizeof(hostname), &port,
                          resource, sizeof(resource));
@@ -931,10 +945,10 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          * address...
          */
 
-         if (strcmp(method, "file") &&
+         if (strcmp(scheme, "file") &&
              strcasecmp(hostname, ServerName) &&
              strcasecmp(hostname, "localhost") &&
-             !isdigit(hostname[0]))
+             !isdigit(hostname[0]) && hostname[0] != '[')
          {
           /*
            * Nope, we don't do proxies...
@@ -981,7 +995,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
          return;
        }
 
-        con->start     = time(NULL);
+        gettimeofday(&(con->start), NULL);
         con->operation = con->http.state;
 
         cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %s %s HTTP/%d.%d",
@@ -990,8 +1004,11 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
        con->http.status = HTTP_OK;
 
-        cupsArrayAdd(ActiveClients, con);
-       cupsdSetBusyState();
+        if (!cupsArrayFind(ActiveClients, con))
+       {
+         cupsArrayAdd(ActiveClients, con);
+          cupsdSetBusyState();
+        }
 
     case HTTP_OPTIONS :
     case HTTP_DELETE :
@@ -1005,12 +1022,21 @@ 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)
        {
+         if (con->http.error && con->http.error != EPIPE)
+           cupsdLogMessage(CUPSD_LOG_DEBUG,
+                           "cupsdReadClient: %d FIELDS Closing for error %d "
+                           "(%s)", con->http.fd, con->http.error,
+                           strerror(con->http.error));
+         else
+           cupsdLogMessage(CUPSD_LOG_DEBUG,
+                           "cupsdReadClient: %d FIELDS Closing on EOF",
+                           con->http.fd);
+
          cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE);
          cupsdCloseClient(con);
          return;
@@ -1018,6 +1044,17 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
        break;
 
     default :
+        if (!data_ready(con) && recv(con->http.fd, buf, 1, MSG_PEEK) < 1)
+       {
+        /*
+         * Connection closed...
+         */
+
+         cupsdLogMessage(CUPSD_LOG_DEBUG,
+                         "cupsdReadClient: %d Closing on EOF", con->http.fd);
+          cupsdCloseClient(con);
+         return;
+       }
         break; /* Anti-compiler-warning-code */
   }
 
@@ -1034,13 +1071,16 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
       * fields...
       */
 
-      if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ',')) != NULL)
+      if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
+                        ',')) != NULL)
         *ptr = '\0';
 
-      if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], ';')) != NULL)
+      if ((ptr = strchr(con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE],
+                        ';')) != NULL)
         *ptr = '\0';
 
-      if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE], "charset=")) != NULL)
+      if ((ptr = strstr(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
+                        "charset=")) != NULL)
       {
        /*
         * Combine language and charset, and trim any extra params in the
@@ -1054,12 +1094,8 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
          *ptr = '\0';
       }
       else
-        snprintf(locale, sizeof(locale), "%s.%s",
-                con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE], DefaultCharset);
-
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdReadClient: %d Browser asked for language \"%s\"...",
-                      con->http.fd, locale);
+        snprintf(locale, sizeof(locale), "%s.UTF-8",
+                con->http.fields[HTTP_FIELD_ACCEPT_LANGUAGE]);
 
       con->language = cupsLangGet(locale);
     }
@@ -1068,9 +1104,11 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
 
     cupsdAuthorize(con);
 
-    if (!strncmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive", 10) &&
-        KeepAlive)
+    if (!strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Keep-Alive",
+                    10) && KeepAlive)
       con->http.keep_alive = HTTP_KEEPALIVE_ON;
+    else if (!strncasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "close", 5))
+      con->http.keep_alive = HTTP_KEEPALIVE_OFF;
 
     if (!con->http.fields[HTTP_FIELD_HOST][0] &&
         con->http.version >= HTTP_1_1)
@@ -1079,6 +1117,24 @@ 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, CUPSD_AUTH_NONE))
+      {
+        cupsdLogMessage(CUPSD_LOG_ERROR, "Missing Host: field in request!");
+       cupsdCloseClient(con);
+       return;
+      }
+    }
+    else if (!valid_host(con))
+    {
+     /*
+      * Access to localhost must use "localhost" or the corresponding IPv4
+      * or IPv6 values in the Host: field.
+      */
+
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Request from \"%s\" using invalid Host: field \"%s\"",
+                     con->http.hostname, con->http.fields[HTTP_FIELD_HOST]);
+
       if (!cupsdSendError(con, HTTP_BAD_REQUEST, CUPSD_AUTH_NONE))
       {
        cupsdCloseClient(con);
@@ -1161,6 +1217,9 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
       * Protect against malicious users!
       */
 
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "Request for non-absolute resource \"%s\"!", con->uri);
+
       if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
       {
        cupsdCloseClient(con);
@@ -1210,9 +1269,6 @@ 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",
-                       con->uri);
        cupsdSendError(con, status, CUPSD_AUTH_NONE);
        cupsdCloseClient(con);
        return;
@@ -1239,7 +1295,8 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          * Send 417-expectation-failed header...
          */
 
-         if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL, CUPSD_AUTH_NONE))
+         if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL,
+                              CUPSD_AUTH_NONE))
          {
            cupsdCloseClient(con);
            return;
@@ -1282,14 +1339,57 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
                break;
              }
            }
+            else if ((!strncmp(con->uri, "/printers/", 10) ||
+                     !strncmp(con->uri, "/classes/", 9)) &&
+                    !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
+           {
+            /*
+             * Send icon file - get the real queue name since queue names are
+             * not case sensitive but filenames can be...
+             */
 
-           if ((!strncmp(con->uri, "/admin", 6) &&
-                strncmp(con->uri, "/admin/conf/", 12) &&
-                strncmp(con->uri, "/admin/log/", 11)) ||
-               !strncmp(con->uri, "/printers", 9) ||
-               !strncmp(con->uri, "/classes", 8) ||
-               !strncmp(con->uri, "/help", 5) ||
-               !strncmp(con->uri, "/jobs", 5))
+             con->uri[strlen(con->uri) - 4] = '\0';    /* Drop ".png" */
+
+              if (!strncmp(con->uri, "/printers/", 10))
+                p = cupsdFindPrinter(con->uri + 10);
+              else
+                p = cupsdFindClass(con->uri + 9);
+
+              if (p)
+               snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
+             else
+             {
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
+
+               break;
+             }
+           }
+
+            if (!WebInterface)
+           {
+            /*
+             * Web interface is disabled. Show an appropriate message...
+             */
+
+             if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
+             {
+               cupsdCloseClient(con);
+               return;
+             }
+
+             break;
+           }
+           else if ((!strncmp(con->uri, "/admin", 6) &&
+                     strncmp(con->uri, "/admin/conf/", 12) &&
+                     strncmp(con->uri, "/admin/log/", 11)) ||
+                    !strncmp(con->uri, "/printers", 9) ||
+                    !strncmp(con->uri, "/classes", 8) ||
+                    !strncmp(con->uri, "/help", 5) ||
+                    !strncmp(con->uri, "/jobs", 5))
            {
             /*
              * Send CGI output...
@@ -1365,10 +1465,13 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
                       strlen(con->uri) == 11)))
            {
             /*
-             * GET can only be done to configuration files under
+             * GET can only be done to configuration files directly under
              * /admin/conf...
              */
 
+             cupsdLogMessage(CUPSD_LOG_ERROR,
+                             "Request for subdirectory \"%s\"!", con->uri);
+
              if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
@@ -1450,10 +1553,6 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
            * so check the length against any limits that are set...
            */
 
-            cupsdLogMessage(CUPSD_LOG_DEBUG2, "POST %s", con->uri);
-           cupsdLogMessage(CUPSD_LOG_DEBUG2, "CONTENT_TYPE = %s",
-                           con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
-
             if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
                MaxRequestSize > 0 &&
                con->http.data_remaining > MaxRequestSize)
@@ -1495,6 +1594,20 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
            if (!strcmp(con->http.fields[HTTP_FIELD_CONTENT_TYPE],
                        "application/ipp"))
               con->request = ippNew();
+            else if (!WebInterface)
+           {
+            /*
+             * Web interface is disabled. Show an appropriate message...
+             */
+
+             if (!cupsdSendError(con, HTTP_WEBIF_DISABLED, CUPSD_AUTH_NONE))
+             {
+               cupsdCloseClient(con);
+               return;
+             }
+
+             break;
+           }
            else if ((!strncmp(con->uri, "/admin", 6) &&
                      strncmp(con->uri, "/admin/conf/", 12) &&
                      strncmp(con->uri, "/admin/log/", 11)) ||
@@ -1555,12 +1668,6 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
                  cupsdSetString(&con->options, NULL);
              }
 
-              cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                             "cupsdReadClient: %d command=\"%s\", "
-                             "options = \"%s\"",
-                             con->http.fd, con->command,
-                             con->options ? con->options : "(null)");
-
              if (con->http.version <= HTTP_1_0)
                con->http.keep_alive = HTTP_KEEPALIVE_OFF;
            }
@@ -1613,6 +1720,9 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * /admin/conf...
              */
 
+             cupsdLogMessage(CUPSD_LOG_ERROR,
+                             "Request for subdirectory \"%s\"!", con->uri);
+
              if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
@@ -1627,10 +1737,6 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
            * so check the length against any limits that are set...
            */
 
-            cupsdLogMessage(CUPSD_LOG_DEBUG2, "PUT %s", con->uri);
-           cupsdLogMessage(CUPSD_LOG_DEBUG2, "CONTENT_TYPE = %s",
-                           con->http.fields[HTTP_FIELD_CONTENT_TYPE]);
-
             if (con->http.fields[HTTP_FIELD_CONTENT_LENGTH][0] &&
                MaxRequestSize > 0 &&
                con->http.data_remaining > MaxRequestSize)
@@ -1683,10 +1789,6 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              }
            }
 
-            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);
@@ -1722,6 +1824,52 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
                break;
              }
            }
+            else if (!strncmp(con->uri, "/printers/", 10) &&
+                    !strcmp(con->uri + strlen(con->uri) - 4, ".png"))
+           {
+            /*
+             * Send PNG file - get the real printer name since printer
+             * names are not case sensitive but filenames can be...
+             */
+
+              con->uri[strlen(con->uri) - 4] = '\0';   /* Drop ".ppd" */
+
+              if ((p = cupsdFindPrinter(con->uri + 10)) != NULL)
+               snprintf(con->uri, sizeof(con->uri), "/icons/%s.png", p->name);
+             else
+             {
+               if (!cupsdSendError(con, HTTP_NOT_FOUND, CUPSD_AUTH_NONE))
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
+
+               break;
+             }
+           }
+           else if (!WebInterface)
+           {
+              if (!cupsdSendHeader(con, HTTP_OK, line, CUPSD_AUTH_NONE))
+             {
+               cupsdCloseClient(con);
+               return;
+             }
+
+             if (httpPrintf(HTTP(con), "\r\n") < 0)
+             {
+               cupsdCloseClient(con);
+               return;
+             }
+
+             if (cupsdFlushHeader(con) < 0)
+             {
+               cupsdCloseClient(con);
+               return;
+             }
+
+             con->http.state = HTTP_WAITING;
+             break;
+           }
 
            if ((!strncmp(con->uri, "/admin", 6) &&
                 strncmp(con->uri, "/admin/conf/", 12) &&
@@ -1767,6 +1915,9 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * /admin/conf...
              */
 
+             cupsdLogMessage(CUPSD_LOG_ERROR,
+                             "Request for subdirectory \"%s\"!", con->uri);
+
              if (!cupsdSendError(con, HTTP_FORBIDDEN, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
@@ -1778,7 +1929,8 @@ 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", CUPSD_AUTH_NONE))
+             if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html",
+                                  CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1859,18 +2011,20 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
   switch (con->http.state)
   {
     case HTTP_PUT_RECV :
-        cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                       "cupsdReadClient: %d con->data_encoding=HTTP_ENCODE_%s, "
-                       "con->data_remaining=" CUPS_LLFMT ", con->file=%d",
-                       con->http.fd,
-                       con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
-                           "CHUNKED" : "LENGTH",
-                       CUPS_LLCAST con->http.data_remaining, con->file);
-
         do
        {
           if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
          {
+           if (con->http.error && con->http.error != EPIPE)
+             cupsdLogMessage(CUPSD_LOG_DEBUG,
+                             "cupsdReadClient: %d PUT_RECV Closing for error "
+                             "%d (%s)", con->http.fd, con->http.error,
+                             strerror(con->http.error));
+           else
+             cupsdLogMessage(CUPSD_LOG_DEBUG,
+                             "cupsdReadClient: %d PUT_RECV Closing on EOF",
+                             con->http.fd);
+
            cupsdCloseClient(con);
            return;
          }
@@ -1878,20 +2032,12 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
          {
            con->bytes += bytes;
 
-            cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                           "cupsdReadClient: %d writing %d bytes to %d",
-                           con->http.fd, bytes, con->file);
-
             if (write(con->file, line, bytes) < bytes)
            {
               cupsdLogMessage(CUPSD_LOG_ERROR,
                              "cupsdReadClient: Unable to write %d bytes to %s: %s",
                              bytes, con->filename, strerror(errno));
 
-             cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                             "cupsdReadClient: Closing data file %d...",
-                             con->file);
-
              close(con->file);
              con->file = -1;
              unlink(con->filename);
@@ -1905,7 +2051,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)
        {
@@ -1915,12 +2061,6 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
 
          fstat(con->file, &filestats);
 
-          cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                         "cupsdReadClient: %d Closing data file %d, size="
-                         CUPS_LLFMT ".",
-                          con->http.fd, con->file,
-                         CUPS_LLCAST filestats.st_size);
-
          close(con->file);
          con->file = -1;
 
@@ -1931,9 +2071,6 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            * Request is too big; remove it and send an error...
            */
 
-            cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                           "cupsdReadClient: %d Removing temp file %s",
-                           con->http.fd, con->filename);
            unlink(con->filename);
            cupsdClearString(&con->filename);
 
@@ -1963,17 +2100,9 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
         break;
 
     case HTTP_POST_RECV :
-        cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                       "cupsdReadClient: %d con->data_encoding=HTTP_ENCODE_"
-                       "%s, con->data_remaining=" CUPS_LLFMT ", con->file=%d",
-                       con->http.fd,
-                       con->http.data_encoding == HTTP_ENCODE_CHUNKED ?
-                           "CHUNKED" : "LENGTH",
-                       CUPS_LLCAST con->http.data_remaining, con->file);
-
         do
        {
-          if (con->request)
+          if (con->request && con->file < 0)
          {
           /*
            * Grab any request data from the connection...
@@ -2001,7 +2130,14 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              break;
             }
            else
+           {
+             cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdReadClient: %d %d.%d %s %d",
+                             con->http.fd, con->request->request.op.version[0],
+                             con->request->request.op.version[1],
+                             ippOpString(con->request->request.op.operation_id),
+                             con->request->request.op.request_id);
              con->bytes += ippLength(con->request);
+           }
          }
 
           if (con->file < 0 && con->http.state != HTTP_POST_SEND)
@@ -2010,7 +2146,8 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            * Create a file as needed for the request data...
            */
 
-            cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
+            cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot,
+                           request_id ++);
            con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
 
            if (con->file < 0)
@@ -2026,9 +2163,6 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              }
            }
 
-            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);
@@ -2038,6 +2172,16 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
          {
             if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
            {
+             if (con->http.error && con->http.error != EPIPE)
+               cupsdLogMessage(CUPSD_LOG_DEBUG,
+                               "cupsdReadClient: %d POST_SEND Closing for "
+                               "error %d (%s)", con->http.fd, con->http.error,
+                               strerror(con->http.error));
+             else
+               cupsdLogMessage(CUPSD_LOG_DEBUG,
+                               "cupsdReadClient: %d POST_SEND Closing on EOF",
+                               con->http.fd);
+
              cupsdCloseClient(con);
              return;
            }
@@ -2045,26 +2189,20 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
            {
              con->bytes += bytes;
 
-              cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                             "cupsdReadClient: %d writing %d bytes to %d",
-                             con->http.fd, bytes, con->file);
-
               if (write(con->file, line, bytes) < bytes)
              {
                cupsdLogMessage(CUPSD_LOG_ERROR,
-                               "cupsdReadClient: Unable to write %d bytes to %s: %s",
-                               bytes, con->filename, strerror(errno));
-
-               cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                               "cupsdReadClient: Closing file %d...",
-                               con->file);
+                               "cupsdReadClient: Unable to write %d bytes to "
+                               "%s: %s", bytes, con->filename,
+                               strerror(errno));
 
                close(con->file);
                con->file = -1;
                unlink(con->filename);
                cupsdClearString(&con->filename);
 
-               if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
+               if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE,
+                                   CUPSD_AUTH_NONE))
                {
                  cupsdCloseClient(con);
                  return;
@@ -2075,12 +2213,15 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
               return;
            else if (con->http.state != HTTP_POST_SEND)
            {
+             cupsdLogMessage(CUPSD_LOG_DEBUG,
+                             "cupsdReadClient: %d Closing on unknown HTTP "
+                             "state %d", con->http.fd, con->http.state);
              cupsdCloseClient(con);
              return;
            }
          }
         }
-       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)
        {
@@ -2088,12 +2229,6 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
          {
            fstat(con->file, &filestats);
 
-            cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                           "cupsdReadClient: %d Closing data file %d, "
-                           "size=" CUPS_LLFMT ".",
-                            con->http.fd, con->file,
-                           CUPS_LLCAST filestats.st_size);
-
            close(con->file);
            con->file = -1;
 
@@ -2104,9 +2239,6 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              * Request is too big; remove it and send an error...
              */
 
-              cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                             "cupsdReadClient: %d Removing temp file %s",
-                             con->http.fd, con->filename);
              unlink(con->filename);
              cupsdClearString(&con->filename);
 
@@ -2126,6 +2258,15 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
                return;
              }
            }
+           else if (filestats.st_size == 0)
+           {
+            /*
+             * Don't allow empty file...
+             */
+
+             unlink(con->filename);
+             cupsdClearString(&con->filename);
+           }
 
            if (con->command)
            {
@@ -2148,9 +2289,6 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
            if (con->filename)
            {
-             cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                             "cupsdReadClient: %d Removing temp file %s",
-                             con->http.fd, con->filename);
              unlink(con->filename);
              cupsdClearString(&con->filename);
            }
@@ -2167,7 +2305,12 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
   if (con->http.state == HTTP_WAITING)
   {
     if (!con->http.keep_alive)
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                     "cupsdReadClient: %d Closing because Keep-Alive disabled",
+                     con->http.fd);
       cupsdCloseClient(con);
+    }
     else
     {
       cupsArrayRemove(ActiveClients, con);
@@ -2230,7 +2373,7 @@ cupsdSendCommand(
   con->sent_header = 0;
   con->file_ready  = 0;
   con->got_fields  = 0;
-  con->field_col   = 0;
+  con->header_used = 0;
 
   return (1);
 }
@@ -2245,6 +2388,10 @@ cupsdSendError(cupsd_client_t *con,      /* I - Connection */
                http_status_t  code,    /* I - Error code */
               int            auth_type)/* I - Authentication type */
 {
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "cupsdSendError(con=%p(%d), code=%d, auth_type=%d", con,
+                 con->http.fd, code, auth_type);
+
 #ifdef HAVE_SSL
  /*
   * Force client to upgrade for authentication if that is how the
@@ -2256,8 +2403,6 @@ cupsdSendError(cupsd_client_t *con,       /* I - Connection */
       strcasecmp(con->http.hostname, "localhost") &&
       !con->http.tls)
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdSendError: Encryption before authentication!");
     code = HTTP_UPGRADE_REQUIRED;
   }
 #endif /* HAVE_SSL */
@@ -2268,9 +2413,6 @@ cupsdSendError(cupsd_client_t *con,       /* I - Connection */
 
   cupsdLogRequest(con, code);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendError: %d code=%d (%s)",
-                  con->http.fd, code, httpStatus(code));
-
  /*
   * To work around bugs in some proxies, don't use Keep-Alive for some
   * error messages...
@@ -2343,27 +2485,31 @@ cupsdSendError(cupsd_client_t *con,     /* I - Connection */
               "CONTENT=\"3;URL=https://%s:%d%s\">\n",
               con->servername, con->serverport, con->uri);
     }
+    else if (code == HTTP_WEBIF_DISABLED)
+      text = _cupsLangString(con->language,
+                             _("The web interface is currently disabled. Run "
+                              "\"cupsctl WebInterface=yes\" to enable it."));
     else
       text = "";
 
     snprintf(message, sizeof(message),
-             "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" "
-            "\"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"
+             "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" "
+            "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
             "<HTML>\n"
             "<HEAD>\n"
              "\t<META HTTP-EQUIV=\"Content-Type\" "
             "CONTENT=\"text/html; charset=utf-8\">\n"
-            "\t<TITLE>%d %s</TITLE>\n"
+            "\t<TITLE>%s - " CUPS_SVERSION "</TITLE>\n"
             "\t<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" "
             "HREF=\"/cups.css\">\n"
             "%s"
             "</HEAD>\n"
              "<BODY>\n"
-            "<H1>%d %s</H1>\n"
+            "<H1>%s</H1>\n"
             "<P>%s</P>\n"
             "</BODY>\n"
             "</HTML>\n",
-            code, httpStatus(code), redirect, code, httpStatus(code), text);
+            httpStatus(code), redirect, httpStatus(code), text);
 
     if (httpPrintf(HTTP(con), "Content-Type: text/html; charset=utf-8\r\n") < 0)
       return (0);
@@ -2418,6 +2564,15 @@ cupsdSendHeader(
     return (httpPrintf(HTTP(con), "HTTP/%d.%d 100 Continue\r\n\r\n",
                       con->http.version / 100, con->http.version % 100) > 0);
   }
+  else if (code == HTTP_WEBIF_DISABLED)
+  {
+   /*
+    * Treat our special "web interface is disabled" status as "200 OK" for web
+    * browsers.
+    */
+
+    code = HTTP_OK;
+  }
 
   httpFlushWrite(HTTP(con));
 
@@ -2440,7 +2595,7 @@ cupsdSendHeader(
       return (0);
   }
   if (code == HTTP_METHOD_NOT_ALLOWED)
-    if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST\r\n") < 0)
+    if (httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0)
       return (0);
 
   if (code == HTTP_UNAUTHORIZED)
@@ -2465,39 +2620,58 @@ cupsdSendHeader(
       strlcpy(auth_str, "Negotiate", sizeof(auth_str));
 #endif /* HAVE_GSSAPI */
 
-#ifdef HAVE_AUTHORIZATION_H
-    if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE)
+    if (con->best && auth_type != CUPSD_AUTH_NEGOTIATE &&
+        !strcasecmp(con->http.hostname, "localhost"))
     {
-      int       i;                     /* Looping var */
-      char     *auth_key;              /* Auth key buffer */
-      size_t   auth_size;              /* Size of remaining buffer */
+     /*
+      * Add a "trc" (try root certification) parameter for local non-Kerberos
+      * requests when the request requires system group membership - then the
+      * client knows the root certificate can/should be used.
+      *
+      * Also, for Mac OS X we also look for @AUTHKEY and add an "authkey"
+      * parameter as needed...
+      */
 
+      char     *name,                  /* Current user name */
+               *auth_key;              /* Auth key buffer */
+      size_t   auth_size;              /* Size of remaining buffer */
 
       auth_key  = auth_str + strlen(auth_str);
       auth_size = sizeof(auth_str) - (auth_key - auth_str);
 
-      for (i = 0; i < con->best->num_names; i ++)
+      for (name = (char *)cupsArrayFirst(con->best->names);
+           name;
+          name = (char *)cupsArrayNext(con->best->names))
       {
-       if (!strncasecmp(con->best->names[i], "@AUTHKEY(", 9))
+#ifdef HAVE_AUTHORIZATION_H
+       if (!strncasecmp(name, "@AUTHKEY(", 9))
        {
-         snprintf(auth_key, auth_size, ", authkey=\"%s\"",
-                  con->best->names[i] + 9);
+         snprintf(auth_key, auth_size, ", authkey=\"%s\"", name + 9);
          /* end parenthesis is stripped in conf.c */
          break;
         }
-       else if (!strcasecmp(con->best->names[i], "@SYSTEM") &&
-                SystemGroupAuthKey)
+       else
+#endif /* HAVE_AUTHORIZATION_H */
+       if (!strcasecmp(name, "@SYSTEM"))
        {
-         snprintf(auth_key, auth_size, ", authkey=\"%s\"", SystemGroupAuthKey);
+#ifdef HAVE_AUTHORIZATION_H
+         if (SystemGroupAuthKey)
+           snprintf(auth_key, auth_size,
+                    ", authkey=\"%s\", trc=\"y\"",
+                    SystemGroupAuthKey);
+          else
+#else
+         strlcpy(auth_key, ", trc=\"y\"", auth_size);
+#endif /* HAVE_AUTHORIZATION_H */
          break;
        }
       }
     }
-#endif /* HAVE_AUTHORIZATION_H */
 
     if (auth_str[0])
     {
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdSendHeader: WWW-Authenticate: %s",
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                      "cupsdSendHeader: %d WWW-Authenticate: %s", con->http.fd,
                       auth_str);
 
       if (httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
@@ -2601,8 +2775,13 @@ cupsdUpdateCGI(void)
 
   while ((ptr = cupsdStatBufUpdate(CGIStatusBuffer, &loglevel,
                                    message, sizeof(message))) != NULL)
+  {
+    if (loglevel == CUPSD_LOG_INFO)
+      cupsdLogMessage(CUPSD_LOG_INFO, "%s", message);
+
     if (!strchr(CGIStatusBuffer->buffer, '\n'))
       break;
+  }
 
   if (ptr == NULL && !CGIStatusBuffer->bufused)
   {
@@ -2624,24 +2803,34 @@ cupsdUpdateCGI(void)
 void
 cupsdWriteClient(cupsd_client_t *con)  /* I - Client connection */
 {
-  int          bytes;                  /* Number of bytes written */
-  char         buf[16385];             /* Data buffer */
-  char         *bufptr;                /* Pointer into buffer */
+  int          bytes,                  /* Number of bytes written */
+               field_col;              /* Current column */
+  char         *bufptr,                /* Pointer into buffer */
+               *bufend;                /* Pointer to end of buffer */
   ipp_state_t  ipp_state;              /* IPP state value */
 
 
-#ifdef DEBUG
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdWriteClient(con=%p) %d response=%p(%d), file=%d "
+                  "cupsdWriteClient(con=%p(%d)) response=%p(%d), file=%d "
                  "pipe_pid=%d state=%d",
                   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 &&
       con->http.state != HTTP_POST_SEND)
+  {
+   /*
+    * If we get called in the wrong state, then something went wrong with the
+    * connection and we need to shut it down...
+    */
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "cupsdWriteClient: %d Closing on unknown HTTP state %d",
+                   con->http.fd, con->http.state);
+    cupsdCloseClient(con);
     return;
+  }
 
   if (con->pipe_pid)
   {
@@ -2670,11 +2859,10 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
     bytes     = ipp_state != IPP_ERROR &&
                 (con->file >= 0 || ipp_state != IPP_DATA);
   }
-  else if ((bytes = read(con->file, buf, sizeof(buf) - 1)) > 0)
+  else if ((bytes = read(con->file, con->header,
+                        sizeof(con->header) - con->header_used)) > 0)
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdWriteClient: Read %d bytes from file %d...",
-                    bytes, con->file);
+    con->header_used += bytes;
 
     if (con->pipe_pid && !con->got_fields)
     {
@@ -2682,20 +2870,22 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
       * Inspect the data for Content-Type and other fields.
       */
 
-      buf[bytes] = '\0';
-
-      for (bufptr = buf; !con->got_fields && *bufptr; bufptr ++)
+      for (bufptr = con->header, bufend = con->header + con->header_used,
+               field_col = 0;
+           !con->got_fields && bufptr < bufend;
+          bufptr ++)
+      {
         if (*bufptr == '\n')
        {
         /*
          * Send line to client...
          */
 
-         if (bufptr > buf && bufptr[-1] == '\r')
+         if (bufptr > con->header && bufptr[-1] == '\r')
            bufptr[-1] = '\0';
          *bufptr++ = '\0';
 
-          cupsdLogMessage(CUPSD_LOG_DEBUG2, "Script header: %s", buf);
+          cupsdLogMessage(CUPSD_LOG_DEBUG, "Script header: %s", con->header);
 
           if (!con->sent_header)
          {
@@ -2703,7 +2893,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
            * Handle redirection and CGI status codes...
            */
 
-            if (!strncasecmp(buf, "Location:", 9))
+            if (!strncasecmp(con->header, "Location:", 9))
            {
              cupsdSendHeader(con, HTTP_SEE_OTHER, NULL, CUPSD_AUTH_NONE);
              con->sent_header = 2;
@@ -2711,9 +2901,10 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
              if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
                return;
            }
-           else if (!strncasecmp(buf, "Status:", 7))
+           else if (!strncasecmp(con->header, "Status:", 7))
            {
-             cupsdSendError(con, (http_status_t)atoi(buf + 7), CUPSD_AUTH_NONE);
+             cupsdSendError(con, (http_status_t)atoi(con->header + 7),
+                            CUPSD_AUTH_NONE);
              con->sent_header = 2;
            }
            else
@@ -2729,25 +2920,25 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
             }
          }
 
-         if (strncasecmp(buf, "Status:", 7))
-           httpPrintf(HTTP(con), "%s\r\n", buf);
-
-         cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWriteClient: %d %s",
-                         con->http.fd, buf);
+         if (strncasecmp(con->header, "Status:", 7))
+           httpPrintf(HTTP(con), "%s\r\n", con->header);
 
          /*
          * Update buffer...
          */
 
-         bytes -= (bufptr - buf);
-         memmove(buf, bufptr, bytes + 1);
-         bufptr = buf - 1;
+         con->header_used -= bufptr - con->header;
+
+         if (con->header_used > 0)
+           memmove(con->header, bufptr, con->header_used);
+
+         bufptr = con->header - 1;
 
          /*
          * See if the line was empty...
          */
 
-         if (con->field_col == 0)
+         if (field_col == 0)
          {
            con->got_fields = 1;
 
@@ -2761,66 +2952,67 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
              con->http.data_encoding = HTTP_ENCODE_CHUNKED;
           }
          else
-           con->field_col = 0;
+           field_col = 0;
        }
        else if (*bufptr != '\r')
-         con->field_col ++;
-
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdWriteClient: %d bytes=%d, got_fields=%d",
-                      con->http.fd, bytes, con->got_fields);
+         field_col ++;
+      }
 
-      if (bytes > 0 && !con->got_fields)
+      if (!con->got_fields)
       {
-       /*
-        * Remaining text needs to go out...
-       */
-
-        httpPrintf(HTTP(con), "%s", buf);
-
         con->http.activity = time(NULL);
         return;
       }
-      else if (bytes == 0)
-        con->http.activity = time(NULL);
     }
 
-    if (bytes > 0)
+    if (con->header_used > 0)
     {
-      if (httpWrite2(HTTP(con), buf, bytes) < 0)
+      if (httpWrite2(HTTP(con), con->header, con->header_used) < 0)
       {
-       cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                       "cupsdWriteClient: %d Write of %d bytes failed!",
-                       con->http.fd, bytes);
-
+       cupsdLogMessage(CUPSD_LOG_DEBUG,
+                       "cupsdWriteClient: %d Closing for error %d (%s)",
+                       con->http.fd, con->http.error,
+                       strerror(con->http.error));
        cupsdCloseClient(con);
        return;
       }
 
-      httpFlushWrite(HTTP(con));
+      if (con->http.data_encoding == HTTP_ENCODE_CHUNKED)
+        httpFlushWrite(HTTP(con));
 
-      con->bytes += bytes;
+      con->bytes += con->header_used;
 
       if (con->http.state == HTTP_WAITING)
        bytes = 0;
+      else
+        bytes = con->header_used;
+
+      con->header_used = 0;
     }
   }
 
-  if (bytes <= 0)
+  if (bytes <= 0 ||
+      (con->http.state != HTTP_GET_SEND && con->http.state != HTTP_POST_SEND))
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWriteClient: %d bytes < 0",
-                    con->http.fd);
-
-    cupsdLogRequest(con, HTTP_OK);
+    if (!con->sent_header && con->pipe_pid)
+      cupsdSendError(con, HTTP_SERVER_ERROR, CUPSD_AUTH_NONE);
+    else
+    {
+      cupsdLogRequest(con, HTTP_OK);
 
-    httpFlushWrite(HTTP(con));
+      httpFlushWrite(HTTP(con));
 
-    if (con->http.data_encoding == HTTP_ENCODE_CHUNKED && con->sent_header == 1)
-    {
-      if (httpWrite2(HTTP(con), "", 0) < 0)
+      if (con->http.data_encoding == HTTP_ENCODE_CHUNKED && con->sent_header == 1)
       {
-        cupsdCloseClient(con);
-       return;
+       if (httpWrite2(HTTP(con), "", 0) < 0)
+       {
+         cupsdLogMessage(CUPSD_LOG_DEBUG,
+                         "cupsdWriteClient: %d Closing for error %d (%s)",
+                         con->http.fd, con->http.error,
+                         strerror(con->http.error));
+         cupsdCloseClient(con);
+         return;
+       }
       }
     }
 
@@ -2835,10 +3027,6 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
       if (con->pipe_pid)
        cupsdEndProcess(con->pipe_pid, 0);
 
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdWriteClient: %d Closing data file %d.",
-                      con->http.fd, con->file);
-
       close(con->file);
       con->file     = -1;
       con->pipe_pid = 0;
@@ -2846,9 +3034,6 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
 
     if (con->filename)
     {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdWriteClient: %d Removing temp file %s",
-                      con->http.fd, con->filename);
       unlink(con->filename);
       cupsdClearString(&con->filename);
     }
@@ -2871,9 +3056,17 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
 
     if (!con->http.keep_alive)
     {
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                     "cupsdWriteClient: %d Closing because Keep-Alive disabled",
+                     con->http.fd);
       cupsdCloseClient(con);
       return;
     }
+    else
+    {
+      cupsArrayRemove(ActiveClients, con);
+      cupsdSetBusyState();
+    }
   }
 
   con->http.activity = time(NULL);
@@ -2902,8 +3095,10 @@ check_if_modified(
     return (1);
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "check_if_modified: %d If-Modified-Since=\"%s\"",
-                  con->http.fd, ptr);
+                  "check_if_modified(con=%p(%d), "
+                 "filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"",
+                  con, con->http.fd, filestats, CUPS_LLCAST filestats->st_size,
+                 (int)filestats->st_mtime, ptr);
 
   while (*ptr != '\0')
   {
@@ -2928,13 +3123,6 @@ check_if_modified(
       ptr ++;
   }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "check_if_modified: %d sizes=" CUPS_LLFMT ","
-                 CUPS_LLFMT " dates=%d,%d",
-                  con->http.fd, CUPS_LLCAST size,
-                 CUPS_LLCAST filestats->st_size, (int)date,
-                 (int)filestats->st_mtime);
-
   return ((size != filestats->st_size && size != 0) ||
           (date < filestats->st_mtime && date != 0) ||
          (size == 0 && date == 0));
@@ -2961,6 +3149,37 @@ compare_clients(cupsd_client_t *a,       /* I - First client */
 }
 
 
+/*
+ * '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(con->http.tls))
+      return (1);
+#  elif defined(HAVE_CDSASSL)
+    size_t bytes;                      /* Bytes that are available */
+
+    if (!SSLGetBufferedReadSize(con->http.tls, &bytes) && bytes > 0)
+      return (1);
+#  endif /* HAVE_LIBSSL */
+  }
+#endif /* HAVE_SSL */
+
+  return (0);
+}
+
+
 #ifdef HAVE_SSL
 /*
  * 'encrypt_client()' - Enable encryption for the client...
@@ -2971,11 +3190,13 @@ encrypt_client(cupsd_client_t *con)     /* I - Client to encrypt */
 {
 #  ifdef HAVE_LIBSSL
   SSL_CTX      *context;               /* Context for encryption */
-  SSL          *conn;                  /* Connection for encryption */
   BIO          *bio;                   /* BIO data */
   unsigned long        error;                  /* Error code */
 
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
+                  con->http.fd);
+
  /*
   * Verify that we have a certificate...
   */
@@ -2997,44 +3218,45 @@ encrypt_client(cupsd_client_t *con)     /* I - Client to encrypt */
   context = SSL_CTX_new(SSLv23_server_method());
 
   SSL_CTX_set_options(context, SSL_OP_NO_SSLv2); /* Only use SSLv3 or TLS */
+  if (SSLOptions & CUPSD_SSL_NOEMPTY)
+    SSL_CTX_set_options(context, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
   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);
 
   bio = BIO_new(_httpBIOMethods());
   BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
 
-  conn = SSL_new(context);
-  SSL_set_bio(conn, bio, bio);
+  con->http.tls = SSL_new(context);
+  SSL_set_bio(con->http.tls, bio, bio);
 
-  if (SSL_accept(conn) != 1)
+  if (SSL_accept(con->http.tls) != 1)
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "encrypt_client: Unable to encrypt connection from %s!",
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to encrypt connection from %s!",
                     con->http.hostname);
 
     while ((error = ERR_get_error()) != 0)
-      cupsdLogMessage(CUPSD_LOG_ERROR, "encrypt_client: %s",
-                      ERR_error_string(error, NULL));
+      cupsdLogMessage(CUPSD_LOG_ERROR, "%s", ERR_error_string(error, NULL));
 
     SSL_CTX_free(context);
-    SSL_free(conn);
+    SSL_free(con->http.tls);
+    con->http.tls = NULL;
     return (0);
   }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG,
-                  "encrypt_client: %d Connection from %s now encrypted.",
-                  con->http.fd, con->http.hostname);
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
+                  con->http.hostname);
 
-  con->http.tls = conn;
   return (1);
 
 #  elif defined(HAVE_GNUTLS)
-  http_tls_t   *conn;                  /* TLS session object */
   int          error;                  /* Error code */
   gnutls_certificate_server_credentials *credentials;
                                        /* TLS credentials */
 
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
+                  con->http.fd);
+
  /*
   * Verify that we have a certificate...
   */
@@ -3053,21 +3275,14 @@ encrypt_client(cupsd_client_t *con)     /* I - Client to encrypt */
   * Create the SSL object and perform the SSL handshake...
   */
 
-  conn = (http_tls_t *)malloc(sizeof(http_tls_t));
-
-  if (conn == NULL)
-    return (0);
-
   credentials = (gnutls_certificate_server_credentials *)
                     malloc(sizeof(gnutls_certificate_server_credentials));
   if (credentials == NULL)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "encrypt_client: Unable to encrypt connection from %s!",
-                    con->http.hostname);
-    cupsdLogMessage(CUPSD_LOG_ERROR, "encrypt_client: %s", strerror(errno));
+                    "Unable to encrypt connection from %s - %s",
+                    con->http.hostname, strerror(errno));
 
-    free(conn);
     return (0);
   }
 
@@ -3075,88 +3290,82 @@ encrypt_client(cupsd_client_t *con)     /* I - Client to encrypt */
   gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
                                       ServerKey, GNUTLS_X509_FMT_PEM);
 
-  gnutls_init(&(conn->session), GNUTLS_SERVER);
-  gnutls_set_default_priority(conn->session);
-  gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials);
-  gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr)HTTP(con));
-  gnutls_transport_set_pull_function(conn->session, _httpReadGNUTLS);
-  gnutls_transport_set_push_function(conn->session, _httpWriteGNUTLS);
+  gnutls_init(&con->http.tls), GNUTLS_SERVER);
+  gnutls_set_default_priority(con->http.tls);
+  gnutls_credentials_set(con->http.tls, GNUTLS_CRD_CERTIFICATE, *credentials);
+  gnutls_transport_set_ptr(con->http.tls, (gnutls_transport_ptr)HTTP(con));
+  gnutls_transport_set_pull_function(con->http.tls, _httpReadGNUTLS);
+  gnutls_transport_set_push_function(con->http.tls, _httpWriteGNUTLS);
 
-  error = gnutls_handshake(conn->session);
+  error = gnutls_handshake(con->http.tls);
 
   if (error != GNUTLS_E_SUCCESS)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "encrypt_client: Unable to encrypt connection from %s!",
-                    con->http.hostname);
-    cupsdLogMessage(CUPSD_LOG_ERROR, "encrypt_client: %s",
-                    gnutls_strerror(error));
+                    "Unable to encrypt connection from %s - %s",
+                    con->http.hostname, gnutls_strerror(error));
 
-    gnutls_deinit(conn->session);
+    gnutls_deinit(con->http.tls);
     gnutls_certificate_free_credentials(*credentials);
-    free(conn);
+    con->http.tls = NULL;
     free(credentials);
     return (0);
   }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG,
-                  "encrypt_client: %d Connection from %s now encrypted.",
-                  con->http.fd, con->http.hostname);
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
+                  con->http.hostname);
 
-  conn->credentials = credentials;
-  con->http.tls = conn;
+  con->http.tls_credentials = credentials;
   return (1);
 
 #  elif defined(HAVE_CDSASSL)
   OSStatus     error;                  /* Error code */
-  http_tls_t   *conn;                  /* CDSA connection information */
 
 
-  if ((conn = (http_tls_t *)malloc(sizeof(http_tls_t))) == NULL)
-    return (0);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "encrypt_client(con=%p(%d))", con,
+                  con->http.fd);
 
-  error            = 0;
-  conn->session    = NULL;
-  conn->certsArray = get_cdsa_certificate(con);
+  error                     = 0;
+  con->http.tls_credentials = get_cdsa_certificate(con);
 
-  if (!conn->certsArray)
+  if (!con->http.tls_credentials)
   {
    /*
     * No keychain (yet), make a self-signed certificate...
     */
 
     if (make_certificate(con))
-      conn->certsArray = get_cdsa_certificate(con);
+      con->http.tls_credentials = get_cdsa_certificate(con);
   }
 
-  if (!conn->certsArray)
+  if (!con->http.tls_credentials)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                   "encrypt_client: Could not find signing key in keychain "
-                   "\"%s\"", ServerCertificate);
+                   "Could not find signing key in keychain \"%s\"",
+                   ServerCertificate);
     error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */
   }
 
   if (!error)
-    error = SSLNewContext(true, &conn->session);
+    error = SSLNewContext(true, &con->http.tls);
 
   if (!error)
-    error = SSLSetIOFuncs(conn->session, _httpReadCDSA, _httpWriteCDSA);
+    error = SSLSetIOFuncs(con->http.tls, _httpReadCDSA, _httpWriteCDSA);
 
   if (!error)
-    error = SSLSetProtocolVersionEnabled(conn->session, kSSLProtocol2, false);
+    error = SSLSetProtocolVersionEnabled(con->http.tls, kSSLProtocol2, false);
 
   if (!error)
-    error = SSLSetConnection(conn->session, HTTP(con));
+    error = SSLSetConnection(con->http.tls, HTTP(con));
 
   if (!error)
-    error = SSLSetAllowsExpiredCerts(conn->session, true);
+    error = SSLSetAllowsExpiredCerts(con->http.tls, true);
 
   if (!error)
-    error = SSLSetAllowsAnyRoot(conn->session, true);
+    error = SSLSetAllowsAnyRoot(con->http.tls, true);
 
   if (!error)
-    error = SSLSetCertificate(conn->session, conn->certsArray);
+    error = SSLSetCertificate(con->http.tls, con->http.tls_credentials);
 
   if (!error)
   {
@@ -3164,38 +3373,47 @@ encrypt_client(cupsd_client_t *con)     /* I - Client to encrypt */
     * Perform SSL/TLS handshake
     */
 
-    while ((error = SSLHandshake(conn->session)) == errSSLWouldBlock)
+    while ((error = SSLHandshake(con->http.tls)) == errSSLWouldBlock)
       usleep(1000);
   }
 
   if (error)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "encrypt_client: Unable to encrypt connection from %s!",
-                    con->http.hostname);
-
-    cupsdLogMessage(CUPSD_LOG_ERROR, "encrypt_client: %s (%d)",
-                    cssmErrorString(error), (int)error);
+                    "Unable to encrypt connection from %s - %s (%d)",
+                    con->http.hostname, cssmErrorString(error), (int)error);
 
     con->http.error  = error;
     con->http.status = HTTP_ERROR;
 
-    if (conn->session)
-      SSLDisposeContext(conn->session);
-
-    if (conn->certsArray)
-      CFRelease(conn->certsArray);
+    if (con->http.tls)
+    {
+      SSLDisposeContext(con->http.tls);
+      con->http.tls = NULL;
+    }
 
-    free(conn);
+    if (con->http.tls_credentials)
+    {
+      CFRelease(con->http.tls_credentials);
+      con->http.tls_credentials = NULL;
+    }
 
     return (0);
   }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG,
-                  "encrypt_client: %d Connection from %s now encrypted.",
-                  con->http.fd, con->http.hostname);
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "Connection from %s now encrypted.",
+                  con->http.hostname);
+
+  CFArrayRef           peerCerts;      /* Peer certificates */
+
+  if (!(error = SSLCopyPeerCertificates(con->http.tls, &peerCerts)) && peerCerts)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received %d peer certificates!",
+                   (int)CFArrayGetCount(peerCerts));
+  }
+  else
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "Received NO peer certificates!");
 
-  con->http.tls = conn;
   return (1);
 
 #  endif /* HAVE_LIBSSL */
@@ -3209,51 +3427,129 @@ encrypt_client(cupsd_client_t *con)    /* I - Client to encrypt */
  */
 
 static CFArrayRef                              /* O - Array of certificates */
-get_cdsa_certificate(cupsd_client_t *con)      /* I - Client connection */
+get_cdsa_certificate(
+    cupsd_client_t *con)                       /* I - Client connection */
 {
   OSStatus             err;            /* Error info */
-  SecKeychainRef       keychain;       /* Keychain reference */
-  SecIdentitySearchRef search;         /* Search reference */
-  SecIdentityRef       identity;       /* Identity */
+  SecKeychainRef       keychain = NULL;/* Keychain reference */
+  SecIdentitySearchRef search = NULL;  /* Search reference */
+  SecIdentityRef       identity = NULL;/* Identity */
   CFArrayRef           certificates = NULL;
                                        /* Certificate array */
+#  if HAVE_SECPOLICYCREATESSL
+  SecPolicyRef         policy = NULL;  /* Policy ref */
+  CFStringRef          servername = NULL;
+                                       /* Server name */
+  CFMutableDictionaryRef query = NULL; /* Query qualifiers */
+  char                 localname[1024];/* Local hostname */
+#  elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
+  SecPolicyRef         policy = NULL;  /* Policy ref */
+  SecPolicySearchRef   policy_search = NULL;
+                                       /* Policy search ref */
+  CSSM_DATA            options;        /* Policy options */
+  CSSM_APPLE_TP_SSL_OPTIONS
+                       ssl_options;    /* SSL Option for hostname */
+  char                 localname[1024];/* Local hostname */
+#  endif /* HAVE_SECPOLICYCREATESSL */
 
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG,
+                  "get_cdsa_certificate: Looking for certs for \"%s\"...",
+                 con->servername);
+
   if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\", %s",
-                   ServerCertificate, cssmErrorString(err));
-    return (NULL);
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\" - %s (%d)",
+                   ServerCertificate, cssmErrorString(err), (int)err);
+    goto cleanup;
+  }
+
+#  if HAVE_SECPOLICYCREATESSL
+  servername = CFStringCreateWithCString(kCFAllocatorDefault, con->servername,
+                                        kCFStringEncodingUTF8);
+
+  if ((policy = SecPolicyCreateSSL(1, servername)) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
+    goto cleanup;
   }
 
-#  if HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY
+  if (servername)
+    CFRelease(servername);
+
+  if (!(query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                         &kCFTypeDictionaryKeyCallBacks,
+                                         &kCFTypeDictionaryValueCallBacks)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create query dictionary");
+    goto cleanup;
+  }
+
+  CFDictionaryAddValue(query, kSecClass, kSecClassIdentity);
+  CFDictionaryAddValue(query, kSecMatchPolicy, policy);
+  CFDictionaryAddValue(query, kSecReturnRef, kCFBooleanTrue);
+  CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitOne);
+
+  err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
+
+  if (err && DNSSDHostName)
+  {
+   /*
+    * Search for the connection server name failed; try the DNS-SD .local
+    * hostname instead...
+    */
+
+    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "get_cdsa_certificate: Looking for certs for \"%s\"...",
+                   localname);
+
+    servername = CFStringCreateWithCString(kCFAllocatorDefault, localname,
+                                          kCFStringEncodingUTF8);
+  
+    CFRelease(policy);
+
+    if ((policy = SecPolicyCreateSSL(1, servername)) == NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create ssl policy reference");
+      goto cleanup;
+    }
+
+    if (servername)
+      CFRelease(servername);
+
+    CFDictionarySetValue(query, kSecMatchPolicy, policy);
+
+    err = SecItemCopyMatching(query, (CFTypeRef *)&identity);
+  }
+
+  if (err)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "Cannot find signing key in keychain \"%s\": %s (%d)",
+                   ServerCertificate, cssmErrorString(err), (int)err);
+    goto cleanup;
+  }
+
+#  elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
  /*
-  * Use a policy to search for valid certificates who's common name matches the
+  * Use a policy to search for valid certificates whose common name matches the
   * servername...
   */
 
-  SecPolicySearchRef   policy_search;  /* Policy search ref */
-  SecPolicyRef         policy;         /* Policy ref */
-  CSSM_DATA            options;        /* Policy options */
-  CSSM_APPLE_TP_SSL_OPTIONS
-                       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);
+    goto cleanup;
   }
 
-  if ((err = SecPolicySearchCopyNext(policy_search, &policy)))
+  if (SecPolicySearchCopyNext(policy_search, &policy))
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR, 
+    cupsdLogMessage(CUPSD_LOG_ERROR,
                    "Cannot find a policy to use for searching");
-    CFRelease(keychain);
-    CFRelease(policy_search);
-    return (NULL);
+    goto cleanup;
   }
 
   memset(&ssl_options, 0, sizeof(ssl_options));
@@ -3264,59 +3560,124 @@ 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, 
+    cupsdLogMessage(CUPSD_LOG_ERROR,
                    "Cannot set policy value to use for searching");
-    CFRelease(keychain);
-    CFRelease(policy_search);
-    return (NULL);
+    goto cleanup;
+  }
+
+  if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
+                                              keychain, FALSE, &search)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                   "Cannot create identity search reference: %s (%d)",
+                   cssmErrorString(err), (int)err);
+    goto cleanup;
+  }
+
+  err = SecIdentitySearchCopyNext(search, &identity);
+
+  if (err && DNSSDHostName)
+  {
+   /*
+    * Search for the connection server name failed; try the DNS-SD .local
+    * hostname instead...
+    */
+
+    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
+
+    ssl_options.ServerName    = localname;
+    ssl_options.ServerNameLen = strlen(localname);
+
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "get_cdsa_certificate: Looking for certs for \"%s\"...",
+                   localname);
+
+    if (SecPolicySetValue(policy, &options))
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "Cannot set policy value to use for searching");
+      goto cleanup;
+    }
+
+    CFRelease(search);
+    search = NULL;
+    if ((err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
+                                              keychain, FALSE, &search)))
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "Cannot create identity search reference: %s (%d)",
+                     cssmErrorString(err), (int)err);
+      goto cleanup;
+    }
+
+    err = SecIdentitySearchCopyNext(search, &identity);
+
+  }
+
+  if (err)
+  {
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "Cannot find signing key in keychain \"%s\": %s (%d)",
+                   ServerCertificate, cssmErrorString(err), (int)err);
+    goto cleanup;
   }
 
-  err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
-                                         keychain, FALSE, &search);
 #  else
  /*
   * Assume there is exactly one SecIdentity in the keychain...
   */
 
-  err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search);
-#  endif /* HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */
-
-  if (err)
+  if ((err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search)))
+  {
     cupsdLogMessage(CUPSD_LOG_DEBUG,
-                   "Cannot create keychain search reference: %s", 
-                   cssmErrorString(err));
-  else
+                   "Cannot create identity search reference (%d)", (int)err);
+    goto cleanup;
+  }
+
+  if ((err = SecIdentitySearchCopyNext(search, &identity)))
   {
-    if ((err = SecIdentitySearchCopyNext(search, &identity)))
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG,
-                       "Cannot find signing key in keychain \"%s\", error %d",
-                       ServerCertificate, (int)err);
-    }
-    else
-    {
-      if (CFGetTypeID(identity) != SecIdentityGetTypeID())
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "SecIdentitySearchCopyNext CFTypeID failure!");
-      else
-      {
-      if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 
-                                     1, &kCFTypeArrayCallBacks)) == NULL)
-       cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
-      }
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "Cannot find signing key in keychain \"%s\": %s (%d)",
+                   ServerCertificate, cssmErrorString(err), (int)err);
+    goto cleanup;
+  }
+#  endif /* HAVE_SECPOLICYCREATESSL */
 
-      CFRelease(identity);
-    }
+  if (CFGetTypeID(identity) != SecIdentityGetTypeID())
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "SecIdentity CFTypeID failure!");
+    goto cleanup;
+  }
 
-    CFRelease(search);
+  if ((certificates = CFArrayCreate(NULL, (const void **)&identity,
+                                 1, &kCFTypeArrayCallBacks)) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
+    goto cleanup;
   }
 
-#  if HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY
-  CFRelease(policy);
-  CFRelease(policy_search);
-#  endif /* HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */
+  cleanup :
+
+  if (keychain)
+    CFRelease(keychain);
+  if (search)
+    CFRelease(search);
+  if (identity)
+    CFRelease(identity);
+
+#  if HAVE_SECPOLICYCREATESSL
+  if (policy)
+    CFRelease(policy);
+  if (query)
+    CFRelease(query);
+#  elif defined(HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY)
+  if (policy)
+    CFRelease(policy);
+  if (policy_search)
+    CFRelease(policy_search);
+#  endif /* HAVE_SECPOLICYCREATESSL */
 
   return (certificates);
 }
@@ -3345,8 +3706,14 @@ get_file(cupsd_client_t *con,            /* I  - Client connection */
 
   language[0] = '\0';
 
-  if (!strncmp(con->uri, "/ppd/", 5))
+  if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/'))
     snprintf(filename, len, "%s%s", ServerRoot, con->uri);
+  else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/'))
+  {
+    snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7);
+    if (access(filename, F_OK) < 0)
+      snprintf(filename, len, "%s/images/generic.png", DocumentRoot);
+  }
   else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
     snprintf(filename, len, "%s/rss/%s", CacheDir, con->uri + 5);
   else if (!strncmp(con->uri, "/admin/conf/", 12))
@@ -3379,7 +3746,9 @@ get_file(cupsd_client_t *con,             /* I  - Client connection */
   */
 
   if ((status = stat(filename, filestats)) != 0 && language[0] &&
+      strncmp(con->uri, "/icons/", 7) &&
       strncmp(con->uri, "/ppd/", 5) &&
+      strncmp(con->uri, "/rss/", 5) &&
       strncmp(con->uri, "/admin/conf/", 12) &&
       strncmp(con->uri, "/admin/log/", 11))
   {
@@ -3497,12 +3866,10 @@ get_file(cupsd_client_t *con,           /* I  - Client connection */
     while (status && language[0]);
   }
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_file: %d filename=%s size=%d",
-                  con->http.fd, filename,
-                 status ? -1 : (int)filestats->st_size);
-
-  if (!status)
-    con->http.data_remaining = (int)filestats->st_size;
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "get_file(con=%p(%d), filestats=%p, filename=%p, len=%d) = "
+                 "%s", con, con->http.fd, filestats, filename, len,
+                 status ? "(null)" : filename);
 
   if (status)
     return (NULL);
@@ -3687,11 +4054,6 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
   const char   *options;               /* Options on URL */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "is_cgi(con=%p, filename=\"%s\", filestats=%p, type=%s/%s)\n",
-                 con, filename, filestats, type ? type->super : "unknown",
-                 type ? type->type : "unknown");
-
  /*
   * Get the options, if any...
   */
@@ -3708,7 +4070,11 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
 
   if (!type || strcasecmp(type->super, "application"))
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "is_cgi: Returning 0...");
+    cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                   "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
+                   "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
+                   type ? type->super : "unknown",
+                   type ? type->type : "unknown");
     return (0);
   }
 
@@ -3721,15 +4087,14 @@ is_cgi(cupsd_client_t *con,             /* I - Client connection */
 
     cupsdSetString(&con->command, filename);
 
-    filename = strrchr(filename, '/') + 1; /* Filename always absolute */
-
     if (options)
       cupsdSetStringf(&con->options, " %s", options);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
-                    con->command, con->options);
-
+                   "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
+                   "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
+                   type ? type->super : "unknown",
+                   type ? type->type : "unknown");
     return (1);
   }
 #ifdef HAVE_JAVA
@@ -3747,9 +4112,10 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
       cupsdSetStringf(&con->options, " %s", filename);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
-                    con->command, con->options);
-
+                   "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
+                   "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
+                   type ? type->super : "unknown",
+                   type ? type->type : "unknown");
     return (1);
   }
 #endif /* HAVE_JAVA */
@@ -3768,9 +4134,10 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
       cupsdSetStringf(&con->options, " %s", filename);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
-                    con->command, con->options);
-
+                   "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
+                   "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
+                   type ? type->super : "unknown",
+                   type ? type->type : "unknown");
     return (1);
   }
 #endif /* HAVE_PERL */
@@ -3789,9 +4156,10 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
       cupsdSetStringf(&con->options, " %s", filename);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
-                    con->command, con->options);
-
+                   "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
+                   "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
+                   type ? type->super : "unknown",
+                   type ? type->type : "unknown");
     return (1);
   }
 #endif /* HAVE_PHP */
@@ -3810,15 +4178,19 @@ is_cgi(cupsd_client_t *con,             /* I - Client connection */
       cupsdSetStringf(&con->options, " %s", filename);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "is_cgi: Returning 1 with command=\"%s\" and options=\"%s\"",
-                    con->command, con->options);
-
+                   "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
+                   "type=%s/%s) = 1", con, con->http.fd, filename, filestats,
+                   type ? type->super : "unknown",
+                   type ? type->type : "unknown");
     return (1);
   }
 #endif /* HAVE_PYTHON */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "is_cgi: Returning 0...");
-
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                 "is_cgi(con=%p(%d), filename=\"%s\", filestats=%p, "
+                 "type=%s/%s) = 0", con, con->http.fd, filename, filestats,
+                 type ? type->super : "unknown",
+                 type ? type->type : "unknown");
   return (0);
 }
 
@@ -3872,7 +4244,6 @@ make_certificate(cupsd_client_t *con)     /* I - Client connection */
   char         command[1024],          /* Command */
                *argv[12],              /* Command-line arguments */
                *envp[MAX_ENV + 1],     /* Environment variables */
-               home[1024],             /* HOME environment variable */
                infofile[1024],         /* Type-in information for cert */
                seedfile[1024];         /* Random number seed file */
   int          envc,                   /* Number of environment variables */
@@ -3913,8 +4284,6 @@ make_certificate(cupsd_client_t *con)     /* I - Client connection */
     cupsdLogMessage(CUPSD_LOG_INFO,
                     "Seeding the random number generator...");
 
-    snprintf(home, sizeof(home), "HOME=%s", TempDir);
-
    /*
     * Write the seed file...
     */
@@ -3943,11 +4312,10 @@ make_certificate(cupsd_client_t *con)   /* I - Client connection */
     argv[5] = NULL;
 
     envc = cupsdLoadEnv(envp, MAX_ENV);
-    envp[envc++] = home;
-    envp[envc]   = NULL;
+    envp[envc] = NULL;
 
     if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, NULL,
-                           &pid))
+                           NULL, &pid))
     {
       unlink(seedfile);
       return (0);
@@ -3960,7 +4328,7 @@ make_certificate(cupsd_client_t *con)     /* I - Client connection */
        break;
       }
 
-    cupsdFinishProcess(pid, command, sizeof(command));
+    cupsdFinishProcess(pid, command, sizeof(command), NULL);
 
    /*
     * Remove the seed file, as it is no longer needed...
@@ -4025,7 +4393,7 @@ 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, NULL,
-                         &pid))
+                         NULL, &pid))
   {
     close(infofd);
     unlink(infofile);
@@ -4042,7 +4410,7 @@ make_certificate(cupsd_client_t *con)     /* I - Client connection */
       break;
     }
 
-  cupsdFinishProcess(pid, command, sizeof(command));
+  cupsdFinishProcess(pid, command, sizeof(command), NULL);
 
   if (status)
   {
@@ -4209,11 +4577,21 @@ make_certificate(cupsd_client_t *con)   /* I - Client connection */
                *argv[4],               /* Command-line arguments */
                *envp[MAX_ENV + 1],     /* Environment variables */
                keychain[1024],         /* Keychain argument */
-               infofile[1024];         /* Type-in information for cert */
+               infofile[1024],         /* Type-in information for cert */
+               localname[1024],        /* Local hostname */
+               *servername;            /* Name of server in cert */
   cups_file_t  *fp;                    /* Seed/info file */
   int          infofd;                 /* Info file descriptor */
 
 
+  if (con->servername && isdigit(con->servername[0] & 255) && DNSSDHostName)
+  {
+    snprintf(localname, sizeof(localname), "%s.local", DNSSDHostName);
+    servername = localname;
+  }
+  else
+    servername = con->servername;
+       
  /*
   * Run the "certtool" command to generate a self-signed certificate...
   */
@@ -4240,8 +4618,8 @@ make_certificate(cupsd_client_t *con)     /* I - Client connection */
     return (0);
   }
 
-  cupsFilePrintf(fp, "%s\nr\n\ny\nb\ns\ny\n%s\n\n\n\n\n%s\ny\n", 
-                con->servername, con->servername, ServerAdmin);
+  cupsFilePrintf(fp, "%s\nr\n\ny\nb\ns\ny\n%s\n\n\n\n\n%s\ny\n",
+                servername, servername, ServerAdmin);
   cupsFileClose(fp);
 
   cupsdLogMessage(CUPSD_LOG_INFO,
@@ -4259,7 +4637,7 @@ 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, NULL,
-                         &pid))
+                         NULL, &pid))
   {
     close(infofd);
     unlink(infofile);
@@ -4276,7 +4654,7 @@ make_certificate(cupsd_client_t *con)     /* I - Client connection */
       break;
     }
 
-  cupsdFinishProcess(pid, command, sizeof(command));
+  cupsdFinishProcess(pid, command, sizeof(command), NULL);
 
   if (status)
   {
@@ -4330,7 +4708,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],         /* CUPSD_AUTH_TYPE environment variable */
+  char         auth_type[256],         /* 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 */
@@ -4346,10 +4724,7 @@ pipe_command(cupsd_client_t *con,        /* I - Client connection */
                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 */
+  void         *ccache = NULL;         /* Kerberos credentials */
 
 
  /*
@@ -4371,13 +4746,20 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
   */
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "pipe_command: command=\"%s\", options=\"%s\"",
-                  command, options ? options : "(null)");
+                  "pipe_command(con=%p(%d), infile=%d, outfile=%p, "
+                 "command=\"%s\", options=\"%s\", root=%d)",
+                  con, con->http.fd, infile, outfile, command,
+                 options ? options : "(null)", root);
 
   argv[0] = command;
 
   if (options)
-    strlcpy(argbuf, options, sizeof(argbuf));
+  {
+    commptr = options;
+    if (*commptr == ' ')
+      commptr ++;
+    strlcpy(argbuf, commptr, sizeof(argbuf));
+  }
   else
     argbuf[0] = '\0';
 
@@ -4476,7 +4858,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
 
   if (con->username[0])
   {
-    snprintf(auth_type, sizeof(auth_type), "CUPSD_AUTH_TYPE=%s",
+    snprintf(auth_type, sizeof(auth_type), "AUTH_TYPE=%s",
              httpGetField(HTTP(con), HTTP_FIELD_AUTHORIZATION));
 
     if ((uriptr = strchr(auth_type + 10, ' ')) != NULL)
@@ -4575,114 +4957,9 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
     */
 
 #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 */
-    }
+    if (con->gss_creds)
+      ccache = cupsdCopyKrb5Creds(con);
 #endif /* HAVE_GSSAPI */
-
   }
 
   if (con->http.version == HTTP_1_1)
@@ -4753,14 +5030,14 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
 
   envp[envc] = NULL;
 
-  if (LogLevel == CUPSD_LOG_DEBUG2)
+  if (LogLevel >= CUPSD_LOG_DEBUG)
   {
     for (i = 0; i < argc; i ++)
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "pipe_command: argv[%d] = \"%s\"", i, argv[i]);
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                      "[CGI] argv[%d] = \"%s\"", i, argv[i]);
     for (i = 0; i < envc; i ++)
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "pipe_command: envp[%d] = \"%s\"", i, envp[i]);
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                      "[CGI] envp[%d] = \"%s\"", i, envp[i]);
   }
 
  /*
@@ -4769,7 +5046,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
 
   if (cupsdOpenPipe(fds))
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create pipes for CGI %s - %s",
+    cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to create pipe for %s - %s",
                     argv[0], strerror(errno));
     return (0);
   }
@@ -4779,13 +5056,13 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
   */
 
   if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
-                       -1, -1, root, DefaultProfile, &pid) < 0)
+                       -1, -1, root, DefaultProfile, NULL, &pid) < 0)
   {
    /*
     * Error - can't fork!
     */
 
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to fork for CGI %s - %s", argv[0],
+    cupsdLogMessage(CUPSD_LOG_ERROR, "[CGI] Unable to start %s - %s", argv[0],
                     strerror(errno));
 
     cupsdClosePipe(fds);
@@ -4798,14 +5075,9 @@ pipe_command(cupsd_client_t *con,        /* I - Client connection */
     */
 
     if (con->username[0])
-#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] Started %s (PID %d)", command, pid);
 
     *outfile = fds[0];
     close(fds[1]);
@@ -4815,6 +5087,165 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
 }
 
 
+/*
+ * 'valid_host()' - Is the Host: field valid?
+ */
+
+static int                             /* O - 1 if valid, 0 if not */
+valid_host(cupsd_client_t *con)                /* I - Client connection */
+{
+  cupsd_alias_t        *a;                     /* Current alias */
+  cupsd_netif_t        *netif;                 /* Current network interface */
+  const char   *host,                  /* Host field */
+               *end;                   /* End character */
+
+
+  host = con->http.fields[HTTP_FIELD_HOST];
+
+  if (httpAddrLocalhost(con->http.hostaddr))
+  {
+   /*
+    * Only allow "localhost" or the equivalent IPv4 or IPv6 numerical
+    * addresses when accessing CUPS via the loopback interface...
+    */
+
+    return (!strcasecmp(host, "localhost") ||
+            !strncasecmp(host, "localhost:", 10) ||
+           !strcasecmp(host, "localhost.") ||
+            !strncasecmp(host, "localhost.:", 11) ||
+#ifdef __linux
+           !strcasecmp(host, "localhost.localdomain") ||
+            !strncasecmp(host, "localhost.localdomain:", 22) ||
+#endif /* __linux */
+            !strcmp(host, "127.0.0.1") ||
+           !strncmp(host, "127.0.0.1:", 10) ||
+           !strcmp(host, "[::1]") ||
+           !strncmp(host, "[::1]:", 6));
+  }
+
+#ifdef HAVE_DNSSD
+ /*
+  * Check if the hostname is something.local (Bonjour); if so, allow it.
+  */
+
+  if ((end = strrchr(host, '.')) != NULL &&
+      (!strcasecmp(end, ".local") || !strncasecmp(end, ".local:", 7) ||
+       !strcasecmp(end, ".local.") || !strncasecmp(end, ".local.:", 8)))
+    return (1);
+#endif /* HAVE_DNSSD */
+
+ /*
+  * Check if the hostname is an IP address...
+  */
+
+  if (isdigit(*host & 255) || *host == '[')
+  {
+   /*
+    * Possible IPv4/IPv6 address...
+    */
+
+    char       temp[1024],             /* Temporary string */
+               *ptr;                   /* Pointer into temporary string */
+    http_addrlist_t *addrlist;         /* List of addresses */
+
+
+    strlcpy(temp, host, sizeof(temp));
+    if ((ptr = strrchr(temp, ':')) != NULL && !strchr(ptr, ']'))
+      *ptr = '\0';                     /* Strip :port from host value */
+
+    if ((addrlist = httpAddrGetList(temp, AF_UNSPEC, NULL)) != NULL)
+    {
+     /*
+      * Good IPv4/IPv6 address...
+      */
+
+      httpAddrFreeList(addrlist);
+      return (1);
+    }
+  }
+
+ /*
+  * Check for (alias) name matches...
+  */
+
+  for (a = (cupsd_alias_t *)cupsArrayFirst(ServerAlias);
+       a;
+       a = (cupsd_alias_t *)cupsArrayNext(ServerAlias))
+  {
+   /*
+    * "ServerAlias *" allows all host values through...
+    */
+
+    if (!strcmp(a->name, "*"))
+      return (1);
+
+    if (!strncasecmp(host, a->name, a->namelen))
+    {
+     /*
+      * Prefix matches; check the character at the end - it must be ":", ".",
+      * ".:", or nul...
+      */
+
+      end = host + a->namelen;
+
+      if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
+        return (1);
+    }
+  }
+
+#ifdef HAVE_DNSSD
+  for (a = (cupsd_alias_t *)cupsArrayFirst(DNSSDAlias);
+       a;
+       a = (cupsd_alias_t *)cupsArrayNext(DNSSDAlias))
+  {
+   /*
+    * "ServerAlias *" allows all host values through...
+    */
+
+    if (!strcmp(a->name, "*"))
+      return (1);
+
+    if (!strncasecmp(host, a->name, a->namelen))
+    {
+     /*
+      * Prefix matches; check the character at the end - it must be ":", ".",
+      * ".:", or nul...
+      */
+
+      end = host + a->namelen;
+
+      if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
+        return (1);
+    }
+  }
+#endif /* HAVE_DNSSD */
+
+ /*
+  * Check for interface hostname matches...
+  */
+
+  for (netif = (cupsd_netif_t *)cupsArrayFirst(NetIFList);
+       netif;
+       netif = (cupsd_netif_t *)cupsArrayNext(NetIFList))
+  {
+    if (!strncasecmp(host, netif->hostname, netif->hostlen))
+    {
+     /*
+      * Prefix matches; check the character at the end - it must be ":", ".",
+      * ".:", or nul...
+      */
+
+      end = host + netif->hostlen;
+
+      if (!*end || *end == ':' || (*end == '.' && (!end[1] || end[1] == ':')))
+        return (1);
+    }
+  }
+
+  return (0);
+}
+
+
 /*
  * 'write_file()' - Send a file via HTTP.
  */
@@ -4828,8 +5259,10 @@ write_file(cupsd_client_t *con,          /* I - Client connection */
 {
   con->file = open(filename, O_RDONLY);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "write_file: %d file=%d", con->http.fd,
-                  con->file);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "write_file(con=%p(%d), code=%d, filename=\"%s\" (%d), "
+                 "type=\"%s\", filestats=%p)", con, con->http.fd,
+                 code, filename, con->file, type ? type : "(null)", filestats);
 
   if (con->file < 0)
     return (0);
@@ -4875,8 +5308,9 @@ write_file(cupsd_client_t *con,           /* I - Client connection */
 static void
 write_pipe(cupsd_client_t *con)                /* I - Client connection */
 {
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "write_pipe: CGI output on fd %d...",
-                  con->file);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "write_pipe(con=%p(%d)) CGI output on fd %d",
+                  con, con->http.fd, con->file);
 
   con->file_ready = 1;
 
@@ -4886,5 +5320,5 @@ write_pipe(cupsd_client_t *con)           /* I - Client connection */
 
 
 /*
- * End of "$Id: client.c 6999 2007-09-28 19:46:53Z mike $".
+ * End of "$Id: client.c 7950 2008-09-17 00:21:59Z mike $".
  */