]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/client.c
Remove all of the Subversion keywords from various source files.
[thirdparty/cups.git] / scheduler / client.c
index bc47f9c7aa3aa73f743c58934abfc2ebc284b04d..02e5fafb8c9e2afffff519f2ffa9ff67d7f0be0f 100644 (file)
@@ -1,9 +1,7 @@
 /*
- * "$Id$"
- *
  * Client routines for the CUPS scheduler.
  *
- * Copyright 2007-2013 by Apple Inc.
+ * Copyright 2007-2015 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * This file contains Kerberos support code, copyright 2006 by
@@ -21,7 +19,7 @@
  */
 
 #define _CUPS_NO_DEPRECATED
-//#define _HTTP_NO_PRIVATE
+#define _HTTP_NO_PRIVATE
 #include "cupsd.h"
 
 #ifdef __APPLE__
 #endif /* HAVE_TCPD_H */
 
 
-/*
- * Local globals...
- */
-
-static const char      * const ipp_states[] =
-                       {               /* IPP state strings */
-                         "IPP_IDLE",
-                         "IPP_HEADER",
-                         "IPP_ATTRIBUTE",
-                         "IPP_STATE_DATA"
-                       };
-
-
 /*
  * Local functions...
  */
@@ -53,8 +38,11 @@ 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);
+#ifdef HAVE_SSL
+static int             cupsd_start_tls(cupsd_client_t *con, http_encryption_t e);
+#endif /* HAVE_SSL */
 static char            *get_file(cupsd_client_t *con, struct stat *filestats,
-                                 char *filename, int len);
+                                 char *filename, size_t len);
 static http_status_t   install_cupsd_conf(cupsd_client_t *con);
 static int             is_cgi(cupsd_client_t *con, const char *filename,
                               struct stat *filestats, mime_type_t *type);
@@ -88,9 +76,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
 #endif /* HAVE_TCPD_H */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdAcceptClient(lis=%p(%d)) Clients=%d",
-                  lis, lis->fd, cupsArrayCount(Clients));
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAcceptClient(lis=%p(%d)) Clients=%d", lis, lis->fd, cupsArrayCount(Clients));
 
  /*
   * Make sure we don't have a full set of clients already...
@@ -155,23 +141,12 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
   * Save the connected address and port number...
   */
 
-  con->clientaddr = lis->address;
+  addrlen = sizeof(con->clientaddr);
 
-#if 0 /* ifdef AF_INET6 */
- /* FIXME: I don't believe this is recommended any longer, and we specifically
-  *        disable IPv4-over-IPv6 when we listen...
-  */
- /*
-  * Convert IPv4 over IPv6 addresses (::ffff:n.n.n.n) to IPv4 forms we
-  * can more easily use...
-  */
+  if (getsockname(httpGetFd(con->http), (struct sockaddr *)&con->clientaddr, &addrlen) || addrlen == 0)
+    con->clientaddr = lis->address;
 
-  if (lis->address.addr.sa_family == AF_INET6 &&
-      httpGetAddress(con->http)->ipv6.sin6_addr.s6_addr32[0] == 0 &&
-      httpGetAddress(con->http)->ipv6.sin6_addr.s6_addr32[1] == 0 &&
-      ntohl(httpGetAddress(con->http)->ipv6.sin6_addr.s6_addr32[2]) == 0xffff)
-    httpGetAddress(con->http)->ipv6.sin6_addr.s6_addr32[2] = 0;
-#endif /* AF_INET6 */
+  cupsdLogClient(con, CUPSD_LOG_DEBUG, "Server address is \"%s\".", httpAddrString(&con->clientaddr, name, sizeof(name)));
 
  /*
   * Check the number of clients on the same address...
@@ -300,17 +275,17 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
     char       peername[256];          /* Name of process */
 
     peersize = sizeof(peerpid);
-    if (!getsockopt(con->number, SOL_LOCAL, LOCAL_PEERPID, &peerpid,
+    if (!getsockopt(httpGetFd(con->http), SOL_LOCAL, LOCAL_PEERPID, &peerpid,
                     &peersize))
     {
-      if (!proc_name(peerpid, peername, sizeof(peername)))
+      if (!proc_name((int)peerpid, peername, sizeof(peername)))
        cupsdLogClient(con, CUPSD_LOG_DEBUG,
                       "Accepted from %s (Domain ???[%d])",
                        httpGetHostname(con->http, NULL, 0), (int)peerpid);
       else
        cupsdLogClient(con, CUPSD_LOG_DEBUG,
                        "Accepted from %s (Domain %s[%d])",
-                       httpGetHostname(con->http, NULL, 0), name, (int)peerpid);
+                       httpGetHostname(con->http, NULL, 0), peername, (int)peerpid);
     }
     else
 #  endif /* __APPLE__ */
@@ -390,7 +365,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
     * https connection; go secure...
     */
 
-    if (!cupsdStartTLS(con))
+    if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS))
       cupsdCloseClient(con);
   }
   else
@@ -409,8 +384,7 @@ cupsdCloseAllClients(void)
   cupsd_client_t       *con;           /* Current client */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d",
-                  cupsArrayCount(Clients));
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCloseAllClients() Clients=%d", cupsArrayCount(Clients));
 
   for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
        con;
@@ -428,10 +402,6 @@ int                                        /* O - 1 if partial close, 0 if fully closed */
 cupsdCloseClient(cupsd_client_t *con)  /* I - Client to close */
 {
   int          partial;                /* Do partial close for SSL? */
-#ifdef HAVE_LIBSSL
-#elif defined(HAVE_GNUTLS)
-#  elif defined(HAVE_CDSASSL)
-#endif /* HAVE_LIBSSL */
 
 
   cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing connection.");
@@ -514,7 +484,12 @@ cupsdCloseClient(cupsd_client_t *con)      /* I - Client to close */
 
     httpClose(con->http);
 
-    cupsdClearString(&con->filename);
+    if (con->filename)
+    {
+      unlink(con->filename);
+      cupsdClearString(&con->filename);
+    }
+
     cupsdClearString(&con->command);
     cupsdClearString(&con->options);
     cupsdClearString(&con->query_string);
@@ -566,22 +541,6 @@ cupsdCloseClient(cupsd_client_t *con)      /* I - Client to close */
 }
 
 
-/*
- * 'cupsdFlushHeader()' - Flush the header fields to the client.
- */
-
-int                                    /* I - Bytes written or -1 on error */
-cupsdFlushHeader(cupsd_client_t *con)  /* I - Client to flush to */
-{
-  int bytes = httpFlushWrite(con->http);
-
-  // TODO: Need to use httpSendResponse
-  con->http->data_encoding = HTTP_ENCODING_LENGTH;
-
-  return (bytes);
-}
-
-
 /*
  * 'cupsdReadClient()' - Read data from a client.
  */
@@ -590,11 +549,8 @@ void
 cupsdReadClient(cupsd_client_t *con)   /* I - Client to read from */
 {
   char                 line[32768],    /* Line from client... */
-                       operation[64],  /* Operation code from socket */
-                       version[64],    /* HTTP version number string */
                        locale[64],     /* Locale */
                        *ptr;           /* Pointer into strings */
-  int                  major, minor;   /* HTTP version numbers */
   http_status_t                status;         /* Transfer status */
   ipp_state_t          ipp_state;      /* State of IPP transfer */
   int                  bytes;          /* Number of bytes to POST */
@@ -608,22 +564,32 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
   status = HTTP_STATUS_CONTINUE;
 
-  cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                "cupsdReadClient "
-                "error=%d, "
-                "used=%d, "
-                "state=%s, "
-                "data_encoding=HTTP_ENCODING_%s, "
-                "data_remaining=" CUPS_LLFMT ", "
-                "request=%p(%s), "
-                "file=%d",
-                httpError(con->http), (int)httpGetReady(con->http),
-                httpStateString(httpGetState(con->http)),
-                httpIsChunked(con->http) ? "CHUNKED" : "LENGTH",
-                CUPS_LLCAST httpGetRemaining(con->http),
-                con->request,
-                con->request ? ipp_states[con->request->state] : "",
-                con->file);
+  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdReadClient: error=%d, used=%d, state=%s, data_encoding=HTTP_ENCODING_%s, data_remaining=" CUPS_LLFMT ", request=%p(%s), file=%d", httpError(con->http), (int)httpGetReady(con->http), httpStateString(httpGetState(con->http)), httpIsChunked(con->http) ? "CHUNKED" : "LENGTH", CUPS_LLCAST httpGetRemaining(con->http), con->request, con->request ? ippStateString(ippGetState(con->request)) : "", con->file);
+
+  if (httpGetState(con->http) == HTTP_STATE_GET_SEND ||
+      httpGetState(con->http) == HTTP_STATE_POST_SEND ||
+      httpGetState(con->http) == HTTP_STATE_STATUS)
+  {
+   /*
+    * If we get called in the wrong state, then something went wrong with the
+    * connection and we need to shut it down...
+    */
+
+    if (!httpGetReady(con->http) && recv(httpGetFd(con->http), buf, 1, MSG_PEEK) < 1)
+    {
+     /*
+      * Connection closed...
+      */
+
+      cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on EOF.");
+      cupsdCloseClient(con);
+      return;
+    }
+
+    cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP read state %s.", httpStateString(httpGetState(con->http)));
+    cupsdCloseClient(con);
+    return;
+  }
 
 #ifdef HAVE_SSL
   if (con->auto_ssl)
@@ -641,11 +607,9 @@ cupsdReadClient(cupsd_client_t *con)       /* I - Client to read from */
       * Encrypt this connection...
       */
 
-      cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                     "Saw first byte %02X, auto-negotiating "
-                    "SSL/TLS session.", buf[0] & 255);
+      cupsdLogClient(con, CUPSD_LOG_DEBUG2, "Saw first byte %02X, auto-negotiating SSL/TLS session.", buf[0] & 255);
 
-      if (!cupsdStartTLS(con))
+      if (cupsd_start_tls(con, HTTP_ENCRYPTION_ALWAYS))
         cupsdCloseClient(con);
 
       return;
@@ -660,15 +624,19 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         * See if we've received a request line...
        */
 
-        if (httpGets(line, sizeof(line) - 1, con->http) == NULL)
+        con->operation = httpReadRequest(con->http, con->uri, sizeof(con->uri));
+        if (con->operation == HTTP_STATE_ERROR ||
+           con->operation == HTTP_STATE_UNKNOWN_METHOD ||
+           con->operation == HTTP_STATE_UNKNOWN_VERSION)
        {
-         if (httpError(con->http) && httpError(con->http) != EPIPE)
+         if (httpError(con->http))
            cupsdLogClient(con, CUPSD_LOG_DEBUG,
                           "HTTP_STATE_WAITING Closing for error %d (%s)",
                           httpError(con->http), strerror(httpError(con->http)));
          else
            cupsdLogClient(con, CUPSD_LOG_DEBUG,
-                          "HTTP_STATE_WAITING Closing on EOF.");
+                          "HTTP_STATE_WAITING Closing on error: %s",
+                          cupsLastErrorString());
 
          cupsdCloseClient(con);
          return;
@@ -678,29 +646,19 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         * Ignore blank request lines...
        */
 
-        if (line[0] == '\0')
+        if (con->operation == HTTP_STATE_WAITING)
          break;
 
        /*
         * Clear other state variables...
        */
 
-        httpClearFields(con->http);
-
-        con->http->activity        = time(NULL);
-        con->http->version         = HTTP_VERSION_1_0;
-       httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
-       con->http->data_encoding   = HTTP_ENCODING_LENGTH;
-       con->http->data_remaining  = 0;
-       con->http->_data_remaining = 0;
-       con->operation            = HTTP_STATE_WAITING;
-       con->bytes                = 0;
-       con->file                 = -1;
-       con->file_ready           = 0;
-       con->pipe_pid             = 0;
-       con->username[0]          = '\0';
-       con->password[0]          = '\0';
-       con->uri[0]               = '\0';
+       con->bytes       = 0;
+       con->file        = -1;
+       con->file_ready  = 0;
+       con->pipe_pid    = 0;
+       con->username[0] = '\0';
+       con->password[0] = '\0';
 
        cupsdClearString(&con->command);
        cupsdClearString(&con->options);
@@ -729,59 +687,6 @@ cupsdReadClient(cupsd_client_t *con)       /* I - Client to read from */
        con->gss_uid  = 0;
 #endif /* HAVE_GSSAPI */
 
-       /*
-        * Grab the request line...
-       */
-
-        switch (sscanf(line, "%63s%1023s%63s", operation, con->uri, version))
-       {
-         case 1 :
-             if (line[0])
-             {
-               cupsdLogClient(con, CUPSD_LOG_ERROR,
-                               "Bad request line \"%s\" from %s.",
-                               _httpEncodeURI(buf, line, sizeof(buf)),
-                               httpGetHostname(con->http, NULL, 0));
-               cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE);
-               cupsdCloseClient(con);
-              }
-             return;
-         case 2 :
-             con->http->version = HTTP_VERSION_0_9;
-             break;
-         case 3 :
-             if (sscanf(version, "HTTP/%d.%d", &major, &minor) != 2)
-             {
-               cupsdLogClient(con, CUPSD_LOG_ERROR,
-                               "Bad request line \"%s\" from %s.",
-                               _httpEncodeURI(buf, line, sizeof(buf)),
-                               httpGetHostname(con->http, NULL, 0));
-               cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE);
-               cupsdCloseClient(con);
-               return;
-             }
-
-             if (major < 2)
-             {
-               con->http->version = (http_version_t)(major * 100 + minor);
-               if (con->http->version == HTTP_VERSION_1_1 && KeepAlive)
-                 httpSetKeepAlive(con->http, HTTP_KEEPALIVE_ON);
-               else
-                 httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
-             }
-             else
-             {
-               cupsdLogClient(con, CUPSD_LOG_ERROR,
-                               "Unsupported request line \"%s\" from %s.",
-                               _httpEncodeURI(buf, line, sizeof(buf)),
-                               httpGetHostname(con->http, NULL, 0));
-               cupsdSendError(con, HTTP_STATUS_NOT_SUPPORTED, CUPSD_AUTH_NONE);
-               cupsdCloseClient(con);
-               return;
-             }
-             break;
-       }
-
        /*
         * Handle full URLs in the request line...
        */
@@ -794,18 +699,24 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                resource[HTTP_MAX_URI]; /* Resource path */
           int  port;                   /* Port number */
 
-
          /*
          * Separate the URI into its components...
          */
 
-          httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
-                         scheme, sizeof(scheme),
-                         userpass, sizeof(userpass),
-                         hostname, sizeof(hostname), &port,
-                         resource, sizeof(resource));
+          if (httpSeparateURI(HTTP_URI_CODING_MOST, con->uri,
+                             scheme, sizeof(scheme),
+                             userpass, sizeof(userpass),
+                             hostname, sizeof(hostname), &port,
+                             resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
+          {
+           cupsdLogClient(con, CUPSD_LOG_ERROR, "Bad URI \"%s\" in request.",
+                           con->uri);
+           cupsdSendError(con, HTTP_STATUS_METHOD_NOT_ALLOWED, CUPSD_AUTH_NONE);
+           cupsdCloseClient(con);
+           return;
+         }
 
-         /*
+        /*
          * Only allow URIs with the servername, localhost, or an IP
          * address...
          */
@@ -813,6 +724,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
          if (strcmp(scheme, "file") &&
              _cups_strcasecmp(hostname, ServerName) &&
              _cups_strcasecmp(hostname, "localhost") &&
+             !cupsArrayFind(ServerAlias, hostname) &&
              !isdigit(hostname[0]) && hostname[0] != '[')
          {
           /*
@@ -838,37 +750,12 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         * Process the request...
        */
 
-        if (!strcmp(operation, "GET"))
-         con->http->state = HTTP_STATE_GET;
-        else if (!strcmp(operation, "PUT"))
-         con->http->state = HTTP_STATE_PUT;
-        else if (!strcmp(operation, "POST"))
-         con->http->state = HTTP_STATE_POST;
-        else if (!strcmp(operation, "DELETE"))
-         con->http->state = HTTP_STATE_DELETE;
-        else if (!strcmp(operation, "TRACE"))
-         con->http->state = HTTP_STATE_TRACE;
-        else if (!strcmp(operation, "OPTIONS"))
-         con->http->state = HTTP_STATE_OPTIONS;
-        else if (!strcmp(operation, "HEAD"))
-         con->http->state = HTTP_STATE_HEAD;
-       else
-       {
-         cupsdLogClient(con, CUPSD_LOG_ERROR, "Bad operation \"%s\".",
-                        operation);
-         cupsdSendError(con, HTTP_STATUS_BAD_REQUEST, CUPSD_AUTH_NONE);
-         cupsdCloseClient(con);
-         return;
-       }
-
         gettimeofday(&(con->start), NULL);
-        con->operation = httpGetState(con->http);
 
         cupsdLogClient(con, CUPSD_LOG_DEBUG, "%s %s HTTP/%d.%d",
-                      operation, con->uri, con->http->version / 100,
-                       con->http->version % 100);
-
-       con->http->status = HTTP_STATUS_OK;
+                      httpStateString(con->operation) + 11, con->uri,
+                      httpGetVersion(con->http) / 100,
+                       httpGetVersion(con->http) % 100);
 
         if (!cupsArrayFind(ActiveClients, con))
        {
@@ -925,6 +812,8 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
   * Handle new transfers...
   */
 
+  cupsdLogClient(con, CUPSD_LOG_DEBUG, "Read: status=%d", status);
+
   if (status == HTTP_STATUS_OK)
   {
     if (httpGetField(con->http, HTTP_FIELD_ACCEPT_LANGUAGE)[0])
@@ -1013,6 +902,8 @@ cupsdReadClient(cupsd_client_t *con)       /* I - Client to read from */
 
       if (con->best && con->best->type != CUPSD_AUTH_NONE)
       {
+        httpClearFields(con->http);
+
        if (!cupsdSendHeader(con, HTTP_STATUS_UNAUTHORIZED, NULL, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
@@ -1020,38 +911,28 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
        }
       }
 
-      if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), "Upgrade") &&
-         !httpIsEncrypted(con->http))
+      if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION), "Upgrade") && strstr(httpGetField(con->http, HTTP_FIELD_UPGRADE), "TLS/") != NULL && !httpIsEncrypted(con->http))
       {
 #ifdef HAVE_SSL
        /*
         * Do encryption stuff...
        */
 
+        httpClearFields(con->http);
+
        if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
        }
 
-       httpPrintf(con->http, "Connection: Upgrade\r\n");
-       httpPrintf(con->http, "Upgrade: TLS/1.2,TLS/1.1,TLS/1.0\r\n");
-       httpPrintf(con->http, "Content-Length: 0\r\n");
-       httpPrintf(con->http, "\r\n");
-
-       if (cupsdFlushHeader(con) < 0)
-        {
-         cupsdCloseClient(con);
-         return;
-       }
-
-        if (!cupsdStartTLS(con))
+        if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED))
         {
          cupsdCloseClient(con);
          return;
        }
 #else
-       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
+       if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1059,17 +940,12 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
 #endif /* HAVE_SSL */
       }
 
-      if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
-      {
-       cupsdCloseClient(con);
-       return;
-      }
+      httpClearFields(con->http);
+      httpSetField(con->http, HTTP_FIELD_ALLOW,
+                  "GET, HEAD, OPTIONS, POST, PUT");
+      httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0");
 
-      httpPrintf(con->http, "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
-      httpPrintf(con->http, "Content-Length: 0\r\n");
-      httpPrintf(con->http, "\r\n");
-
-      if (cupsdFlushHeader(con) < 0)
+      if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
       {
        cupsdCloseClient(con);
        return;
@@ -1093,13 +969,15 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
     else
     {
       if (!_cups_strcasecmp(httpGetField(con->http, HTTP_FIELD_CONNECTION),
-                            "Upgrade") && con->http->tls == NULL)
+                            "Upgrade") && !httpIsEncrypted(con->http))
       {
 #ifdef HAVE_SSL
        /*
         * Do encryption stuff...
        */
 
+        httpClearFields(con->http);
+
        if (!cupsdSendHeader(con, HTTP_STATUS_SWITCHING_PROTOCOLS, NULL,
                             CUPSD_AUTH_NONE))
        {
@@ -1107,24 +985,13 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
          return;
        }
 
-       httpPrintf(con->http, "Connection: Upgrade\r\n");
-       httpPrintf(con->http, "Upgrade: TLS/1.2,TLS/1.1,TLS/1.0\r\n");
-       httpPrintf(con->http, "Content-Length: 0\r\n");
-       httpPrintf(con->http, "\r\n");
-
-       if (cupsdFlushHeader(con) < 0)
-        {
-         cupsdCloseClient(con);
-         return;
-       }
-
-        if (!cupsdStartTLS(con))
+        if (cupsd_start_tls(con, HTTP_ENCRYPTION_REQUIRED))
         {
          cupsdCloseClient(con);
          return;
        }
 #else
-       if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
+       if (!cupsdSendError(con, HTTP_STATUS_NOT_IMPLEMENTED, CUPSD_AUTH_NONE))
        {
          cupsdCloseClient(con);
          return;
@@ -1139,16 +1006,16 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
        return;
       }
 
-      if (con->http->expect &&
+      if (httpGetExpect(con->http) &&
           (con->operation == HTTP_STATE_POST || con->operation == HTTP_STATE_PUT))
       {
-        if (con->http->expect == HTTP_STATUS_CONTINUE)
+        if (httpGetExpect(con->http) == HTTP_STATUS_CONTINUE)
        {
         /*
          * Send 100-continue header...
          */
 
-         if (!cupsdSendHeader(con, HTTP_STATUS_CONTINUE, NULL, CUPSD_AUTH_NONE))
+          if (httpWriteResponse(con->http, HTTP_STATUS_CONTINUE))
          {
            cupsdCloseClient(con);
            return;
@@ -1160,27 +1027,20 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
          * Send 417-expectation-failed header...
          */
 
-         if (!cupsdSendHeader(con, HTTP_STATUS_EXPECTATION_FAILED, NULL,
-                              CUPSD_AUTH_NONE))
-         {
-           cupsdCloseClient(con);
-           return;
-         }
-
-         httpPrintf(con->http, "Content-Length: 0\r\n");
-         httpPrintf(con->http, "\r\n");
+          httpClearFields(con->http);
+         httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0");
 
-         if (cupsdFlushHeader(con) < 0)
-          {
-           cupsdCloseClient(con);
-           return;
-         }
+         cupsdSendError(con, HTTP_STATUS_EXPECTATION_FAILED, CUPSD_AUTH_NONE);
+          cupsdCloseClient(con);
+          return;
        }
       }
 
       switch (httpGetState(con->http))
       {
        case HTTP_STATE_GET_SEND :
+            cupsdLogClient(con, CUPSD_LOG_DEBUG, "Processing GET %s", con->uri);
+
             if ((!strncmp(con->uri, "/ppd/", 5) ||
                 !strncmp(con->uri, "/printers/", 10) ||
                 !strncmp(con->uri, "/classes/", 9)) &&
@@ -1299,29 +1159,28 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
                break;
              }
            }
-           else if (!WebInterface)
-           {
-            /*
-             * Web interface is disabled. Show an appropriate message...
-             */
-
-             if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE))
-             {
-               cupsdCloseClient(con);
-               return;
-             }
 
-             break;
-           }
-
-           if ((!strncmp(con->uri, "/admin", 6) &&
-                 strncmp(con->uri, "/admin/conf/", 12) &&
-                 strncmp(con->uri, "/admin/log/", 11)) ||
+           if ((!strncmp(con->uri, "/admin", 6) && strcmp(con->uri, "/admin/conf/cupsd.conf") && 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))
            {
+             if (!WebInterface)
+             {
+              /*
+               * Web interface is disabled. Show an appropriate message...
+               */
+
+               if (!cupsdSendError(con, HTTP_STATUS_CUPS_WEBIF_DISABLED, CUPSD_AUTH_NONE))
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
+
+               break;
+             }
+
             /*
              * Send CGI output...
              */
@@ -1385,23 +1244,17 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
              else
                cupsdLogRequest(con, HTTP_STATUS_OK);
 
-             if (con->http->version <= HTTP_VERSION_1_0)
+             if (httpGetVersion(con->http) <= HTTP_VERSION_1_0)
                httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
            }
-            else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
-                     (strchr(con->uri + 12, '/') ||
-                      strlen(con->uri) == 12)) ||
-                    (!strncmp(con->uri, "/admin/log/", 11) &&
-                     (strchr(con->uri + 11, '/') ||
-                      strlen(con->uri) == 11)))
+            else if (!strncmp(con->uri, "/admin/log/", 11) && (strchr(con->uri + 11, '/') || strlen(con->uri) == 11))
            {
             /*
              * GET can only be done to configuration files directly under
              * /admin/conf...
              */
 
-             cupsdLogClient(con, CUPSD_LOG_ERROR,
-                             "Request for subdirectory \"%s\"!", con->uri);
+             cupsdLogClient(con, CUPSD_LOG_ERROR, "Request for subdirectory \"%s\".", con->uri);
 
              if (!cupsdSendError(con, HTTP_STATUS_FORBIDDEN, CUPSD_AUTH_NONE))
              {
@@ -1431,6 +1284,8 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
              type = mimeFileType(MimeDatabase, filename, NULL, NULL);
 
+              cupsdLogClient(con, CUPSD_LOG_DEBUG, "filename=\"%s\", type=%s/%s", filename, type ? type->super : "", type ? type->type : "");
+
               if (is_cgi(con, filename, &filestats, type))
              {
               /*
@@ -1449,7 +1304,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                else
                  cupsdLogRequest(con, HTTP_STATUS_OK);
 
-               if (con->http->version <= HTTP_VERSION_1_0)
+               if (httpGetVersion(con->http) <= HTTP_VERSION_1_0)
                  httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
                break;
              }
@@ -1486,7 +1341,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
             if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] &&
                MaxRequestSize > 0 &&
-               con->http->data_remaining > MaxRequestSize)
+               httpGetLength2(con->http) > MaxRequestSize)
            {
             /*
              * Request too large...
@@ -1500,9 +1355,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
              break;
             }
-           else if (con->http->data_remaining < 0 ||
-                    (!httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] &&
-                     con->http->data_encoding == HTTP_ENCODING_LENGTH))
+           else if (httpGetLength2(con->http) < 0)
            {
             /*
              * Negative content lengths are invalid!
@@ -1539,9 +1392,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
              break;
            }
-           else if ((!strncmp(con->uri, "/admin", 6) &&
-                     strncmp(con->uri, "/admin/conf/", 12) &&
-                     strncmp(con->uri, "/admin/log/", 11)) ||
+           else if ((!strncmp(con->uri, "/admin", 6) && strncmp(con->uri, "/admin/log/", 11)) ||
                     !strncmp(con->uri, "/printers", 9) ||
                     !strncmp(con->uri, "/classes", 8) ||
                     !strncmp(con->uri, "/help", 5) ||
@@ -1599,7 +1450,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                  cupsdSetString(&con->options, NULL);
              }
 
-             if (con->http->version <= HTTP_VERSION_1_0)
+             if (httpGetVersion(con->http) <= HTTP_VERSION_1_0)
                httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
            }
            else
@@ -1667,7 +1518,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
             if (httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0] &&
                MaxRequestSize > 0 &&
-               con->http->data_remaining > MaxRequestSize)
+               httpGetLength2(con->http) > MaxRequestSize)
            {
             /*
              * Request too large...
@@ -1681,7 +1532,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
              break;
             }
-           else if (con->http->data_remaining < 0)
+           else if (httpGetLength2(con->http) < 0)
            {
             /*
              * Negative content lengths are invalid!
@@ -1749,6 +1600,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                  return;
                }
 
+               cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND);
                break;
              }
            }
@@ -1772,38 +1624,25 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
                  return;
                }
 
+               cupsdLogRequest(con, HTTP_STATUS_NOT_FOUND);
                break;
              }
            }
            else if (!WebInterface)
            {
-              if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
-             {
-               cupsdCloseClient(con);
-               return;
-             }
+              httpClearFields(con->http);
 
-             if (httpPrintf(con->http, "\r\n") < 0)
-             {
-               cupsdCloseClient(con);
-               return;
-             }
-
-             if (cupsdFlushHeader(con) < 0)
+              if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
              }
 
-             con->http->state = HTTP_STATE_WAITING;
-             DEBUG_puts("cupsdReadClient: Set state to HTTP_STATE_WAITING "
-                        "after HEAD.");
+              cupsdLogRequest(con, HTTP_STATUS_OK);
              break;
            }
 
-           if ((!strncmp(con->uri, "/admin", 6) &&
-                strncmp(con->uri, "/admin/conf/", 12) &&
-                strncmp(con->uri, "/admin/log/", 11)) ||
+           if ((!strncmp(con->uri, "/admin", 6) && strcmp(con->uri, "/admin/conf/cupsd.conf") && strncmp(con->uri, "/admin/log/", 11)) ||
                !strncmp(con->uri, "/printers", 9) ||
                !strncmp(con->uri, "/classes", 8) ||
                !strncmp(con->uri, "/help", 5) ||
@@ -1813,19 +1652,9 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              * CGI output...
              */
 
-              if (!cupsdSendHeader(con, HTTP_STATUS_OK, "text/html", CUPSD_AUTH_NONE))
-             {
-               cupsdCloseClient(con);
-               return;
-             }
-
-             if (httpPrintf(con->http, "\r\n") < 0)
-             {
-               cupsdCloseClient(con);
-               return;
-             }
+              httpClearFields(con->http);
 
-             if (cupsdFlushHeader(con) < 0)
+              if (!cupsdSendHeader(con, HTTP_STATUS_OK, "text/html", CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1833,12 +1662,7 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
 
               cupsdLogRequest(con, HTTP_STATUS_OK);
            }
-            else if ((!strncmp(con->uri, "/admin/conf/", 12) &&
-                     (strchr(con->uri + 12, '/') ||
-                      strlen(con->uri) == 12)) ||
-                    (!strncmp(con->uri, "/admin/log/", 11) &&
-                     (strchr(con->uri + 11, '/') ||
-                      strlen(con->uri) == 11)))
+            else if (!strncmp(con->uri, "/admin/log/", 11) && (strchr(con->uri + 11, '/') || strlen(con->uri) == 11))
            {
             /*
              * HEAD can only be done to configuration files under
@@ -1854,11 +1678,14 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
                return;
              }
 
+              cupsdLogRequest(con, HTTP_STATUS_FORBIDDEN);
              break;
            }
            else if ((filename = get_file(con, &filestats, buf,
                                          sizeof(buf))) == NULL)
            {
+              httpClearFields(con->http);
+
              if (!cupsdSendHeader(con, HTTP_STATUS_NOT_FOUND, "text/html",
                                   CUPSD_AUTH_NONE))
              {
@@ -1890,21 +1717,13 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
              else
                snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
 
-              if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE))
-             {
-               cupsdCloseClient(con);
-               return;
-             }
+              httpClearFields(con->http);
 
-             if (httpPrintf(con->http, "Last-Modified: %s\r\n",
-                            httpGetDateString(filestats.st_mtime)) < 0)
-             {
-               cupsdCloseClient(con);
-               return;
-             }
+             httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED,
+                          httpGetDateString(filestats.st_mtime));
+             httpSetLength(con->http, (size_t)filestats.st_size);
 
-             if (httpPrintf(con->http, "Content-Length: %lu\r\n",
-                            (unsigned long)filestats.st_size) < 0)
+              if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE))
              {
                cupsdCloseClient(con);
                return;
@@ -1912,22 +1731,6 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
 
               cupsdLogRequest(con, HTTP_STATUS_OK);
            }
-
-            if (httpPrintf(con->http, "\r\n") < 0)
-           {
-             cupsdCloseClient(con);
-             return;
-           }
-
-           if (cupsdFlushHeader(con) < 0)
-            {
-             cupsdCloseClient(con);
-             return;
-           }
-
-            con->http->state = HTTP_STATE_WAITING;
-           DEBUG_puts("cupsdReadClient: Set state to HTTP_STATE_WAITING "
-                      "after HEAD.");
             break;
 
        default :
@@ -1962,7 +1765,21 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
          {
            con->bytes += bytes;
 
-            if (write(con->file, line, bytes) < bytes)
+            if (MaxRequestSize > 0 && con->bytes > MaxRequestSize)
+            {
+             close(con->file);
+             con->file = -1;
+             unlink(con->filename);
+             cupsdClearString(&con->filename);
+
+              if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
+             {
+               cupsdCloseClient(con);
+               return;
+             }
+            }
+
+            if (write(con->file, line, (size_t)bytes) < bytes)
            {
               cupsdLogClient(con, CUPSD_LOG_ERROR,
                             "Unable to write %d bytes to \"%s\": %s", bytes,
@@ -1980,6 +1797,11 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              }
            }
          }
+          else if (httpGetState(con->http) == HTTP_STATE_PUT_RECV)
+          {
+            cupsdCloseClient(con);
+            return;
+          }
         }
        while (httpGetState(con->http) == HTTP_STATE_PUT_RECV && httpGetReady(con->http));
 
@@ -2038,6 +1860,9 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
            * Grab any request data from the connection...
            */
 
+           if (!httpWait(con->http, 0))
+             return;
+
            if ((ipp_state = ippRead(con->http, con->request)) == IPP_STATE_ERROR)
            {
               cupsdLogClient(con, CUPSD_LOG_ERROR, "IPP read error: %s",
@@ -2067,7 +1892,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                              con->request->request.op.version[1],
                              ippOpString(con->request->request.op.operation_id),
                              con->request->request.op.request_id);
-             con->bytes += ippLength(con->request);
+             con->bytes += (off_t)ippLength(con->request);
            }
          }
 
@@ -2120,7 +1945,21 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
            {
              con->bytes += bytes;
 
-              if (write(con->file, line, bytes) < bytes)
+              if (MaxRequestSize > 0 && con->bytes > MaxRequestSize)
+              {
+                close(con->file);
+                con->file = -1;
+                unlink(con->filename);
+                cupsdClearString(&con->filename);
+
+                if (!cupsdSendError(con, HTTP_STATUS_REQUEST_TOO_LARGE, CUPSD_AUTH_NONE))
+                {
+                  cupsdCloseClient(con);
+                  return;
+                }
+              }
+
+              if (write(con->file, line, (size_t)bytes) < bytes)
              {
                cupsdLogClient(con, CUPSD_LOG_ERROR,
                               "Unable to write %d bytes to \"%s\": %s",
@@ -2281,7 +2120,10 @@ cupsdSendCommand(
   else
     fd = -1;
 
-  con->pipe_pid = pipe_command(con, fd, &(con->file), command, options, root);
+  con->pipe_pid    = pipe_command(con, fd, &(con->file), command, options, root);
+  con->pipe_status = HTTP_STATUS_OK;
+
+  httpClearFields(con->http);
 
   if (fd >= 0)
     close(fd);
@@ -2316,8 +2158,10 @@ cupsdSendError(cupsd_client_t *con,      /* I - Connection */
                http_status_t  code,    /* I - Error code */
               int            auth_type)/* I - Authentication type */
 {
-  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdSendError code=%d, auth_type=%d",
-                code, auth_type);
+  char location[HTTP_MAX_VALUE];       /* Location field */
+
+
+  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "cupsdSendError code=%d, auth_type=%d", code, auth_type);
 
 #ifdef HAVE_SSL
  /*
@@ -2348,32 +2192,18 @@ cupsdSendError(cupsd_client_t *con,     /* I - Connection */
   * never disable it in that case.
   */
 
-  if (code >= HTTP_STATUS_BAD_REQUEST && con->type != CUPSD_AUTH_NEGOTIATE)
-    httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
+  strlcpy(location, httpGetField(con->http, HTTP_FIELD_LOCATION), sizeof(location));
 
- /*
-  * Send an error message back to the client.  If the error code is a
-  * 400 or 500 series, make sure the message contains some text, too!
-  */
+  httpClearFields(con->http);
 
-  if (!cupsdSendHeader(con, code, NULL, auth_type))
-    return (0);
+  httpSetField(con->http, HTTP_FIELD_LOCATION, location);
 
-#ifdef HAVE_SSL
-  if (code == HTTP_STATUS_UPGRADE_REQUIRED)
-    if (httpPrintf(con->http, "Connection: Upgrade\r\n") < 0)
-      return (0);
-
-  if (httpPrintf(con->http, "Upgrade: TLS/1.2,TLS/1.1,TLS/1.0\r\n") < 0)
-    return (0);
-#endif /* HAVE_SSL */
+  if (code >= HTTP_STATUS_BAD_REQUEST && con->type != CUPSD_AUTH_NEGOTIATE)
+    httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
 
-  if (con->http->version >= HTTP_VERSION_1_1 &&
+  if (httpGetVersion(con->http) >= HTTP_VERSION_1_1 &&
       httpGetKeepAlive(con->http) == HTTP_KEEPALIVE_OFF)
-  {
-    if (httpPrintf(con->http, "Connection: close\r\n") < 0)
-      return (0);
-  }
+    httpSetField(con->http, HTTP_FIELD_CONNECTION, "close");
 
   if (code >= HTTP_STATUS_BAD_REQUEST)
   {
@@ -2439,25 +2269,31 @@ cupsdSendError(cupsd_client_t *con,     /* I - Connection */
             _httpStatus(con->language, code), redirect,
             _httpStatus(con->language, code), text);
 
-    if (httpPrintf(con->http, "Content-Type: text/html; charset=utf-8\r\n") < 0)
-      return (0);
-    if (httpPrintf(con->http, "Content-Length: %d\r\n",
-                   (int)strlen(message)) < 0)
+   /*
+    * Send an error message back to the client.  If the error code is a
+    * 400 or 500 series, make sure the message contains some text, too!
+    */
+
+    size_t length = strlen(message);   /* Length of message */
+
+    httpSetLength(con->http, length);
+
+    if (!cupsdSendHeader(con, code, "text/html", auth_type))
       return (0);
-    if (httpPrintf(con->http, "\r\n") < 0)
+
+    if (httpWrite2(con->http, message, length) < 0)
       return (0);
-    if (httpPrintf(con->http, "%s", message) < 0)
+
+    if (httpFlushWrite(con->http) < 0)
       return (0);
   }
-  else if (httpPrintf(con->http, "\r\n") < 0)
-    return (0);
-
-  if (cupsdFlushHeader(con) < 0)
-    return (0);
-
-  con->http->state = HTTP_STATE_WAITING;
+  else
+  {
+    httpSetField(con->http, HTTP_FIELD_CONTENT_LENGTH, "0");
 
-  DEBUG_puts("cupsdSendError: Set state to HTTP_STATE_WAITING.");
+    if (!cupsdSendHeader(con, code, NULL, auth_type))
+      return (0);
+  }
 
   return (1);
 }
@@ -2477,20 +2313,13 @@ cupsdSendHeader(
   char         auth_str[1024];         /* Authorization string */
 
 
+  cupsdLogClient(con, CUPSD_LOG_DEBUG, "cupsdSendHeader: code=%d, type=\"%s\", auth_type=%d", code, type, auth_type);
+
  /*
   * Send the HTTP status header...
   */
 
-  if (code == HTTP_STATUS_CONTINUE)
-  {
-   /*
-    * 100-continue doesn't send any headers...
-    */
-
-    return (httpPrintf(con->http, "HTTP/%d.%d 100 Continue\r\n\r\n",
-                      con->http->version / 100, con->http->version % 100) > 0);
-  }
-  else if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED)
+  if (code == HTTP_STATUS_CUPS_WEBIF_DISABLED)
   {
    /*
     * Treat our special "web interface is disabled" status as "200 OK" for web
@@ -2500,32 +2329,11 @@ cupsdSendHeader(
     code = HTTP_STATUS_OK;
   }
 
-  httpFlushWrite(con->http);
-
-  con->http->data_encoding = HTTP_ENCODING_FIELDS;
-
-  if (httpPrintf(con->http, "HTTP/%d.%d %d %s\r\n", con->http->version / 100,
-                 con->http->version % 100, code, httpStatus(code)) < 0)
-    return (0);
-  if (httpPrintf(con->http, "Date: %s\r\n", httpGetDateString(time(NULL))) < 0)
-    return (0);
   if (ServerHeader)
-    if (httpPrintf(con->http, "Server: %s\r\n", ServerHeader) < 0)
-      return (0);
-  if (httpGetKeepAlive(con->http) && con->http->version >= HTTP_VERSION_1_0)
-  {
-    if (httpPrintf(con->http, "Connection: Keep-Alive\r\n") < 0)
-      return (0);
-    if (httpPrintf(con->http, "Keep-Alive: timeout=%d\r\n",
-                   KeepAliveTimeout) < 0)
-      return (0);
-  }
-  else if (httpPrintf(con->http, "Connection: close\r\n") < 0)
-    return (0);
+    httpSetField(con->http, HTTP_FIELD_SERVER, ServerHeader);
 
   if (code == HTTP_STATUS_METHOD_NOT_ALLOWED)
-    if (httpPrintf(con->http, "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n") < 0)
-      return (0);
+    httpSetField(con->http, HTTP_FIELD_ALLOW, "GET, HEAD, OPTIONS, POST, PUT");
 
   if (code == HTTP_STATUS_UNAUTHORIZED)
   {
@@ -2539,11 +2347,8 @@ cupsdSendHeader(
 
     auth_str[0] = '\0';
 
-    if (auth_type == CUPSD_AUTH_BASIC || auth_type == CUPSD_AUTH_BASICDIGEST)
+    if (auth_type == CUPSD_AUTH_BASIC)
       strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
-    else if (auth_type == CUPSD_AUTH_DIGEST)
-      snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
-              httpGetHostname(con->http, NULL, 0));
 #ifdef HAVE_GSSAPI
     else if (auth_type == CUPSD_AUTH_NEGOTIATE)
     {
@@ -2573,7 +2378,7 @@ cupsdSendHeader(
       size_t   auth_size;              /* Size of remaining buffer */
 
       auth_key  = auth_str + strlen(auth_str);
-      auth_size = sizeof(auth_str) - (auth_key - auth_str);
+      auth_size = sizeof(auth_str) - (size_t)(auth_key - auth_str);
 
       for (name = (char *)cupsArrayFirst(con->best->names);
            name;
@@ -2608,31 +2413,22 @@ cupsdSendHeader(
     {
       cupsdLogClient(con, CUPSD_LOG_DEBUG, "WWW-Authenticate: %s", auth_str);
 
-      if (httpPrintf(con->http, "WWW-Authenticate: %s\r\n", auth_str) < 0)
-        return (0);
+      httpSetField(con->http, HTTP_FIELD_WWW_AUTHENTICATE, auth_str);
     }
   }
 
   if (con->language && strcmp(con->language->language, "C"))
-  {
-    if (httpPrintf(con->http, "Content-Language: %s\r\n",
-                   con->language->language) < 0)
-      return (0);
-  }
+    httpSetField(con->http, HTTP_FIELD_CONTENT_LANGUAGE, con->language->language);
 
   if (type)
   {
     if (!strcmp(type, "text/html"))
-    {
-      if (httpPrintf(con->http,
-                     "Content-Type: text/html; charset=utf-8\r\n") < 0)
-        return (0);
-    }
-    else if (httpPrintf(con->http, "Content-Type: %s\r\n", type) < 0)
-      return (0);
+      httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, "text/html; charset=utf-8");
+    else
+      httpSetField(con->http, HTTP_FIELD_CONTENT_TYPE, type);
   }
 
-  return (1);
+  return (!httpWriteResponse(con->http, code));
 }
 
 
@@ -2698,11 +2494,10 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
                 "file=%d",
                 httpError(con->http), (int)httpGetReady(con->http),
                 httpStateString(httpGetState(con->http)),
-                con->http->data_encoding == HTTP_ENCODING_CHUNKED ?
-                    "CHUNKED" : "LENGTH",
-                CUPS_LLCAST con->http->data_remaining,
+                httpIsChunked(con->http) ? "CHUNKED" : "LENGTH",
+                CUPS_LLCAST httpGetLength2(con->http),
                 con->response,
-                con->response ? ipp_states[con->response->state] : "",
+                con->response ? ippStateString(ippGetState(con->request)) : "",
                 con->pipe_pid, con->file);
 
   if (httpGetState(con->http) != HTTP_STATE_GET_SEND &&
@@ -2713,7 +2508,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
     * connection and we need to shut it down...
     */
 
-    cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP state %s.",
+    cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing on unexpected HTTP write state %s.",
                   httpStateString(httpGetState(con->http)));
     cupsdCloseClient(con);
     return;
@@ -2742,9 +2537,20 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
     con->file_ready = 0;
   }
 
+  bytes = (ssize_t)(sizeof(con->header) - (size_t)con->header_used);
+
+  if (!con->pipe_pid && bytes > (ssize_t)httpGetRemaining(con->http))
+  {
+   /*
+    * Limit GET bytes to original size of file (STR #3265)...
+    */
+
+    bytes = (ssize_t)httpGetRemaining(con->http);
+  }
+
   if (con->response && con->response->state != IPP_STATE_DATA)
   {
-    int wused = con->http->wused;      /* Previous write buffer use */
+    size_t wused = httpGetPending(con->http);  /* Previous write buffer use */
 
     do
     {
@@ -2758,21 +2564,18 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
       * If the write buffer has been flushed, stop buffering up attributes...
       */
 
-      if (con->http->wused <= wused)
+      if (httpGetPending(con->http) <= wused)
         break;
     }
     while (ipp_state != IPP_STATE_DATA && ipp_state != IPP_STATE_ERROR);
 
     cupsdLogClient(con, CUPSD_LOG_DEBUG,
                    "Writing IPP response, ipp_state=%s, old "
-                   "wused=%d, new wused=%d",
-                   ipp_state == IPP_STATE_ERROR ? "ERROR" :
-                       ipp_state == IPP_STATE_IDLE ? "IDLE" :
-                       ipp_state == IPP_STATE_HEADER ? "HEADER" :
-                       ipp_state == IPP_STATE_ATTRIBUTE ? "ATTRIBUTE" : "DATA",
-                  wused, con->http->wused);
-
-    if (con->http->wused > 0)
+                   "wused=" CUPS_LLFMT ", new wused=" CUPS_LLFMT,
+                   ippStateString(ipp_state),
+                  CUPS_LLCAST wused, CUPS_LLCAST httpGetPending(con->http));
+
+    if (httpGetPending(con->http) > 0)
       httpFlushWrite(con->http);
 
     bytes = ipp_state != IPP_STATE_ERROR &&
@@ -2781,10 +2584,9 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
     cupsdLogClient(con, CUPSD_LOG_DEBUG,
                    "bytes=%d, http_state=%d, data_remaining=" CUPS_LLFMT,
                    (int)bytes, httpGetState(con->http),
-                   CUPS_LLCAST con->http->data_remaining);
+                   CUPS_LLCAST httpGetLength2(con->http));
   }
-  else if ((bytes = read(con->file, con->header + con->header_used,
-                        sizeof(con->header) - con->header_used)) > 0)
+  else if ((bytes = read(con->file, con->header + con->header_used, (size_t)bytes)) > 0)
   {
     con->header_used += bytes;
 
@@ -2817,46 +2619,43 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
            * Handle redirection and CGI status codes...
            */
 
-            if (!_cups_strncasecmp(con->header, "Location:", 9))
+           http_field_t field;         /* HTTP field */
+           char        *value = strchr(con->header, ':');
+                                       /* Value of field */
+
+           if (value)
            {
-             if (!cupsdSendHeader(con, HTTP_STATUS_SEE_OTHER, NULL, CUPSD_AUTH_NONE))
-             {
-               cupsdCloseClient(con);
-               return;
-             }
+             *value++ = '\0';
+             while (isspace(*value & 255))
+               value ++;
+           }
 
-             con->sent_header = 2;
+           field = httpFieldValue(con->header);
 
-             if (httpPrintf(con->http, "Content-Length: 0\r\n") < 0)
-               return;
+           if (field != HTTP_FIELD_UNKNOWN && value)
+           {
+             httpSetField(con->http, field, value);
+
+             if (field == HTTP_FIELD_LOCATION)
+             {
+               con->pipe_status = HTTP_STATUS_SEE_OTHER;
+               con->sent_header = 2;
+             }
+             else
+               con->sent_header = 1;
            }
-           else if (!_cups_strncasecmp(con->header, "Status:", 7))
+           else if (!_cups_strcasecmp(con->header, "Status") && value)
            {
-             cupsdSendError(con, (http_status_t)atoi(con->header + 7),
-                            CUPSD_AUTH_NONE);
+             con->pipe_status = (http_status_t)atoi(value);
              con->sent_header = 2;
            }
-           else
+           else if (!_cups_strcasecmp(con->header, "Set-Cookie") && value)
            {
-             if (!cupsdSendHeader(con, HTTP_STATUS_OK, NULL, CUPSD_AUTH_NONE))
-             {
-               cupsdCloseClient(con);
-               return;
-             }
-
+             httpSetCookie(con->http, value);
              con->sent_header = 1;
-
-             if (con->http->version == HTTP_VERSION_1_1)
-             {
-               if (httpPrintf(con->http, "Transfer-Encoding: chunked\r\n") < 0)
-                 return;
-             }
-            }
+           }
          }
 
-         if (_cups_strncasecmp(con->header, "Status:", 7))
-           httpPrintf(con->http, "%s\r\n", con->header);
-
          /*
          * Update buffer...
          */
@@ -2864,7 +2663,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
          con->header_used -= bufptr - con->header;
 
          if (con->header_used > 0)
-           memmove(con->header, bufptr, con->header_used);
+           memmove(con->header, bufptr, (size_t)con->header_used);
 
          bufptr = con->header - 1;
 
@@ -2876,14 +2675,28 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
          {
            con->got_fields = 1;
 
-            if (cupsdFlushHeader(con) < 0)
+           if (httpGetVersion(con->http) == HTTP_VERSION_1_1 &&
+               !httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0])
+             httpSetLength(con->http, 0);
+
+            cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending status %d for CGI.", con->pipe_status);
+
+            if (con->pipe_status == HTTP_STATUS_OK)
            {
-             cupsdCloseClient(con);
-             return;
+             if (!cupsdSendHeader(con, con->pipe_status, NULL, CUPSD_AUTH_NONE))
+             {
+               cupsdCloseClient(con);
+               return;
+             }
+           }
+           else
+           {
+             if (!cupsdSendError(con, con->pipe_status, CUPSD_AUTH_NONE))
+             {
+               cupsdCloseClient(con);
+               return;
+             }
            }
-
-           if (con->http->version == HTTP_VERSION_1_1)
-             con->http->data_encoding = HTTP_ENCODING_CHUNKED;
           }
          else
            field_col = 0;
@@ -2893,15 +2706,12 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
       }
 
       if (!con->got_fields)
-      {
-        con->http->activity = time(NULL);
         return;
-      }
     }
 
     if (con->header_used > 0)
     {
-      if (httpWrite2(con->http, con->header, con->header_used) < 0)
+      if (httpWrite2(con->http, con->header, (size_t)con->header_used) < 0)
       {
        cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)",
                       httpError(con->http), strerror(httpError(con->http)));
@@ -2909,7 +2719,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
        return;
       }
 
-      if (con->http->data_encoding == HTTP_ENCODING_CHUNKED)
+      if (httpIsChunked(con->http))
         httpFlushWrite(con->http);
 
       con->bytes += con->header_used;
@@ -2933,11 +2743,10 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
     {
       cupsdLogRequest(con, HTTP_STATUS_OK);
 
-      httpFlushWrite(con->http);
-
-      if (con->http->data_encoding == HTTP_ENCODING_CHUNKED &&
-          con->sent_header == 1)
+      if (httpIsChunked(con->http) && (!con->pipe_pid || con->sent_header > 0))
       {
+        cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending 0-length chunk.");
+
        if (httpWrite2(con->http, "", 0) < 0)
        {
          cupsdLogClient(con, CUPSD_LOG_DEBUG, "Closing for error %d (%s)",
@@ -2946,9 +2755,11 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
          return;
        }
       }
-    }
 
-    con->http->state = HTTP_STATE_WAITING;
+      cupsdLogClient(con, CUPSD_LOG_DEBUG, "Flushing write buffer.");
+      httpFlushWrite(con->http);
+      cupsdLogClient(con, CUPSD_LOG_DEBUG, "New state is %s", httpStateString(httpGetState(con->http)));
+    }
 
     cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, NULL, con);
 
@@ -3001,8 +2812,6 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
       cupsdSetBusyState();
     }
   }
-
-  con->http->activity = time(NULL);
 }
 
 
@@ -3027,11 +2836,7 @@ check_if_modified(
   if (*ptr == '\0')
     return (1);
 
-  cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                 "check_if_modified "
-                "filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"",
-                 filestats, CUPS_LLCAST filestats->st_size,
-                (int)filestats->st_mtime, ptr);
+  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "check_if_modified: filestats=%p(" CUPS_LLFMT ", %d)) If-Modified-Since=\"%s\"", filestats, CUPS_LLCAST filestats->st_size, (int)filestats->st_mtime, ptr);
 
   while (*ptr != '\0')
   {
@@ -3082,6 +2887,28 @@ compare_clients(cupsd_client_t *a,       /* I - First client */
 }
 
 
+#ifdef HAVE_SSL
+/*
+ * 'cupsd_start_tls()' - Start encryption on a connection.
+ */
+
+static int                             /* O - 0 on success, -1 on error */
+cupsd_start_tls(cupsd_client_t    *con,        /* I - Client connection */
+                http_encryption_t e)   /* I - Encryption mode */
+{
+  if (httpEncryption(con->http, e))
+  {
+    cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to encrypt connection: %s",
+                   cupsLastErrorString());
+    return (-1);
+  }
+
+  cupsdLogClient(con, CUPSD_LOG_DEBUG, "Connection now encrypted.");
+  return (0);
+}
+#endif /* HAVE_SSL */
+
+
 /*
  * 'get_file()' - Get a filename and state info.
  */
@@ -3090,12 +2917,14 @@ static char *                           /* O  - Real filename */
 get_file(cupsd_client_t *con,          /* I  - Client connection */
          struct stat    *filestats,    /* O  - File information */
          char           *filename,     /* IO - Filename buffer */
-         int            len)           /* I  - Buffer length */
+         size_t         len)           /* I  - Buffer length */
 {
   int          status;                 /* Status of filesystem calls */
   char         *ptr;                   /* Pointer info filename */
-  int          plen;                   /* Remaining length after pointer */
-  char         language[7];            /* Language subdirectory, if any */
+  size_t       plen;                   /* Remaining length after pointer */
+  char         language[7],            /* Language subdirectory, if any */
+               dest[1024];             /* Destination name */
+  int          perm_check = 1;         /* Do permissions check? */
 
 
  /*
@@ -3105,17 +2934,59 @@ get_file(cupsd_client_t *con,           /* I  - Client connection */
   language[0] = '\0';
 
   if (!strncmp(con->uri, "/ppd/", 5) && !strchr(con->uri + 5, '/'))
+  {
+    strlcpy(dest, con->uri + 5, sizeof(dest));
+    ptr = dest + strlen(dest) - 4;
+
+    if (ptr <= dest || strcmp(ptr, ".ppd"))
+    {
+      cupsdLogClient(con, CUPSD_LOG_INFO, "Disallowed path \"%s\".", con->uri);
+      return (NULL);
+    }
+
+    *ptr = '\0';
+    if (!cupsdFindPrinter(dest))
+    {
+      cupsdLogClient(con, CUPSD_LOG_INFO, "No printer \"%s\" found.", dest);
+      return (NULL);
+    }
+
     snprintf(filename, len, "%s%s", ServerRoot, con->uri);
+
+    perm_check = 0;
+  }
   else if (!strncmp(con->uri, "/icons/", 7) && !strchr(con->uri + 7, '/'))
   {
-    snprintf(filename, len, "%s/%s", CacheDir, con->uri + 7);
+    strlcpy(dest, con->uri + 7, sizeof(dest));
+    ptr = dest + strlen(dest) - 4;
+
+    if (ptr <= dest || strcmp(ptr, ".png"))
+    {
+      cupsdLogClient(con, CUPSD_LOG_INFO, "Disallowed path \"%s\".", con->uri);
+      return (NULL);
+    }
+
+    *ptr = '\0';
+    if (!cupsdFindDest(dest))
+    {
+      cupsdLogClient(con, CUPSD_LOG_INFO, "No printer \"%s\" found.", dest);
+      return (NULL);
+    }
+
+    snprintf(filename, len, "%s/%s.png", CacheDir, dest);
     if (access(filename, F_OK) < 0)
       snprintf(filename, len, "%s/images/generic.png", DocumentRoot);
+
+    perm_check = 0;
   }
   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))
-    snprintf(filename, len, "%s%s", ServerRoot, con->uri + 11);
+  else if (!strcmp(con->uri, "/admin/conf/cupsd.conf"))
+  {
+    strlcpy(filename, ConfigurationFile, len);
+
+    perm_check = 0;
+  }
   else if (!strncmp(con->uri, "/admin/log/", 11))
   {
     if (!strncmp(con->uri + 11, "access_log", 10) && AccessLog[0] == '/')
@@ -3126,6 +2997,8 @@ get_file(cupsd_client_t *con,             /* I  - Client connection */
       strlcpy(filename, PageLog, len);
     else
       return (NULL);
+
+    perm_check = 0;
   }
   else if (con->language)
   {
@@ -3143,7 +3016,7 @@ get_file(cupsd_client_t *con,             /* I  - Client connection */
   * then fallback to the default one...
   */
 
-  if ((status = stat(filename, filestats)) != 0 && language[0] &&
+  if ((status = lstat(filename, filestats)) != 0 && language[0] &&
       strncmp(con->uri, "/icons/", 7) &&
       strncmp(con->uri, "/ppd/", 5) &&
       strncmp(con->uri, "/rss/", 5) &&
@@ -3160,7 +3033,7 @@ get_file(cupsd_client_t *con,             /* I  - Client connection */
     if ((ptr = strchr(filename, '?')) != NULL)
       *ptr = '\0';
 
-    if ((status = stat(filename, filestats)) != 0)
+    if ((status = lstat(filename, filestats)) != 0)
     {
      /*
       * Drop the language prefix and try the root directory...
@@ -3172,12 +3045,33 @@ get_file(cupsd_client_t *con,           /* I  - Client connection */
       if ((ptr = strchr(filename, '?')) != NULL)
        *ptr = '\0';
 
-      status = stat(filename, filestats);
+      status = lstat(filename, filestats);
     }
   }
 
  /*
-  * If we're found a directory, get the index.html file instead...
+  * If we've found a symlink, 404 the sucker to avoid disclosing information.
+  */
+
+  if (!status && S_ISLNK(filestats->st_mode))
+  {
+    cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename);
+    return (NULL);
+  }
+
+ /*
+  * Similarly, if the file/directory does not have world read permissions, do
+  * not allow access...
+  */
+
+  if (!status && perm_check && !(filestats->st_mode & S_IROTH))
+  {
+    cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename);
+    return (NULL);
+  }
+
+ /*
+  * If we've found a directory, get the index.html file instead...
   */
 
   if (!status && S_ISDIR(filestats->st_mode))
@@ -3217,16 +3111,16 @@ get_file(cupsd_client_t *con,           /* I  - Client connection */
        *ptr = '\0';
 
       ptr  = filename + strlen(filename);
-      plen = len - (ptr - filename);
+      plen = len - (size_t)(ptr - filename);
 
       strlcpy(ptr, "index.html", plen);
-      status = stat(filename, filestats);
+      status = lstat(filename, filestats);
 
 #ifdef HAVE_JAVA
       if (status)
       {
        strlcpy(ptr, "index.class", plen);
-       status = stat(filename, filestats);
+       status = lstat(filename, filestats);
       }
 #endif /* HAVE_JAVA */
 
@@ -3234,7 +3128,7 @@ get_file(cupsd_client_t *con,             /* I  - Client connection */
       if (status)
       {
        strlcpy(ptr, "index.pl", plen);
-       status = stat(filename, filestats);
+       status = lstat(filename, filestats);
       }
 #endif /* HAVE_PERL */
 
@@ -3242,7 +3136,7 @@ get_file(cupsd_client_t *con,             /* I  - Client connection */
       if (status)
       {
        strlcpy(ptr, "index.php", plen);
-       status = stat(filename, filestats);
+       status = lstat(filename, filestats);
       }
 #endif /* HAVE_PHP */
 
@@ -3250,24 +3144,42 @@ get_file(cupsd_client_t *con,           /* I  - Client connection */
       if (status)
       {
        strlcpy(ptr, "index.pyc", plen);
-       status = stat(filename, filestats);
+       status = lstat(filename, filestats);
       }
 
       if (status)
       {
        strlcpy(ptr, "index.py", plen);
-       status = stat(filename, filestats);
+       status = lstat(filename, filestats);
       }
 #endif /* HAVE_PYTHON */
 
     }
     while (status && language[0]);
+
+   /*
+    * If we've found a symlink, 404 the sucker to avoid disclosing information.
+    */
+
+    if (!status && S_ISLNK(filestats->st_mode))
+    {
+      cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as \"%s\" are not allowed.", filename);
+      return (NULL);
+    }
+
+   /*
+    * Similarly, if the file/directory does not have world read permissions, do
+    * not allow access...
+    */
+
+    if (!status && perm_check && !(filestats->st_mode & S_IROTH))
+    {
+      cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as \"%s\" must be world-readable.", filename);
+      return (NULL);
+    }
   }
 
-  cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                 "get_file filestats=%p, filename=%p, len=%d, "
-                "returning \"%s\".", filestats, filename, len,
-                status ? "(null)" : filename);
+  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "get_file: filestats=%p, filename=%p, len=" CUPS_LLFMT ", returning \"%s\".", filestats, filename, CUPS_LLCAST len, status ? "(null)" : filename);
 
   if (status)
     return (NULL);
@@ -3298,7 +3210,7 @@ install_cupsd_conf(cupsd_client_t *con)   /* I - Connection */
   {
     cupsdLogClient(con, CUPSD_LOG_ERROR, "Unable to open request file \"%s\": %s",
                     con->filename, strerror(errno));
-    return (HTTP_STATUS_SERVER_ERROR);
+    goto server_error;
   }
 
  /*
@@ -3308,7 +3220,7 @@ install_cupsd_conf(cupsd_client_t *con)   /* I - Connection */
   if ((out = cupsdCreateConfFile(ConfigurationFile, ConfigFilePerm)) == NULL)
   {
     cupsFileClose(in);
-    return (HTTP_STATUS_SERVER_ERROR);
+    goto server_error;
   }
 
   cupsdLogClient(con, CUPSD_LOG_INFO, "Installing config file \"%s\"...",
@@ -3319,7 +3231,7 @@ install_cupsd_conf(cupsd_client_t *con)   /* I - Connection */
   */
 
   while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
-    if (cupsFileWrite(out, buffer, bytes) < bytes)
+    if (cupsFileWrite(out, buffer, (size_t)bytes) < bytes)
     {
       cupsdLogClient(con, CUPSD_LOG_ERROR,
                       "Unable to copy to config file \"%s\": %s",
@@ -3331,7 +3243,7 @@ install_cupsd_conf(cupsd_client_t *con)   /* I - Connection */
       snprintf(filename, sizeof(filename), "%s.N", ConfigurationFile);
       cupsdUnlinkOrRemoveFile(filename);
 
-      return (HTTP_STATUS_SERVER_ERROR);
+      goto server_error;
     }
 
  /*
@@ -3341,7 +3253,7 @@ install_cupsd_conf(cupsd_client_t *con)   /* I - Connection */
   cupsFileClose(in);
 
   if (cupsdCloseCreatedConfFile(out, ConfigurationFile))
-    return (HTTP_STATUS_SERVER_ERROR);
+    goto server_error;
 
  /*
   * Remove the request file...
@@ -3362,6 +3274,17 @@ install_cupsd_conf(cupsd_client_t *con)  /* I - Connection */
   */
 
   return (HTTP_STATUS_CREATED);
+
+ /*
+  * Common exit for errors...
+  */
+
+  server_error:
+
+  cupsdUnlinkOrRemoveFile(con->filename);
+  cupsdClearString(&con->filename);
+
+  return (HTTP_STATUS_SERVER_ERROR);
 }
 
 
@@ -3394,11 +3317,7 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
 
   if (!type || _cups_strcasecmp(type->super, "application"))
   {
-    cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                  "is_cgi filename=\"%s\", filestats=%p, "
-                  "type=%s/%s, returning 0", filename,
-                  filestats, type ? type->super : "unknown",
-                  type ? type->type : "unknown");
+    cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 0.", filename, filestats, type ? type->super : "unknown", type ? type->type : "unknown");
     return (0);
   }
 
@@ -3414,10 +3333,7 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
     if (options)
       cupsdSetStringf(&con->options, " %s", options);
 
-    cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                  "is_cgi filename=\"%s\", filestats=%p, "
-                  "type=%s/%s, returning 1", filename,
-                  filestats, type->super, type->type);
+    cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
     return (1);
   }
 #ifdef HAVE_JAVA
@@ -3434,10 +3350,7 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
     else
       cupsdSetStringf(&con->options, " %s", filename);
 
-    cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                  "is_cgi filename=\"%s\", filestats=%p, "
-                  "type=%s/%s, returning 1", filename,
-                  filestats, type->super, type->type);
+    cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
     return (1);
   }
 #endif /* HAVE_JAVA */
@@ -3455,10 +3368,7 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
     else
       cupsdSetStringf(&con->options, " %s", filename);
 
-    cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                  "is_cgi filename=\"%s\", filestats=%p, "
-                  "type=%s/%s, returning 1", filename,
-                  filestats, type->super, type->type);
+    cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
     return (1);
   }
 #endif /* HAVE_PERL */
@@ -3476,10 +3386,7 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
     else
       cupsdSetStringf(&con->options, " %s", filename);
 
-    cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                  "is_cgi filename=\"%s\", filestats=%p, "
-                  "type=%s/%s, returning 1", filename,
-                  filestats, type->super, type->type);
+    cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
     return (1);
   }
 #endif /* HAVE_PHP */
@@ -3497,18 +3404,12 @@ is_cgi(cupsd_client_t *con,             /* I - Client connection */
     else
       cupsdSetStringf(&con->options, " %s", filename);
 
-    cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                  "is_cgi filename=\"%s\", filestats=%p, "
-                  "type=%s/%s, returning 1", filename,
-                  filestats, type->super, type->type);
+    cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 1.", filename, filestats, type->super, type->type);
     return (1);
   }
 #endif /* HAVE_PYTHON */
 
-  cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                "is_cgi filename=\"%s\", filestats=%p, "
-                "type=%s/%s, returning 0", filename,
-                filestats, type->super, type->type);
+  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "is_cgi: filename=\"%s\", filestats=%p, type=%s/%s, returning 0.", filename, filestats, type->super, type->type);
   return (0);
 }
 
@@ -3527,6 +3428,14 @@ is_path_absolute(const char *path)       /* I - Input path */
   if (path[0] != '/')
     return (0);
 
+ /*
+  * Check for "<" or quotes in the path and reject since this is probably
+  * someone trying to inject HTML...
+  */
+
+  if (strchr(path, '<') != NULL || strchr(path, '\"') != NULL || strchr(path, '\'') != NULL)
+    return (0);
+
  /*
   * Check for "/.." in the path...
   */
@@ -3607,21 +3516,12 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
   * be consistent with Apache...
   */
 
-  cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                 "pipe_command infile=%d, outfile=%p, "
-                "command=\"%s\", options=\"%s\", root=%d",
-                 infile, outfile, command,
-                options ? options : "(null)", root);
+  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "pipe_command: infile=%d, outfile=%p, command=\"%s\", options=\"%s\", root=%d", infile, outfile, command, options ? options : "(null)", root);
 
   argv[0] = command;
 
   if (options)
-  {
-    commptr = options;
-    if (*commptr == ' ')
-      commptr ++;
-    strlcpy(argbuf, commptr, sizeof(argbuf));
-  }
+    strlcpy(argbuf, options, sizeof(argbuf));
   else
     argbuf[0] = '\0';
 
@@ -3691,9 +3591,9 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
        */
 
        if (commptr[1] >= '0' && commptr[1] <= '9')
-          *commptr = (commptr[1] - '0') << 4;
+          *commptr = (char)((commptr[1] - '0') << 4);
        else
-          *commptr = (tolower(commptr[1]) - 'a' + 10) << 4;
+          *commptr = (char)((tolower(commptr[1]) - 'a' + 10) << 4);
 
        if (commptr[2] >= '0' && commptr[2] <= '9')
           *commptr |= commptr[2] - '0';
@@ -3785,7 +3685,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
   snprintf(script_filename, sizeof(script_filename), "SCRIPT_FILENAME=%s%s",
            DocumentRoot, script_name + 12);
 
-  sprintf(server_port, "SERVER_PORT=%d", con->serverport);
+  snprintf(server_port, sizeof(server_port), "SERVER_PORT=%d", con->serverport);
 
   if (httpGetField(con->http, HTTP_FIELD_HOST)[0])
   {
@@ -3825,17 +3725,17 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
     envp[envc ++] = remote_user;
   }
 
-  if (con->http->version == HTTP_VERSION_1_1)
+  if (httpGetVersion(con->http) == HTTP_VERSION_1_1)
     envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.1";
-  else if (con->http->version == HTTP_VERSION_1_0)
+  else if (httpGetVersion(con->http) == HTTP_VERSION_1_0)
     envp[envc ++] = "SERVER_PROTOCOL=HTTP/1.0";
   else
     envp[envc ++] = "SERVER_PROTOCOL=HTTP/0.9";
 
-  if (con->http->cookie)
+  if (httpGetCookie(con->http))
   {
     snprintf(http_cookie, sizeof(http_cookie), "HTTP_COOKIE=%s",
-             con->http->cookie);
+             httpGetCookie(con->http));
     envp[envc ++] = http_cookie;
   }
 
@@ -3884,7 +3784,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
   * Tell the CGI if we are using encryption...
   */
 
-  if (con->http->tls)
+  if (httpIsEncrypted(con->http))
     envp[envc ++] = "HTTPS=ON";
 
  /*
@@ -4133,43 +4033,27 @@ write_file(cupsd_client_t *con,         /* I - Client connection */
 {
   con->file = open(filename, O_RDONLY);
 
-  cupsdLogClient(con, CUPSD_LOG_DEBUG2,
-                 "write_file code=%d, filename=\"%s\" (%d), "
-                "type=\"%s\", filestats=%p",
-                code, filename, con->file, type ? type : "(null)", filestats);
+  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_file: code=%d, filename=\"%s\" (%d), type=\"%s\", filestats=%p.", code, filename, con->file, type ? type : "(null)", filestats);
 
   if (con->file < 0)
     return (0);
 
   fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
 
-  con->pipe_pid = 0;
-
-  if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
-    return (0);
+  con->pipe_pid    = 0;
+  con->sent_header = 1;
 
-  if (httpPrintf(con->http, "Last-Modified: %s\r\n",
-                 httpGetDateString(filestats->st_mtime)) < 0)
-    return (0);
-  if (httpPrintf(con->http, "Content-Length: " CUPS_LLFMT "\r\n",
-                 CUPS_LLCAST filestats->st_size) < 0)
-    return (0);
-  if (httpPrintf(con->http, "\r\n") < 0)
-    return (0);
+  httpClearFields(con->http);
 
-  if (cupsdFlushHeader(con) < 0)
-    return (0);
+  httpSetLength(con->http, (size_t)filestats->st_size);
 
-  con->http->data_encoding  = HTTP_ENCODING_LENGTH;
-  con->http->data_remaining = filestats->st_size;
+  httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED,
+              httpGetDateString(filestats->st_mtime));
 
-  if (con->http->data_remaining <= INT_MAX)
-    con->http->_data_remaining = con->http->data_remaining;
-  else
-    con->http->_data_remaining = INT_MAX;
+  if (!cupsdSendHeader(con, code, type, CUPSD_AUTH_NONE))
+    return (0);
 
-  cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient,
-                 (cupsd_selfunc_t)cupsdWriteClient, con);
+  cupsdAddSelect(httpGetFd(con->http), NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
 
   cupsdLogClient(con, CUPSD_LOG_DEBUG, "Sending file.");
 
@@ -4184,8 +4068,7 @@ write_file(cupsd_client_t *con,           /* I - Client connection */
 static void
 write_pipe(cupsd_client_t *con)                /* I - Client connection */
 {
-  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_pipe CGI output on fd %d",
-                 con->file);
+  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "write_pipe: CGI output on fd %d.", con->file);
 
   con->file_ready = 1;
 
@@ -4194,8 +4077,3 @@ write_pipe(cupsd_client_t *con)           /* I - Client connection */
 
   cupsdLogClient(con, CUPSD_LOG_DEBUG, "CGI data ready to be sent.");
 }
-
-
-/*
- * End of "$Id$".
- */