]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/client.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / client.c
index 85e2ac2057347dce104636e40b0c08f5134aa499..9c0a17dc68191759b1e521f654192352769283b9 100644 (file)
@@ -1,9 +1,12 @@
 /*
- * "$Id: client.c 6027 2006-10-11 21:04:58Z mike $"
+ * "$Id: client.c 6329 2007-03-12 14:48:28Z mike $"
  *
  *   Client routines for the Common UNIX Printing System (CUPS) scheduler.
  *
- *   Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ *   This file contains Kerberos support code, copyright 2006 by
+ *   Jelmer Vernooij.
  *
  *   These coded instructions, statements, and computer programs are the
  *   property of Easy Software Products and are protected by Federal
  *   cupsdSendHeader()       - Send an HTTP request.
  *   cupsdUpdateCGI()        - Read status messages from CGI scripts and programs.
  *   cupsdWriteClient()      - Write data to a client as needed.
+ *   cupsdWritePipe()        - Flag that data is available on the CGI pipe.
  *   check_if_modified()     - Decode an "If-Modified-Since" line.
  *   encrypt_client()        - Enable encryption for the client...
- *   get_cdsa_server_certs() - Convert a keychain name into the CFArrayRef
+ *   get_cdsa_certificate()  - Convert a keychain name into the CFArrayRef
  *                            required by SSLSetCertificate.
  *   get_file()              - Get a filename and state info.
  *   install_conf_file()     - Install a configuration file.
 
 #ifdef HAVE_CDSASSL
 #  include <Security/Security.h>
+#  ifdef HAVE_SECIDENTITYSEARCHPRIV_H
+#    include <Security/SecIdentitySearchPriv.h>
+#  else /* Declare prototype for function in that header... */
+extern OSStatus SecIdentitySearchCreateWithPolicy(SecPolicyRef policy, 
+                               CFStringRef idString, CSSM_KEYUSE keyUsage, 
+                               CFTypeRef keychainOrArray, 
+                               Boolean returnOnlyValidIdentities, 
+                               SecIdentitySearchRef* searchRef);
+#  endif /* HAVE_SECIDENTITYSEARCHPRIV_H */
+#  ifdef HAVE_SECPOLICYPRIV_H
+#    include <Security/SecPolicyPriv.h>
+#  else /* Declare prototype for function in that header... */
+extern OSStatus SecPolicySetValue(SecPolicyRef policyRef, 
+                                  const CSSM_DATA *value);
+#  endif /* HAVE_SECPOLICYPRIV_H */
 #  ifdef HAVE_SECBASEPRIV_H
 #    include <Security/SecBasePriv.h>
-#  else
-     extern const char *cssmErrorString(int error);
+#  else /* Declare prototype for function in that header... */
+extern const char *cssmErrorString(int error);
 #  endif /* HAVE_SECBASEPRIV_H */
 #endif /* HAVE_CDSASSL */
+
 #ifdef HAVE_GNUTLS
 #  include <gnutls/x509.h>
 #endif /* HAVE_GNUTLS */
@@ -76,16 +96,16 @@ static int          check_if_modified(cupsd_client_t *con,
 static int             encrypt_client(cupsd_client_t *con);
 #endif /* HAVE_SSL */
 #ifdef HAVE_CDSASSL
-static CFArrayRef      get_cdsa_server_certs(void);
+static CFArrayRef      get_cdsa_certificate(cupsd_client_t *con);
 #endif /* HAVE_CDSASSL */
-static char            *get_file(cupsd_client_t *con, struct stat *filestats, 
+static char            *get_file(cupsd_client_t *con, struct stat *filestats,
                                  char *filename, int len);
 static http_status_t   install_conf_file(cupsd_client_t *con);
 static int             is_cgi(cupsd_client_t *con, const char *filename,
                               struct stat *filestats, mime_type_t *type);
 static int             is_path_absolute(const char *path);
 #ifdef HAVE_SSL
-static int             make_certificate(void);
+static int             make_certificate(cupsd_client_t *con);
 #endif /* HAVE_SSL */
 static int             pipe_command(cupsd_client_t *con, int infile, int *outfile,
                                     char *command, char *options, int root);
@@ -389,7 +409,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
   */
 
   val = 1;
-  setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)); 
+  setsockopt(con->http.fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
 
  /*
   * Close this file on all execs...
@@ -401,10 +421,7 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
   * Add the socket to the select() input mask.
   */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdAcceptClient: Adding fd %d to InputSet...",
-                  con->http.fd);
-  FD_SET(con->http.fd, InputSet);
+  cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
 
  /*
   * Temporarily suspend accept()'s until we lose a client...
@@ -426,7 +443,8 @@ cupsdAcceptClient(cupsd_listener_t *lis)/* I - Listener socket */
 
     con->http.encryption = HTTP_ENCRYPT_ALWAYS;
 
-    encrypt_client(con);
+    if (!encrypt_client(con))
+      cupsdCloseClient(con);
   }
   else
     con->auto_ssl = 1;
@@ -570,13 +588,7 @@ cupsdCloseClient(cupsd_client_t *con)      /* I - Client to close */
 
   if (con->file >= 0)
   {
-    if (FD_ISSET(con->file, InputSet))
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdCloseClient: %d Removing fd %d from InputSet...",
-                     con->http.fd, con->file);
-      FD_CLR(con->file, InputSet);
-    }
+    cupsdRemoveSelect(con->file);
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2,
                     "cupsdCloseClient: %d Closing data file %d.",
@@ -598,11 +610,8 @@ cupsdCloseClient(cupsd_client_t *con)      /* I - Client to close */
       * Only do a partial close so that the encrypted client gets everything.
       */
 
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdCloseClient: Removing fd %d from OutputSet...",
-                     con->http.fd);
       shutdown(con->http.fd, 0);
-      FD_CLR(con->http.fd, OutputSet);
+      cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
     }
     else
     {
@@ -610,12 +619,8 @@ cupsdCloseClient(cupsd_client_t *con)      /* I - Client to close */
       * Shut the socket down fully...
       */
 
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdCloseClient: Removing fd %d from InputSet and OutputSet...",
-                     con->http.fd);
+      cupsdRemoveSelect(con->http.fd);
       close(con->http.fd);
-      FD_CLR(con->http.fd, InputSet);
-      FD_CLR(con->http.fd, OutputSet);
       con->http.fd = -1;
     }
   }
@@ -634,6 +639,7 @@ cupsdCloseClient(cupsd_client_t *con)       /* I - Client to close */
     cupsdClearString(&con->filename);
     cupsdClearString(&con->command);
     cupsdClearString(&con->options);
+    cupsdClearString(&con->query_string);
 
     if (con->request)
     {
@@ -653,6 +659,14 @@ cupsdCloseClient(cupsd_client_t *con)      /* I - Client to close */
       con->language = NULL;
     }
 
+#ifdef HAVE_AUTHORIZATION_H
+    if (con->authref)
+    {
+      AuthorizationFree(con->authref, kAuthorizationFlagDefaults);
+      con->authref = NULL;
+    }
+#endif /* HAVE_AUTHORIZATION_H */
+
    /*
     * Re-enable new client connections if we are going back under the
     * limit...
@@ -693,7 +707,7 @@ cupsdFlushHeader(cupsd_client_t *con)       /* I - Client to flush to */
  * 'cupsdReadClient()' - Read data from a client.
  */
 
-int                                    /* O - 1 on success, 0 on error */
+void
 cupsdReadClient(cupsd_client_t *con)   /* I - Client to read from */
 {
   char                 line[32768],    /* Line from client... */
@@ -722,7 +736,8 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
   if (con->http.error)
   {
     cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: http error seen...");
-    return (cupsdCloseClient(con));
+    cupsdCloseClient(con);
+    return;
   }
 
 #ifdef HAVE_SSL
@@ -745,8 +760,10 @@ cupsdReadClient(cupsd_client_t *con)       /* I - Client to read from */
                       "cupsdReadClient: Saw first byte %02X, auto-negotiating SSL/TLS session...",
                       buf[0] & 255);
 
-      encrypt_client(con);
-      return (1);
+      if (!encrypt_client(con))
+        cupsdCloseClient(con);
+
+      return;
     }
   }
 #endif /* HAVE_SSL */
@@ -762,7 +779,8 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
        {
          cupsdLogMessage(CUPSD_LOG_DEBUG2,
                          "cupsdReadClient: httpGets returned EOF...");
-          return (cupsdCloseClient(con));
+         cupsdCloseClient(con);
+         return;
        }
 
        /*
@@ -795,6 +813,7 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
 
        cupsdClearString(&con->command);
        cupsdClearString(&con->options);
+       cupsdClearString(&con->query_string);
 
        if (con->request)
        {
@@ -825,7 +844,8 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
                              "Bad request line \"%s\" from %s!", line,
                              con->http.hostname);
              cupsdSendError(con, HTTP_BAD_REQUEST);
-             return (cupsdCloseClient(con));
+             cupsdCloseClient(con);
+             return;
          case 2 :
              con->http.version = HTTP_0_9;
              break;
@@ -836,7 +856,8 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
                                "Bad request line \"%s\" from %s!", line,
                                con->http.hostname);
                cupsdSendError(con, HTTP_BAD_REQUEST);
-               return (cupsdCloseClient(con));
+               cupsdCloseClient(con);
+               return;
              }
 
              if (major < 2)
@@ -850,7 +871,8 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
              else
              {
                cupsdSendError(con, HTTP_NOT_SUPPORTED);
-               return (cupsdCloseClient(con));
+               cupsdCloseClient(con);
+               return;
              }
              break;
        }
@@ -895,7 +917,8 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
            cupsdLogMessage(CUPSD_LOG_ERROR, "Bad URI \"%s\" in request!",
                            con->uri);
            cupsdSendError(con, HTTP_METHOD_NOT_ALLOWED);
-           return (cupsdCloseClient(con));
+           cupsdCloseClient(con);
+           return;
          }
 
          /*
@@ -928,7 +951,8 @@ cupsdReadClient(cupsd_client_t *con)        /* I - Client to read from */
        {
          cupsdLogMessage(CUPSD_LOG_ERROR, "Bad operation \"%s\"!", operation);
          cupsdSendError(con, HTTP_BAD_REQUEST);
-         return (cupsdCloseClient(con));
+         cupsdCloseClient(con);
+         return;
        }
 
         con->start     = time(NULL);
@@ -951,12 +975,16 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
         * Parse incoming parameters until the status changes...
        */
 
-        status = httpUpdate(HTTP(con));
+        while ((status = httpUpdate(HTTP(con))) == HTTP_CONTINUE)
+         if (con->http.used == 0 ||
+             !memchr(con->http.buffer, '\n', con->http.used))
+           break;
 
        if (status != HTTP_OK && status != HTTP_CONTINUE)
        {
          cupsdSendError(con, HTTP_BAD_REQUEST);
-         return (cupsdCloseClient(con));
+         cupsdCloseClient(con);
+         return;
        }
        break;
 
@@ -1023,7 +1051,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
       */
 
       if (!cupsdSendError(con, HTTP_BAD_REQUEST))
-       return (cupsdCloseClient(con));
+      {
+       cupsdCloseClient(con);
+       return;
+      }
     }
     else if (con->operation == HTTP_OPTIONS)
     {
@@ -1034,7 +1065,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
       if (con->best && con->best->type != AUTH_NONE)
       {
        if (!cupsdSendHeader(con, HTTP_UNAUTHORIZED, NULL))
-         return (cupsdCloseClient(con));
+       {
+         cupsdCloseClient(con);
+         return;
+       }
       }
 
       if (!strcasecmp(con->http.fields[HTTP_FIELD_CONNECTION], "Upgrade") &&
@@ -1046,7 +1080,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
        */
 
        if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
-         return (cupsdCloseClient(con));
+       {
+         cupsdCloseClient(con);
+         return;
+       }
 
        httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
        httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
@@ -1054,24 +1091,40 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
        httpPrintf(HTTP(con), "\r\n");
 
        if (cupsdFlushHeader(con) < 0)
-         return (cupsdCloseClient(con));
+        {
+         cupsdCloseClient(con);
+         return;
+       }
 
-        encrypt_client(con);
+        if (!encrypt_client(con))
+        {
+         cupsdCloseClient(con);
+         return;
+       }
 #else
        if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
-         return (cupsdCloseClient(con));
+       {
+         cupsdCloseClient(con);
+         return;
+       }
 #endif /* HAVE_SSL */
       }
 
       if (!cupsdSendHeader(con, HTTP_OK, NULL))
-       return (cupsdCloseClient(con));
+      {
+       cupsdCloseClient(con);
+       return;
+      }
 
       httpPrintf(HTTP(con), "Allow: GET, HEAD, OPTIONS, POST, PUT\r\n");
       httpPrintf(HTTP(con), "Content-Length: 0\r\n");
       httpPrintf(HTTP(con), "\r\n");
 
       if (cupsdFlushHeader(con) < 0)
-       return (cupsdCloseClient(con));
+      {
+       cupsdCloseClient(con);
+       return;
+      }
     }
     else if (!is_path_absolute(con->uri))
     {
@@ -1080,7 +1133,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
       */
 
       if (!cupsdSendError(con, HTTP_FORBIDDEN))
-       return (cupsdCloseClient(con));
+      {
+       cupsdCloseClient(con);
+       return;
+      }
     }
     else
     {
@@ -1093,7 +1149,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
        */
 
        if (!cupsdSendHeader(con, HTTP_SWITCHING_PROTOCOLS, NULL))
-         return (cupsdCloseClient(con));
+       {
+         cupsdCloseClient(con);
+         return;
+       }
 
        httpPrintf(HTTP(con), "Connection: Upgrade\r\n");
        httpPrintf(HTTP(con), "Upgrade: TLS/1.0,HTTP/1.1\r\n");
@@ -1101,12 +1160,22 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
        httpPrintf(HTTP(con), "\r\n");
 
        if (cupsdFlushHeader(con) < 0)
-         return (cupsdCloseClient(con));
+        {
+         cupsdCloseClient(con);
+         return;
+       }
 
-        encrypt_client(con);
+        if (!encrypt_client(con))
+        {
+         cupsdCloseClient(con);
+         return;
+       }
 #else
        if (!cupsdSendError(con, HTTP_NOT_IMPLEMENTED))
-         return (cupsdCloseClient(con));
+       {
+         cupsdCloseClient(con);
+         return;
+       }
 #endif /* HAVE_SSL */
       }
 
@@ -1116,7 +1185,8 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
                        "cupsdReadClient: Unauthorized request for %s...\n",
                        con->uri);
        cupsdSendError(con, status);
-       return (cupsdCloseClient(con));
+       cupsdCloseClient(con);
+       return;
       }
 
       if (con->http.expect &&
@@ -1129,7 +1199,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
          */
 
          if (!cupsdSendHeader(con, HTTP_CONTINUE, NULL))
-           return (cupsdCloseClient(con));
+         {
+           cupsdCloseClient(con);
+           return;
+         }
        }
        else
        {
@@ -1138,13 +1211,19 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
          */
 
          if (!cupsdSendHeader(con, HTTP_EXPECTATION_FAILED, NULL))
-           return (cupsdCloseClient(con));
+         {
+           cupsdCloseClient(con);
+           return;
+         }
 
          httpPrintf(HTTP(con), "Content-Length: 0\r\n");
          httpPrintf(HTTP(con), "\r\n");
 
          if (cupsdFlushHeader(con) < 0)
-           return (cupsdCloseClient(con));
+          {
+           cupsdCloseClient(con);
+           return;
+         }
        }
       }
 
@@ -1166,7 +1245,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              else
              {
                if (!cupsdSendError(con, HTTP_NOT_FOUND))
-                 return (cupsdCloseClient(con));
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
 
                break;
              }
@@ -1235,7 +1317,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
               if (!cupsdSendCommand(con, con->command, con->options, 0))
              {
                if (!cupsdSendError(con, HTTP_NOT_FOUND))
-                 return (cupsdCloseClient(con));
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
               }
              else
                cupsdLogRequest(con, HTTP_OK);
@@ -1256,7 +1341,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              */
 
              if (!cupsdSendError(con, HTTP_FORBIDDEN))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              break;
            }
@@ -1270,7 +1358,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
                                       sizeof(buf))) == NULL)
              {
                if (!cupsdSendError(con, HTTP_NOT_FOUND))
-                 return (cupsdCloseClient(con));
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
 
                break;
              }
@@ -1287,7 +1378,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
                if (!cupsdSendCommand(con, con->command, con->options, 0))
                {
                  if (!cupsdSendError(con, HTTP_NOT_FOUND))
-                   return (cupsdCloseClient(con));
+                 {
+                   cupsdCloseClient(con);
+                   return;
+                 }
                }
                else
                  cupsdLogRequest(con, HTTP_OK);
@@ -1300,7 +1394,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              if (!check_if_modified(con, &filestats))
               {
                if (!cupsdSendError(con, HTTP_NOT_MODIFIED))
-                 return (cupsdCloseClient(con));
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
              }
              else
               {
@@ -1310,7 +1407,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
                  snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
 
                if (!write_file(con, HTTP_OK, filename, line, &filestats))
-                 return (cupsdCloseClient(con));
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
              }
            }
             break;
@@ -1334,7 +1434,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              */
 
               if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              break;
             }
@@ -1345,7 +1448,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              */
 
               if (!cupsdSendError(con, HTTP_BAD_REQUEST))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              break;
            }
@@ -1437,7 +1543,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
                                       sizeof(buf))) == NULL)
              {
                if (!cupsdSendError(con, HTTP_NOT_FOUND))
-                 return (cupsdCloseClient(con));
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
 
                break;
              }
@@ -1451,7 +1560,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
                */
 
                if (!cupsdSendError(con, HTTP_UNAUTHORIZED))
-                 return (cupsdCloseClient(con));
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
              }
            }
            break;
@@ -1471,7 +1583,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              */
 
              if (!cupsdSendError(con, HTTP_FORBIDDEN))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              break;
            }
@@ -1494,7 +1609,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              */
 
               if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              break;
             }
@@ -1505,7 +1623,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              */
 
               if (!cupsdSendError(con, HTTP_BAD_REQUEST))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              break;
            }
@@ -1525,7 +1646,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
            if (con->file < 0)
            {
              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
            }
 
            fchmod(con->file, 0640);
@@ -1536,7 +1660,8 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
        case HTTP_DELETE :
        case HTTP_TRACE :
             cupsdSendError(con, HTTP_NOT_IMPLEMENTED);
-           return (cupsdCloseClient(con));
+           cupsdCloseClient(con);
+           return;
 
        case HTTP_HEAD :
             if (!strncmp(con->uri, "/printers/", 10) &&
@@ -1554,7 +1679,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              else
              {
                if (!cupsdSendError(con, HTTP_NOT_FOUND))
-                 return (cupsdCloseClient(con));
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
 
                break;
              }
@@ -1573,13 +1701,22 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
              */
 
               if (!cupsdSendHeader(con, HTTP_OK, "text/html"))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              if (httpPrintf(HTTP(con), "\r\n") < 0)
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              if (cupsdFlushHeader(con) < 0)
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
               cupsdLogRequest(con, HTTP_OK);
            }
@@ -1596,7 +1733,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              */
 
              if (!cupsdSendError(con, HTTP_FORBIDDEN))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              break;
            }
@@ -1604,14 +1744,20 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
                                          sizeof(buf))) == NULL)
            {
              if (!cupsdSendHeader(con, HTTP_NOT_FOUND, "text/html"))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
               cupsdLogRequest(con, HTTP_NOT_FOUND);
            }
            else if (!check_if_modified(con, &filestats))
             {
               if (!cupsdSendError(con, HTTP_NOT_MODIFIED))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
               cupsdLogRequest(con, HTTP_NOT_MODIFIED);
            }
@@ -1628,24 +1774,39 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
                snprintf(line, sizeof(line), "%s/%s", type->super, type->type);
 
               if (!cupsdSendHeader(con, HTTP_OK, line))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              if (httpPrintf(HTTP(con), "Last-Modified: %s\r\n",
                             httpGetDateString(filestats.st_mtime)) < 0)
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
              if (httpPrintf(HTTP(con), "Content-Length: %lu\r\n",
                             (unsigned long)filestats.st_size) < 0)
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
 
               cupsdLogRequest(con, HTTP_OK);
            }
 
             if (httpPrintf(HTTP(con), "\r\n") < 0)
-             return (cupsdCloseClient(con));
+           {
+             cupsdCloseClient(con);
+             return;
+           }
 
            if (cupsdFlushHeader(con) < 0)
-             return (cupsdCloseClient(con));
+            {
+             cupsdCloseClient(con);
+             return;
+           }
 
             con->http.state = HTTP_WAITING;
             break;
@@ -1671,35 +1832,45 @@ cupsdReadClient(cupsd_client_t *con)    /* I - Client to read from */
                            "CHUNKED" : "LENGTH",
                        CUPS_LLCAST con->http.data_remaining, con->file);
 
-        if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
-         return (cupsdCloseClient(con));
-       else if (bytes > 0)
+        do
        {
-         con->bytes += bytes;
+          if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
+         {
+           cupsdCloseClient(con);
+           return;
+         }
+         else if (bytes > 0)
+         {
+           con->bytes += bytes;
 
-          cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                         "cupsdReadClient: %d writing %d bytes to %d",
-                         con->http.fd, bytes, con->file);
+            cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                           "cupsdReadClient: %d writing %d bytes to %d",
+                           con->http.fd, bytes, con->file);
 
-          if (write(con->file, line, bytes) < bytes)
-         {
-            cupsdLogMessage(CUPSD_LOG_ERROR,
-                           "cupsdReadClient: Unable to write %d bytes to %s: %s",
-                           bytes, con->filename, strerror(errno));
+            if (write(con->file, line, bytes) < bytes)
+           {
+              cupsdLogMessage(CUPSD_LOG_ERROR,
+                             "cupsdReadClient: Unable to write %d bytes to %s: %s",
+                             bytes, con->filename, strerror(errno));
 
-           cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                           "cupsdReadClient: Closing data file %d...",
-                           con->file);
+             cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                             "cupsdReadClient: Closing data file %d...",
+                             con->file);
 
-           close(con->file);
-           con->file = -1;
-           unlink(con->filename);
-           cupsdClearString(&con->filename);
+             close(con->file);
+             con->file = -1;
+             unlink(con->filename);
+             cupsdClearString(&con->filename);
 
-            if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
-             return (cupsdCloseClient(con));
+              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+             {
+               cupsdCloseClient(con);
+               return;
+             }
+           }
          }
-       }
+        }
+       while (con->http.state == HTTP_PUT_RECV && con->http.used > 0);
 
         if (con->http.state == HTTP_WAITING)
        {
@@ -1732,7 +1903,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
            cupsdClearString(&con->filename);
 
             if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
-             return (cupsdCloseClient(con));
+           {
+             cupsdCloseClient(con);
+             return;
+           }
          }
 
          /*
@@ -1746,7 +1920,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
          */
 
           if (!cupsdSendError(con, status))
-           return (cupsdCloseClient(con));
+         {
+           cupsdCloseClient(con);
+           return;
+         }
        }
         break;
 
@@ -1759,94 +1936,112 @@ cupsdReadClient(cupsd_client_t *con)   /* I - Client to read from */
                            "CHUNKED" : "LENGTH",
                        CUPS_LLCAST con->http.data_remaining, con->file);
 
-        if (con->request)
+        do
        {
-        /*
-         * Grab any request data from the connection...
-         */
-
-         if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
+          if (con->request)
          {
-            cupsdLogMessage(CUPSD_LOG_ERROR,
-                           "cupsdReadClient: %d IPP Read Error!",
-                           con->http.fd);
+          /*
+           * Grab any request data from the connection...
+           */
 
-           cupsdSendError(con, HTTP_BAD_REQUEST);
-           return (cupsdCloseClient(con));
-         }
-         else if (ipp_state != IPP_DATA)
-         {
-            if (con->http.state == HTTP_POST_SEND)
+           if ((ipp_state = ippRead(&(con->http), con->request)) == IPP_ERROR)
            {
+              cupsdLogMessage(CUPSD_LOG_ERROR,
+                             "cupsdReadClient: %d IPP Read Error!",
+                             con->http.fd);
+
              cupsdSendError(con, HTTP_BAD_REQUEST);
-             return (cupsdCloseClient(con));
+             cupsdCloseClient(con);
+             return;
            }
+           else if (ipp_state != IPP_DATA)
+           {
+              if (con->http.state == HTTP_POST_SEND)
+             {
+               cupsdSendError(con, HTTP_BAD_REQUEST);
+               cupsdCloseClient(con);
+               return;
+             }
 
-           break;
-          }
-         else
-           con->bytes += ippLength(con->request);
-       }
+             break;
+            }
+           else
+             con->bytes += ippLength(con->request);
+         }
 
-        if (con->file < 0 && con->http.state != HTTP_POST_SEND)
-       {
-         /*
-         * Create a file as needed for the request data...
-         */
+          if (con->file < 0 && con->http.state != HTTP_POST_SEND)
+         {
+           /*
+           * Create a file as needed for the request data...
+           */
 
-          cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
-         con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
+            cupsdSetStringf(&con->filename, "%s/%08x", RequestRoot, request_id ++);
+           con->file = open(con->filename, O_WRONLY | O_CREAT | O_TRUNC, 0640);
 
-          cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
-                         con->filename, con->file);
+            cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReadClient: %d REQUEST %s=%d", con->http.fd,
+                           con->filename, con->file);
 
-         if (con->file < 0)
-         {
-           if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
-             return (cupsdCloseClient(con));
-         }
+           if (con->file < 0)
+           {
+             if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+             {
+               cupsdCloseClient(con);
+               return;
+             }
+           }
 
-         fchmod(con->file, 0640);
-         fchown(con->file, RunUser, Group);
-          fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
-       }
+           fchmod(con->file, 0640);
+           fchown(con->file, RunUser, Group);
+            fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
+         }
 
-       if (con->http.state != HTTP_POST_SEND)
-       {
-          if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
-           return (cupsdCloseClient(con));
-         else if (bytes > 0)
+         if (con->http.state != HTTP_POST_SEND)
          {
-           con->bytes += bytes;
+            if ((bytes = httpRead2(HTTP(con), line, sizeof(line))) < 0)
+           {
+             cupsdCloseClient(con);
+             return;
+           }
+           else if (bytes > 0)
+           {
+             con->bytes += bytes;
 
-            cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                           "cupsdReadClient: %d writing %d bytes to %d",
-                           con->http.fd, bytes, con->file);
+              cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                             "cupsdReadClient: %d writing %d bytes to %d",
+                             con->http.fd, bytes, con->file);
 
-            if (write(con->file, line, bytes) < bytes)
-           {
-              cupsdLogMessage(CUPSD_LOG_ERROR,
-                             "cupsdReadClient: Unable to write %d bytes to %s: %s",
-                             bytes, con->filename, strerror(errno));
+              if (write(con->file, line, bytes) < bytes)
+             {
+               cupsdLogMessage(CUPSD_LOG_ERROR,
+                               "cupsdReadClient: Unable to write %d bytes to %s: %s",
+                               bytes, con->filename, strerror(errno));
 
-             cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                             "cupsdReadClient: Closing file %d...",
-                             con->file);
+               cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                               "cupsdReadClient: Closing file %d...",
+                               con->file);
 
-             close(con->file);
-             con->file = -1;
-             unlink(con->filename);
-             cupsdClearString(&con->filename);
+               close(con->file);
+               con->file = -1;
+               unlink(con->filename);
+               cupsdClearString(&con->filename);
 
-              if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
-               return (cupsdCloseClient(con));
+               if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
+             }
+           }
+           else if (con->http.state == HTTP_POST_RECV)
+              return;
+           else if (con->http.state != HTTP_POST_SEND)
+           {
+             cupsdCloseClient(con);
+             return;
            }
          }
-         else if (con->http.state == HTTP_POST_RECV)
-            return (1); /* ??? */
-         else if (con->http.state != HTTP_POST_SEND)
-           return (cupsdCloseClient(con));
-       }
+        }
+       while (con->http.state == HTTP_POST_RECV && con->http.used > 0);
 
        if (con->http.state == HTTP_POST_SEND)
        {
@@ -1887,7 +2082,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
               }
 
               if (!cupsdSendError(con, HTTP_REQUEST_TOO_LARGE))
-               return (cupsdCloseClient(con));
+             {
+               cupsdCloseClient(con);
+               return;
+             }
            }
 
            if (con->command)
@@ -1895,7 +2093,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
              if (!cupsdSendCommand(con, con->command, con->options, 0))
              {
                if (!cupsdSendError(con, HTTP_NOT_FOUND))
-                 return (cupsdCloseClient(con));
+               {
+                 cupsdCloseClient(con);
+                 return;
+               }
               }
              else
                cupsdLogRequest(con, HTTP_OK);
@@ -1903,7 +2104,10 @@ cupsdReadClient(cupsd_client_t *con)     /* I - Client to read from */
          }
 
           if (con->request)
-           return (cupsdProcessIPPRequest(con));
+         {
+           cupsdProcessIPPRequest(con);
+           return;
+         }
        }
         break;
 
@@ -1912,9 +2116,7 @@ cupsdReadClient(cupsd_client_t *con)      /* I - Client to read from */
   }
 
   if (!con->http.keep_alive && con->http.state == HTTP_WAITING)
-    return (cupsdCloseClient(con));
-  else
-    return (1);
+    cupsdCloseClient(con);
 }
 
 
@@ -1966,14 +2168,7 @@ cupsdSendCommand(
 
   fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdSendCommand: Adding fd %d to InputSet...", con->file);
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdSendCommand: Adding fd %d to OutputSet...",
-                  con->http.fd);
-
-  FD_SET(con->file, InputSet);
-  FD_SET(con->http.fd, OutputSet);
+  cupsdAddSelect(con->file, (cupsd_selfunc_t)cupsdWritePipe, NULL, con);
 
   con->sent_header = 0;
   con->file_ready  = 0;
@@ -2021,9 +2216,12 @@ cupsdSendError(cupsd_client_t *con,      /* I - Connection */
  /*
   * To work around bugs in some proxies, don't use Keep-Alive for some
   * error messages...
+  *
+  * Kerberos authentication doesn't work without Keep-Alive, so
+  * never disable it in that case.
   */
 
-  if (code >= HTTP_BAD_REQUEST)
+  if (code >= HTTP_BAD_REQUEST && con->http.auth_type != AUTH_KERBEROS)
     con->http.keep_alive = HTTP_KEEPALIVE_OFF;
 
  /*
@@ -2043,8 +2241,8 @@ cupsdSendError(cupsd_client_t *con,       /* I - Connection */
     return (0);
 #endif /* HAVE_SSL */
 
-  if ((con->http.version >= HTTP_1_1 && !con->http.keep_alive) ||
-      (code >= HTTP_BAD_REQUEST && code != HTTP_UPGRADE_REQUIRED))
+  if (con->http.version >= HTTP_1_1 &&
+      con->http.keep_alive == HTTP_KEEPALIVE_OFF)
   {
     if (httpPrintf(HTTP(con), "Connection: close\r\n") < 0)
       return (0);
@@ -2068,7 +2266,8 @@ cupsdSendError(cupsd_client_t *con,       /* I - Connection */
       text = _cupsLangString(con->language,
                              _("Enter your username and password or the "
                               "root username and password to access this "
-                              "page."));
+                              "page. If you are using Kerberos authentication, "
+                              "make sure you have a valid Kerberos ticket."));
     else if (code == HTTP_UPGRADE_REQUIRED)
     {
       text = urltext;
@@ -2082,7 +2281,8 @@ cupsdSendError(cupsd_client_t *con,       /* I - Connection */
               con->servername, con->serverport, con->uri);
 
       snprintf(redirect, sizeof(redirect),
-               "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"3;https://%s:%d%s\">\n",
+               "<META HTTP-EQUIV=\"Refresh\" "
+              "CONTENT=\"3;URL=https://%s:%d%s\">\n",
               con->servername, con->serverport, con->uri);
     }
     else
@@ -2138,6 +2338,9 @@ cupsdSendHeader(cupsd_client_t *con,      /* I - Client to send to */
                 http_status_t  code,   /* I - HTTP status code */
                char           *type)   /* I - MIME type of document */
 {
+  char auth_str[1024];                 /* Authorization string */
+
+
  /*
   * Send the HTTP status header...
   */
@@ -2185,21 +2388,76 @@ cupsdSendHeader(cupsd_client_t *con,    /* I - Client to send to */
       auth_type = DefaultAuthType;
     else
       auth_type = con->best->type;
-      
-    if (auth_type != AUTH_DIGEST)
-    {
-      if (httpPrintf(HTTP(con),
-                     "WWW-Authenticate: Basic realm=\"CUPS\"\r\n") < 0)
-       return (0);
-    }
-    else
+
+    auth_str[0] = '\0';
+
+    if (auth_type == AUTH_BASIC || auth_type == AUTH_BASICDIGEST)
+      strlcpy(auth_str, "Basic realm=\"CUPS\"", sizeof(auth_str));
+    else if (auth_type == AUTH_DIGEST)
+      snprintf(auth_str, sizeof(auth_str), "Digest realm=\"CUPS\", nonce=\"%s\"",
+              con->http.hostname);
+#ifdef HAVE_GSSAPI
+    else if (auth_type == AUTH_KERBEROS && !con->no_negotiate &&
+            con->gss_output_token.length == 0)
+      strlcpy(auth_str, "Negotiate", sizeof(auth_str));
+#endif /* HAVE_GSSAPI */
+
+#ifdef HAVE_AUTHORIZATION_H
+    if (con->best)
     {
-      if (httpPrintf(HTTP(con),
-                     "WWW-Authenticate: Digest realm=\"CUPS\", nonce=\"%s\"\r\n",
-                    con->http.hostname) < 0)
-       return (0);
+      int       i;                     /* Looping var */
+      char     *auth_key;              /* Auth key buffer */
+      size_t   auth_size;              /* Size of remaining buffer */
+
+
+      auth_key  = auth_str + strlen(auth_str);
+      auth_size = sizeof(auth_str) - (auth_key - auth_str);
+
+      for (i = 0; i < con->best->num_names; i ++)
+      {
+       if (!strncasecmp(con->best->names[i], "@AUTHKEY(", 9))
+       {
+         snprintf(auth_key, auth_size, ", authkey=\"%s\"",
+                  con->best->names[i] + 9);
+         /* end parenthesis is stripped in conf.c */
+         break;
+        }
+       else if (!strcasecmp(con->best->names[i], "@SYSTEM") &&
+                SystemGroupAuthKey)
+       {
+         snprintf(auth_key, auth_size, ", authkey=\"%s\"", SystemGroupAuthKey);
+         break;
+       }
+      }
     }
+#endif /* HAVE_AUTHORIZATION_H */
+
+    if (auth_str[0] &&
+       httpPrintf(HTTP(con), "WWW-Authenticate: %s\r\n", auth_str) < 0)
+      return (0);
+  }
+
+#ifdef HAVE_GSSAPI
+ /*
+  * WWW-Authenticate: Negotiate can be included even for
+  * non-401 replies...
+  */
+
+  if (con->gss_output_token.length > 0)
+  {
+    char       buf[2048];              /* Output token buffer */
+    OM_uint32  minor_status;           /* Minor status code */
+
+
+    httpEncode64_2(buf, sizeof(buf),
+                  con->gss_output_token.value,
+                  con->gss_output_token.length);
+    gss_release_buffer(&minor_status, &con->gss_output_token);
+
+    if (httpPrintf(HTTP(con), "WWW-Authenticate: Negotiate %s\r\n", buf) < 0)
+      return (0);
   }
+#endif /* HAVE_GSSAPI */
 
   if (con->language && strcmp(con->language->language, "C"))
   {
@@ -2258,7 +2516,7 @@ cupsdUpdateCGI(void)
  * 'cupsdWriteClient()' - Write data to a client as needed.
  */
 
-int                                    /* O - 1 if success, 0 if fail */
+void
 cupsdWriteClient(cupsd_client_t *con)  /* I - Client connection */
 {
   int          bytes;                  /* Number of bytes written */
@@ -2277,7 +2535,28 @@ cupsdWriteClient(cupsd_client_t *con)    /* I - Client connection */
 
   if (con->http.state != HTTP_GET_SEND &&
       con->http.state != HTTP_POST_SEND)
-    return (1);
+    return;
+
+  if (con->pipe_pid)
+  {
+   /*
+    * Make sure we select on the CGI output...
+    */
+
+    cupsdAddSelect(con->file, (cupsd_selfunc_t)cupsdWritePipe, NULL, con);
+
+    if (!con->file_ready)
+    {
+     /*
+      * Try again later when there is CGI output available...
+      */
+
+      cupsdRemoveSelect(con->http.fd);
+      return;
+    }
+
+    con->file_ready = 0;
+  }
 
   if (con->response)
   {
@@ -2323,7 +2602,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
              con->sent_header = 2;
 
              if (httpPrintf(HTTP(con), "Content-Length: 0\r\n") < 0)
-               return (0);
+               return;
            }
            else if (!strncasecmp(buf, "Status:", 7))
            {
@@ -2338,7 +2617,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
              if (con->http.version == HTTP_1_1)
              {
                if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n") < 0)
-                 return (0);
+                 return;
              }
             }
          }
@@ -2368,7 +2647,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
             if (cupsdFlushHeader(con) < 0)
            {
              cupsdCloseClient(con);
-             return (0);
+             return;
            }
 
            if (con->http.version == HTTP_1_1)
@@ -2393,7 +2672,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
         httpPrintf(HTTP(con), "%s", buf);
 
         con->http.activity = time(NULL);
-        return (1);
+        return;
       }
       else if (bytes == 0)
         con->http.activity = time(NULL);
@@ -2408,7 +2687,7 @@ cupsdWriteClient(cupsd_client_t *con)     /* I - Client connection */
                        con->http.fd, bytes);
 
        cupsdCloseClient(con);
-       return (0);
+       return;
       }
 
       con->bytes += bytes;
@@ -2432,27 +2711,17 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
       if (httpWrite2(HTTP(con), "", 0) < 0)
       {
         cupsdCloseClient(con);
-       return (0);
+       return;
       }
     }
 
     con->http.state = HTTP_WAITING;
 
-    cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                    "cupsdWriteClient: Removing fd %d from OutputSet...",
-                    con->http.fd);
-
-    FD_CLR(con->http.fd, OutputSet);
+    cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient, NULL, con);
 
     if (con->file >= 0)
     {
-      if (FD_ISSET(con->file, InputSet))
-      {
-       cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                       "cupsdWriteClient: Removing fd %d from InputSet...",
-                        con->file);
-       FD_CLR(con->file, InputSet);
-      }
+      cupsdRemoveSelect(con->file);
 
       if (con->pipe_pid)
        cupsdEndProcess(con->pipe_pid, 0);
@@ -2489,29 +2758,33 @@ cupsdWriteClient(cupsd_client_t *con)   /* I - Client connection */
 
     cupsdClearString(&con->command);
     cupsdClearString(&con->options);
+    cupsdClearString(&con->query_string);
 
     if (!con->http.keep_alive)
     {
       cupsdCloseClient(con);
-      return (0);
-    }
-  }
-  else
-  {
-    con->file_ready = 0;
-
-    if (con->pipe_pid && !FD_ISSET(con->file, InputSet))
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdWriteClient: Adding fd %d to InputSet...",
-                     con->file);
-      FD_SET(con->file, InputSet);
+      return;
     }
   }
 
   con->http.activity = time(NULL);
+}
 
-  return (1);
+
+/*
+ * 'cupsdWritePipe()' - Flag that data is available on the CGI pipe.
+ */
+
+void
+cupsdWritePipe(cupsd_client_t *con)    /* I - Client connection */
+{
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdWritePipe: CGI output on fd %d...",
+                  con->file);
+
+  con->file_ready = 1;
+
+  cupsdRemoveSelect(con->file);
+  cupsdAddSelect(con->http.fd, NULL, (cupsd_selfunc_t)cupsdWriteClient, con);
 }
 
 
@@ -2587,6 +2860,7 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
 #  ifdef HAVE_LIBSSL
   SSL_CTX      *context;               /* Context for encryption */
   SSL          *conn;                  /* Connection for encryption */
+  BIO          *bio;                   /* BIO data */
   unsigned long        error;                  /* Error code */
 
 
@@ -2600,7 +2874,7 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
     * Nope, make a self-signed certificate...
     */
 
-    if (!make_certificate())
+    if (!make_certificate(con))
       return (0);
   }
 
@@ -2614,9 +2888,12 @@ encrypt_client(cupsd_client_t *con)      /* I - Client to encrypt */
   SSL_CTX_use_PrivateKey_file(context, ServerKey, SSL_FILETYPE_PEM);
   SSL_CTX_use_certificate_file(context, ServerCertificate, SSL_FILETYPE_PEM);
 
+  bio = BIO_new(_httpBIOMethods());
+  BIO_ctrl(bio, BIO_C_SET_FILE_PTR, 0, (char *)HTTP(con));
+
   conn = SSL_new(context);
+  SSL_set_bio(conn, bio, bio);
 
-  SSL_set_fd(conn, con->http.fd);
   if (SSL_accept(conn) != 1)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
@@ -2638,7 +2915,7 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
 
   con->http.tls = conn;
   return (1);
-  
+
 #  elif defined(HAVE_GNUTLS)
   http_tls_t   *conn;                  /* TLS session object */
   int          error;                  /* Error code */
@@ -2656,7 +2933,7 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
     * Nope, make a self-signed certificate...
     */
 
-    if (!make_certificate())
+    if (!make_certificate(con))
       return (0);
   }
 
@@ -2683,14 +2960,15 @@ encrypt_client(cupsd_client_t *con)     /* I - Client to encrypt */
   }
 
   gnutls_certificate_allocate_credentials(credentials);
-  gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate, 
+  gnutls_certificate_set_x509_key_file(*credentials, ServerCertificate,
                                       ServerKey, GNUTLS_X509_FMT_PEM);
 
   gnutls_init(&(conn->session), GNUTLS_SERVER);
   gnutls_set_default_priority(conn->session);
   gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, *credentials);
-  gnutls_transport_set_ptr(conn->session,
-                           (gnutls_transport_ptr)((long)con->http.fd));
+  gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr)HTTP(con));
+  gnutls_transport_set_pull_function(conn->session, _httpReadGNUTLS);
+  gnutls_transport_set_push_function(conn->session, _httpWriteGNUTLS);
 
   error = gnutls_handshake(conn->session);
 
@@ -2720,7 +2998,6 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
 #  elif defined(HAVE_CDSASSL)
   OSStatus     error;                  /* Error code */
   http_tls_t   *conn;                  /* CDSA connection information */
-  cdsa_conn_ref_t u;                   /* Connection reference union */
 
 
   if ((conn = (http_tls_t *)malloc(sizeof(http_tls_t))) == NULL)
@@ -2728,7 +3005,7 @@ encrypt_client(cupsd_client_t *con)       /* I - Client to encrypt */
 
   error            = 0;
   conn->session    = NULL;
-  conn->certsArray = get_cdsa_server_certs();
+  conn->certsArray = get_cdsa_certificate(con);
 
   if (!conn->certsArray)
   {
@@ -2736,14 +3013,14 @@ encrypt_client(cupsd_client_t *con)     /* I - Client to encrypt */
     * No keychain (yet), make a self-signed certificate...
     */
 
-    if (make_certificate())
-      conn->certsArray = get_cdsa_server_certs();
+    if (make_certificate(con))
+      conn->certsArray = get_cdsa_certificate(con);
   }
 
   if (!conn->certsArray)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                   "EncryptClient: Could not find signing key in keychain "
+                   "encrypt_client: Could not find signing key in keychain "
                    "\"%s\"", ServerCertificate);
     error = errSSLBadCert; /* errSSLBadConfiguration is a better choice, but not available on 10.2.x */
   }
@@ -2758,15 +3035,7 @@ encrypt_client(cupsd_client_t *con)      /* I - Client to encrypt */
     error = SSLSetProtocolVersionEnabled(conn->session, kSSLProtocol2, false);
 
   if (!error)
-  {
-   /*
-    * Use a union to resolve warnings about int/pointer size mismatches...
-    */
-
-    u.connection = NULL;
-    u.sock       = con->http.fd;
-    error        = SSLSetConnection(conn->session, u.connection);
-  }
+    error = SSLSetConnection(conn->session, HTTP(con));
 
   if (!error)
     error = SSLSetAllowsExpiredCerts(conn->session, true);
@@ -2824,90 +3093,120 @@ encrypt_client(cupsd_client_t *con)    /* I - Client to encrypt */
 
 #ifdef HAVE_CDSASSL
 /*
- * 'get_cdsa_server_certs()' - Convert a keychain name into the CFArrayRef
- *                            required by SSLSetCertificate.
- *
- * For now we assumes that there is exactly one SecIdentity in the
- * keychain - i.e. there is exactly one matching cert/private key pair.
- * In the future we will search a keychain for a SecIdentity matching a
- * specific criteria.  We also skip the operation of adding additional
- * non-signing certs from the keychain to the CFArrayRef.
- *
- * To create a self-signed certificate for testing use the certtool.
- * Executing the following as root will do it:
- *
- *     certtool c k=/Library/Keychains/System.keychain
+ * 'get_cdsa_certificate()' - Get a SSL/TLS certificate from the System keychain.
  */
 
-static CFArrayRef                      /* O - Array of certificates */
-get_cdsa_server_certs(void)
+static CFArrayRef                              /* O - Array of certificates */
+get_cdsa_certificate(cupsd_client_t *con)      /* I - Client connection */
 {
   OSStatus             err;            /* Error info */
-  SecKeychainRef       kcRef;          /* Keychain reference */
-  SecIdentitySearchRef srchRef;        /* Search reference */
+  SecKeychainRef       keychain;       /* Keychain reference */
+  SecIdentitySearchRef search;         /* Search reference */
   SecIdentityRef       identity;       /* Identity */
-  CFArrayRef           ca;             /* Certificate array */
+  CFArrayRef           certificates = NULL;
+                                       /* Certificate array */
 
 
-  kcRef    = NULL;
-  srchRef  = NULL;
-  identity = NULL;
-  ca      = NULL;
-  err      = SecKeychainOpen(ServerCertificate, &kcRef);
+  if ((err = SecKeychainOpen(ServerCertificate, &keychain)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\", %s",
+                   ServerCertificate, cssmErrorString(err));
+    return (NULL);
+  }
 
-  if (err)
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot open keychain \"%s\", error %d.",
-                   ServerCertificate, (int)err);
-  else
+#  if HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY
+ /*
+  * Use a policy to search for valid certificates who's common name matches the
+  * servername...
+  */
+
+  SecPolicySearchRef   policy_search;  /* Policy search ref */
+  SecPolicyRef         policy;         /* Policy ref */
+  CSSM_DATA            options;        /* Policy options */
+  CSSM_APPLE_TP_SSL_OPTIONS
+                       ssl_options;    /* SSL Option for hostname */
+
+
+  if ((err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_SSL, 
+                             NULL, &policy_search)))
   {
-   /*
-    * Search for "any" identity matching specified key use; 
-    * in this app, we expect there to be exactly one. 
-    */
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create a policy search reference");
+    CFRelease(keychain);
+    return (NULL);
+  }
 
-    err = SecIdentitySearchCreate(kcRef, CSSM_KEYUSE_SIGN, &srchRef);
+  if ((err = SecPolicySearchCopyNext(policy_search, &policy)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, 
+                   "Cannot find a policy to use for searching");
+    CFRelease(keychain);
+    CFRelease(policy_search);
+    return (NULL);
+  }
 
-    if (err)
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                     "Cannot find signing key in keychain \"%s\", error %d",
-                     ServerCertificate, (int)err);
-    else
-    {
-      err = SecIdentitySearchCopyNext(srchRef, &identity);
+  memset(&ssl_options, 0, sizeof(ssl_options));
+  ssl_options.Version = CSSM_APPLE_TP_SSL_OPTS_VERSION;
+  ssl_options.ServerName = con->servername;
+  ssl_options.ServerNameLen = strlen(con->servername);
 
-      if (err)
-       cupsdLogMessage(CUPSD_LOG_DEBUG2,
+  options.Data = (uint8 *)&ssl_options;
+  options.Length = sizeof(ssl_options);
+
+  if ((err = SecPolicySetValue(policy, &options)))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, 
+                   "Cannot set policy value to use for searching");
+    CFRelease(keychain);
+    CFRelease(policy_search);
+    return (NULL);
+  }
+
+  err = SecIdentitySearchCreateWithPolicy(policy, NULL, CSSM_KEYUSE_SIGN,
+                                         keychain, FALSE, &search);
+#  else
+ /*
+  * Assume there is exactly one SecIdentity in the keychain...
+  */
+
+  err = SecIdentitySearchCreate(keychain, CSSM_KEYUSE_SIGN, &search);
+#  endif /* HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */
+
+  if (err)
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "Cannot create keychain search reference: %s", 
+                   cssmErrorString(err));
+  else
+  {
+    if ((err = SecIdentitySearchCopyNext(search, &identity)))
+    {
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
                        "Cannot find signing key in keychain \"%s\", error %d",
                        ServerCertificate, (int)err);
+    }
+    else
+    {
+      if (CFGetTypeID(identity) != SecIdentityGetTypeID())
+       cupsdLogMessage(CUPSD_LOG_ERROR,
+                       "SecIdentitySearchCopyNext CFTypeID failure!");
       else
       {
-       if (CFGetTypeID(identity) != SecIdentityGetTypeID())
-         cupsdLogMessage(CUPSD_LOG_ERROR,
-                         "SecIdentitySearchCopyNext CFTypeID failure!");
-       else
-       {
-        /* 
-         * Found one. Place it in a CFArray. 
-         * TBD: snag other (non-identity) certs from keychain and add them
-         * to array as well.
-         */
-
-         ca = CFArrayCreate(NULL, (const void **)&identity, 1, &kCFTypeArrayCallBacks);
-
-         if (ca == nil)
-           cupsdLogMessage(CUPSD_LOG_ERROR, "CFArrayCreate error");
-       }
-
-       CFRelease(identity);
+      if ((certificates = CFArrayCreate(NULL, (const void **)&identity, 
+                                     1, &kCFTypeArrayCallBacks)) == NULL)
+       cupsdLogMessage(CUPSD_LOG_ERROR, "Cannot create certificate array");
       }
 
-      CFRelease(srchRef);
+      CFRelease(identity);
     }
 
-    CFRelease(kcRef);
+    CFRelease(search);
   }
 
-  return (ca);
+#  if HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY
+  CFRelease(policy);
+  CFRelease(policy_search);
+#  endif /* HAVE_SECIDENTITYSEARCHCREATEWITHPOLICY */
+
+  return (certificates);
 }
 #endif /* HAVE_CDSASSL */
 
@@ -2928,11 +3227,13 @@ get_file(cupsd_client_t *con,           /* I  - Client connection */
 
 
  /*
-  * Need to add DocumentRoot global...
+  * Figure out the real filename...
   */
 
   if (!strncmp(con->uri, "/ppd/", 5))
     snprintf(filename, len, "%s%s", ServerRoot, con->uri);
+  else if (!strncmp(con->uri, "/rss/", 5) && !strchr(con->uri + 5, '/'))
+    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 (!strncmp(con->uri, "/admin/log/", 11))
@@ -3247,8 +3548,13 @@ is_cgi(cupsd_client_t *con,              /* I - Client connection */
   */
 
   if ((options = strchr(con->uri, '?')) != NULL)
+  {
     options ++;
 
+    if (strchr(options, '='))
+      cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", options);
+  }
+
  /*
   * Check for known types...
   */
@@ -3410,13 +3716,13 @@ is_path_absolute(const char *path)      /* I - Input path */
  */
 
 static int                             /* O - 1 on success, 0 on failure */
-make_certificate(void)
+make_certificate(cupsd_client_t *con)  /* I - Client connection */
 {
 #if defined(HAVE_LIBSSL) && defined(HAVE_WAITPID)
   int          pid,                    /* Process ID of command */
                status;                 /* Status of command */
   char         command[1024],          /* Command */
-               *argv[11],              /* Command-line arguments */
+               *argv[12],              /* Command-line arguments */
                *envp[MAX_ENV + 1],     /* Environment variables */
                home[1024],             /* HOME environment variable */
                infofile[1024],         /* Type-in information for cert */
@@ -3492,7 +3798,7 @@ make_certificate(void)
     envp[envc++] = home;
     envp[envc]   = NULL;
 
-    if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, 1, &pid))
+    if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, -1, 1, &pid))
     {
       unlink(seedfile);
       return (0);
@@ -3569,7 +3875,7 @@ make_certificate(void)
 
   infofd = open(infofile, O_RDONLY);
 
-  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, 1, &pid))
+  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, &pid))
   {
     close(infofd);
     unlink(infofile);
@@ -3747,18 +4053,19 @@ make_certificate(void)
   return (1);
 
 #elif defined(HAVE_CDSASSL) && defined(HAVE_WAITPID)
-  int  pid,                            /* Process ID of command */
-       status;                         /* Status of command */
-  char command[1024],                  /* Command */
-       keychain[1024],                 /* Keychain argument */
-       *argv[5],                       /* Command-line arguments */
-       *envp[MAX_ENV];                 /* Environment variables */
+  int          pid,                    /* Process ID of command */
+               status;                 /* Status of command */
+  char         command[1024],          /* Command */
+               *argv[4],               /* Command-line arguments */
+               *envp[MAX_ENV + 1],     /* Environment variables */
+               keychain[1024],         /* Keychain argument */
+               infofile[1024];         /* Type-in information for cert */
+  cups_file_t  *fp;                    /* Seed/info file */
+  int          infofd;                 /* Info file descriptor */
 
 
  /*
-  * Run the "certtool" command to generate a self-signed certificate:
-  *
-  *     certtool c Z k=ServerCertificate
+  * Run the "certtool" command to generate a self-signed certificate...
   */
 
   if (!cupsFileFind("certtool", getenv("PATH"), 1, command, sizeof(command)))
@@ -3768,6 +4075,25 @@ make_certificate(void)
     return (0);
   }
 
+ /*
+  * Create a file with the certificate information fields...
+  *
+  * Note: This assumes that the default questions are asked by the certtool
+  * command...
+  */
+
+  if ((fp = cupsTempFile2(infofile, sizeof(infofile))) == NULL)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to create certificate information file %s - %s",
+                    infofile, strerror(errno));
+    return (0);
+  }
+
+  cupsFilePrintf(fp, "%s\nr\n\ny\nb\ns\ny\n%s\n\n\n\n\n%s\ny\n", 
+                con->servername, con->servername, ServerAdmin);
+  cupsFileClose(fp);
+
   cupsdLogMessage(CUPSD_LOG_INFO,
                   "Generating SSL server key and certificate...");
 
@@ -3775,14 +4101,22 @@ make_certificate(void)
 
   argv[0] = "certtool";
   argv[1] = "c";
-  argv[2] = "Z";
-  argv[3] = keychain;
-  argv[4] = NULL;
+  argv[2] = keychain;
+  argv[3] = NULL;
 
   cupsdLoadEnv(envp, MAX_ENV);
 
-  if (!cupsdStartProcess(command, argv, envp, -1, -1, -1, -1, 1, &pid))
+  infofd = open(infofile, O_RDONLY);
+
+  if (!cupsdStartProcess(command, argv, envp, infofd, -1, -1, -1, -1, 1, &pid))
+  {
+    close(infofd);
+    unlink(infofile);
     return (0);
+  }
+
+  close(infofd);
+  unlink(infofile);
 
   while (waitpid(pid, &status, 0) < 0)
     if (errno != EINTR)
@@ -3844,14 +4178,14 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
   int          envc;                   /* Number of environment variables */
   char         argbuf[10240],          /* Argument buffer */
                *argv[100],             /* Argument strings */
-               *envp[MAX_ENV + 17];    /* Environment variables */
+               *envp[MAX_ENV + 18];    /* Environment variables */
   char         content_length[1024],   /* CONTENT_LENGTH environment variable */
                content_type[1024],     /* CONTENT_TYPE environment variable */
                http_cookie[32768],     /* HTTP_COOKIE environment variable */
+               http_referer[1024],     /* HTTP_REFERER environment variable */
                http_user_agent[1024],  /* HTTP_USER_AGENT environment variable */
                lang[1024],             /* LANG environment variable */
                path_info[1024],        /* PATH_INFO environment variable */
-               *query_string,          /* QUERY_STRING env variable */
                remote_addr[1024],      /* REMOTE_ADDR environment variable */
                remote_host[1024],      /* REMOTE_HOST environment variable */
                remote_user[1024],      /* REMOTE_USER environment variable */
@@ -3882,8 +4216,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
                   "pipe_command: command=\"%s\", options=\"%s\"",
                   command, options ? options : "(null)");
 
-  argv[0]      = command;
-  query_string = NULL;
+  argv[0] = command;
 
   if (options)
     strlcpy(argbuf, options, sizeof(argbuf));
@@ -3910,10 +4243,12 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
     path_info[0] = '\0';
   }
 
-  if (*commptr == '?' && con->operation == HTTP_GET)
+  cupsdLogMessage(CUPSD_LOG_INFO, "commptr=\"%s\"", commptr);
+
+  if (*commptr == '?' && con->operation == HTTP_GET && !con->query_string)
   {
     commptr ++;
-    cupsdSetStringf(&query_string, "QUERY_STRING=%s", commptr);
+    cupsdSetStringf(&(con->query_string), "QUERY_STRING=%s", commptr);
   }
 
   argc = 1;
@@ -4042,20 +4377,24 @@ pipe_command(cupsd_client_t *con,       /* I - Client connection */
     envp[envc ++] = http_user_agent;
   }
 
-  if (con->operation == HTTP_GET)
+  if (con->http.fields[HTTP_FIELD_REFERER][0])
   {
-    for (i = 0; i < argc; i ++)
-      cupsdLogMessage(CUPSD_LOG_DEBUG2, "argv[%d] = \"%s\"", i, argv[i]);
+    snprintf(http_referer, sizeof(http_referer), "HTTP_REFERER=%s",
+             con->http.fields[HTTP_FIELD_REFERER]);
+    envp[envc ++] = http_referer;
+  }
 
+  if (con->operation == HTTP_GET)
+  {
     envp[envc ++] = "REQUEST_METHOD=GET";
 
-    if (query_string)
+    if (con->query_string)
     {
      /*
       * Add GET form variables after ?...
       */
 
-      envp[envc ++] = query_string;
+      envp[envc ++] = con->query_string;
     }
   }
   else
@@ -4099,8 +4438,6 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
 
   if (cupsdOpenPipe(fds))
   {
-    cupsdClearString(&query_string);
-
     cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create pipes for CGI %s - %s",
                     argv[0], strerror(errno));
     return (0);
@@ -4111,7 +4448,7 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
   */
 
   if (cupsdStartProcess(command, argv, envp, infile, fds[1], CGIPipes[1],
-                       -1, root, &pid) < 0)
+                       -1, -1, root, &pid) < 0)
   {
    /*
     * Error - can't fork!
@@ -4138,8 +4475,6 @@ pipe_command(cupsd_client_t *con, /* I - Client connection */
     close(fds[1]);
   }
 
-  cupsdClearString(&query_string);
-
   return (pid);
 }
 
@@ -4190,15 +4525,13 @@ write_file(cupsd_client_t *con,         /* I - Client connection */
   else
     con->http._data_remaining = INT_MAX;
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "write_file: Adding fd %d to OutputSet...", con->http.fd);
-
-  FD_SET(con->http.fd, OutputSet);
+  cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
+                 (cupsd_selfunc_t)cupsdWriteClient, con);
 
   return (1);
 }
 
 
 /*
- * End of "$Id: client.c 6027 2006-10-11 21:04:58Z mike $".
+ * End of "$Id: client.c 6329 2007-03-12 14:48:28Z mike $".
  */