]> 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 3ec06f71b4f116aad0c0887f7ca842d49123703d..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
@@ -40,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);
@@ -75,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...
@@ -142,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...
@@ -287,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__ */
@@ -377,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
@@ -396,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;
@@ -415,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.");
@@ -501,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);
@@ -553,24 +541,6 @@ cupsdCloseClient(cupsd_client_t *con)      /* I - Client to close */
 }
 
 
-#if 0
-/*
- * '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);
-}
-#endif /* 0 */
-
-
 /*
  * 'cupsdReadClient()' - Read data from a client.
  */
@@ -594,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 ? ippStateString(ippGetState(con->request)) : "",
-                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)
@@ -627,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;
@@ -834,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])
@@ -931,8 +911,7 @@ 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
        /*
@@ -947,13 +926,13 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
          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;
@@ -1006,13 +985,13 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
          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;
@@ -1060,6 +1039,8 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
       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)) &&
@@ -1178,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...
              */
@@ -1267,20 +1247,14 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
              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))
              {
@@ -1310,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))
              {
               /*
@@ -1416,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) ||
@@ -1668,9 +1642,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
              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) ||
@@ -1690,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
@@ -1754,7 +1721,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
 
              httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED,
                           httpGetDateString(filestats.st_mtime));
-             httpSetLength(con->http, filestats.st_size);
+             httpSetLength(con->http, (size_t)filestats.st_size);
 
               if (!cupsdSendHeader(con, HTTP_STATUS_OK, line, CUPSD_AUTH_NONE))
              {
@@ -1798,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,
@@ -1816,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));
 
@@ -1874,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",
@@ -1903,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);
            }
          }
 
@@ -1956,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",
@@ -2155,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
  /*
@@ -2187,8 +2192,12 @@ cupsdSendError(cupsd_client_t *con,      /* I - Connection */
   * never disable it in that case.
   */
 
+  strlcpy(location, httpGetField(con->http, HTTP_FIELD_LOCATION), sizeof(location));
+
   httpClearFields(con->http);
 
+  httpSetField(con->http, HTTP_FIELD_LOCATION, location);
+
   if (code >= HTTP_STATUS_BAD_REQUEST && con->type != CUPSD_AUTH_NEGOTIATE)
     httpSetKeepAlive(con->http, HTTP_KEEPALIVE_OFF);
 
@@ -2260,17 +2269,22 @@ cupsdSendError(cupsd_client_t *con,     /* I - Connection */
             _httpStatus(con->language, code), redirect,
             _httpStatus(con->language, code), text);
 
-    /*
-     * 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!
-     */
+   /*
+    * 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!
+    */
 
-    httpSetLength(con->http, strlen(message));
+    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, "%s", message) < 0)
+
+    if (httpWrite2(con->http, message, length) < 0)
+      return (0);
+
+    if (httpFlushWrite(con->http) < 0)
       return (0);
   }
   else
@@ -2299,6 +2313,8 @@ 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...
   */
@@ -2331,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)
     {
@@ -2365,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;
@@ -2495,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;
@@ -2524,6 +2537,17 @@ 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)
   {
     size_t wused = httpGetPending(con->http);  /* Previous write buffer use */
@@ -2562,8 +2586,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
                    (int)bytes, httpGetState(con->http),
                    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;
 
@@ -2628,12 +2651,6 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
            }
            else if (!_cups_strcasecmp(con->header, "Set-Cookie") && value)
            {
-             char *sep = strchr(value, ';');
-                                       /* Separator between name=value and the rest */
-
-             if (sep)
-               *sep = '\0';
-
              httpSetCookie(con->http, value);
              con->sent_header = 1;
            }
@@ -2646,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;
 
@@ -2662,10 +2679,23 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
                !httpGetField(con->http, HTTP_FIELD_CONTENT_LENGTH)[0])
              httpSetLength(con->http, 0);
 
-            if (!cupsdSendHeader(con, con->pipe_status, NULL, CUPSD_AUTH_NONE))
+            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;
+             }
            }
           }
          else
@@ -2681,7 +2711,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
 
     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)));
@@ -2713,10 +2743,10 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
     {
       cupsdLogRequest(con, HTTP_STATUS_OK);
 
-      httpFlushWrite(con->http);
-
-      if (httpIsChunked(con->http) && 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)",
@@ -2725,6 +2755,10 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
          return;
        }
       }
+
+      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);
@@ -2802,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')
   {
@@ -2857,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.
  */
@@ -2865,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? */
 
 
  /*
@@ -2880,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] == '/')
@@ -2901,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)
   {
@@ -2918,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) &&
@@ -2935,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...
@@ -2947,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))
@@ -2992,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 */
 
@@ -3009,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 */
 
@@ -3017,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 */
 
@@ -3025,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);
@@ -3073,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;
   }
 
  /*
@@ -3083,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\"...",
@@ -3094,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",
@@ -3106,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;
     }
 
  /*
@@ -3116,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...
@@ -3137,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);
 }
 
 
@@ -3169,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);
   }
 
@@ -3189,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
@@ -3209,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 */
@@ -3230,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 */
@@ -3251,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 */
@@ -3272,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);
 }
 
@@ -3302,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...
   */
@@ -3382,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';
 
@@ -3466,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';
@@ -3560,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])
   {
@@ -3908,21 +4033,19 @@ 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;
+  con->pipe_pid    = 0;
+  con->sent_header = 1;
 
   httpClearFields(con->http);
 
-  httpSetLength(con->http, filestats->st_size);
+  httpSetLength(con->http, (size_t)filestats->st_size);
 
   httpSetField(con->http, HTTP_FIELD_LAST_MODIFIED,
               httpGetDateString(filestats->st_mtime));
@@ -3930,8 +4053,7 @@ write_file(cupsd_client_t *con,           /* I - Client connection */
   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.");
 
@@ -3946,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;
 
@@ -3956,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$".
- */