]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/util.c
Import CUPS 1.4svn-r7226.
[thirdparty/cups.git] / cups / util.c
index 2ab77291050a98585961752de6f84ccb5ba71473..6dea980850a76c1dfb5e0171139da36b619f5215 100644 (file)
@@ -1,46 +1,42 @@
 /*
- * "$Id: util.c 5064 2006-02-03 16:51:05Z mike $"
+ * "$Id: util.c 7014 2007-10-10 21:57:43Z mike $"
  *
  *   Printing utilities for the Common UNIX Printing System (CUPS).
  *
+ *   Copyright 2007-2008 by Apple Inc.
  *   Copyright 1997-2006 by Easy Software Products.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  *   This file is subject to the Apple OS-Developed Software exception.
  *
  * Contents:
  *
  *   cupsCancelJob()        - Cancel a print job on the default server.
- *   cupsDoFileRequest()    - Do an IPP request.
+ *   cupsCancelJob2()       - Cancel or purge a print job.
+ *   cupsCreateJob()        - Create an empty job.
+ *   cupsFinishDocument()   - Finish sending a document.
  *   cupsFreeJobs()         - Free memory used by job data.
  *   cupsGetClasses()       - Get a list of printer classes from the default
  *                            server.
- *   cupsGetDefault()       - Get the default printer or class from the default
+ *   cupsGetDefault()       - Get the default printer or class for the default
+ *                            server.
+ *   cupsGetDefault2()      - Get the default printer or class for the specified
  *                            server.
- *   cupsGetDefault2()      - Get the default printer or class from the
- *                            specified server.
  *   cupsGetJobs()          - Get the jobs from the default server.
  *   cupsGetJobs2()         - Get the jobs from the specified server.
  *   cupsGetPPD()           - Get the PPD file for a printer on the default
  *                            server.
- *   cupsGetPPD2()          - Get the PPD file for a printer on the specified
+ *   cupsGetPPD2()          - Get the PPD file for a printer from the specified
  *                            server.
+ *   cupsGetPPD3()          - Get the PPD file for a printer on the specified
+ *                            server if it has changed.
  *   cupsGetPrinters()      - Get a list of printers from the default server.
+ *   cupsGetServerPPD()     - Get an available PPD file from the server.
  *   cupsLastError()        - Return the last IPP status code.
  *   cupsLastErrorString()  - Return the last IPP status-message.
  *   cupsPrintFile()        - Print a file to a printer or class on the default
  *                            the default server.
  *   cupsPrintFiles2()      - Print one or more files to a printer or class on
  *                            the specified server.
- *   cups_connect()         - Connect to the specified host...
+ *   cupsStartDocument()    - Add a document to a job created with
+ *                            cupsCreateJob().
+ *   _cupsConnect()         - Get the default server connection...
  *   cups_get_printer_uri() - Get the printer-uri-supported attribute for the
  *                            first printer in a class.
- *   cups_set_error()       - Set the last IPP status code and status-message.
  */
 
 /*
  * Local functions...
  */
 
-static char    *cups_connect(const char *name, char *printer, char *hostname);
 static int     cups_get_printer_uri(http_t *http, const char *name,
                                     char *host, int hostsize, int *port,
                                     char *resource, int resourcesize,
                                     int depth);
-static void    cups_set_error(ipp_status_t status, const char *message);
 
 
 /*
@@ -95,39 +90,51 @@ static void cups_set_error(ipp_status_t status, const char *message);
 
 int                                    /* O - 1 on success, 0 on failure */
 cupsCancelJob(const char *name,                /* I - Name of printer or class */
-              int        job)          /* I - Job ID */
+              int        job_id)       /* I - Job ID */
 {
-  char         printer[HTTP_MAX_URI],  /* Printer name */
-               hostname[HTTP_MAX_URI], /* Hostname */
-               uri[HTTP_MAX_URI];      /* Printer URI */
-  ipp_t                *request,               /* IPP request */
-               *response;              /* IPP response */
-  cups_lang_t  *language;              /* Language info */
-  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
+  return (cupsCancelJob2(CUPS_HTTP_DEFAULT, job_id, 0)
+              < IPP_REDIRECTION_OTHER_SITE);
+}
+
+
+/*
+ * 'cupsCancelJob2()' - Cancel or purge a print job.
+ *
+ * Canceled jobs remain in the job history while purged jobs are removed
+ * from the job history.
+ *
+ * Use the cupsLastError() and cupsLastErrorString() functions to get
+ * the cause of any failure.
+ *
+ * @since CUPS 1.4@
+ */
+
+ipp_status_t                           /* O - IPP status */
+cupsCancelJob2(http_t *http,           /* I - HTTP connection or CUPS_HTTP_DEFAULT */
+               int    job_id,          /* I - Job ID */
+              int    purge)            /* I - 1 to purge, 0 to cancel */
+{
+  char         uri[HTTP_MAX_URI];      /* Job URI */
+  ipp_t                *request;               /* IPP request */
 
 
  /*
-  * See if we can connect to the server...
+  * Range check input...
   */
 
-  if (!cups_connect(name, printer, hostname))
+  if (job_id <= 0)
   {
-    DEBUG_puts("Unable to connect to server!");
-
+    _cupsSetError(IPP_INTERNAL_ERROR, strerror(EINVAL));
     return (0);
   }
 
  /*
-  * Create a printer URI...
+  * Connect to the default server as needed...
   */
 
-  if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                       "localhost", 0, "/printers/%s", printer) != HTTP_URI_OK)
-  {
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
-
-    return (0);
-  }
+  if (!http)
+    if ((http = _cupsConnect()) == NULL)
+      return (IPP_SERVICE_UNAVAILABLE);
 
  /*
   * Build an IPP_CANCEL_JOB request, which requires the following
@@ -135,388 +142,130 @@ cupsCancelJob(const char *name,         /* I - Name of printer or class */
   *
   *    attributes-charset
   *    attributes-natural-language
-  *    printer-uri
-  *    job-id
-  *    [requesting-user-name]
+  *    job-uri
+  *    requesting-user-name
+  *    [purge-job]
   */
 
-  request = ippNew();
-
-  request->request.op.operation_id = IPP_CANCEL_JOB;
-  request->request.op.request_id   = 1;
-
-  language = cupsLangDefault();
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
+  request = ippNewRequest(IPP_CANCEL_JOB);
 
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL,
-               language != NULL ? language->language : "C");
-
-  cupsLangFree(language);
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, uri);
-
-  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job);
+  snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", job_id);
 
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
                NULL, cupsUser());
+  if (purge)
+    ippAddBoolean(request, IPP_TAG_OPERATION, "purge-job", 1);
 
  /*
   * Do the request...
   */
 
-  if ((response = cupsDoRequest(cg->http, request, "/jobs/")) != NULL)
-    ippDelete(response);
+  ippDelete(cupsDoRequest(http, request, "/jobs/"));
 
-  return (cg->last_error < IPP_REDIRECTION_OTHER_SITE);
+  return (cupsLastError());
 }
 
 
 /*
- * 'cupsDoFileRequest()' - Do an IPP request.
+ * 'cupsCreateJob()' - Create an empty job.
+ *
+ * Submit files for printing to the job using the cupsStartDocument(),
+ * cupsWriteRequestData(), and cupsFinishDocument() functions.
  *
- * This function sends any IPP request to the specified server, retrying
- * and authenticating as necessary.
+ * @since CUPS 1.4@
  */
 
-ipp_t *                                        /* O - Response data */
-cupsDoFileRequest(http_t     *http,    /* I - HTTP connection to server */
-                  ipp_t      *request, /* I - IPP request */
-                  const char *resource,        /* I - HTTP resource for POST */
-                 const char *filename) /* I - File to send or NULL for none */
+int                                    /* O - Job ID or 0 on error */
+cupsCreateJob(
+    http_t        *http,               /* I - HTTP connection or CUPS_HTTP_DEFAULT */
+    const char    *name,               /* I - Printer or class name */
+    const char    *title,              /* I - Title of job */
+    int           num_options,         /* I - Number of options */
+    cups_option_t *options)            /* I - Options */
 {
-  ipp_t                *response;              /* IPP response data */
-  size_t       length;                 /* Content-Length value */
-  http_status_t        status;                 /* Status of HTTP request */
-  FILE         *file;                  /* File to send */
-  struct stat  fileinfo;               /* File information */
-  int          bytes;                  /* Number of bytes read/written */
-  char         buffer[65536];          /* Output buffer */
-
-
-  DEBUG_printf(("cupsDoFileRequest(%p, %p, \'%s\', \'%s\')\n",
-                http, request, resource ? resource : "(null)",
-               filename ? filename : "(null)"));
+  char         printer_uri[1024],      /* Printer URI */
+               resource[1024];         /* Printer resource */
+  ipp_t                *request,               /* Create-Job request */
+               *response;              /* Create-Job response */
+  ipp_attribute_t *attr;               /* job-id attribute */
+  int          job_id = 0;             /* job-id value */
 
-  if (http == NULL || request == NULL || resource == NULL)
-  {
-    if (request != NULL)
-      ippDelete(request);
 
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
-
-    return (NULL);
-  }
+  DEBUG_printf(("cupsCreateJob(http=%p, name=\"%s\", title=\"%s\", "
+                "num_options=%d, options=%p)\n",
+                http, name, title, num_options, options));
 
  /*
-  * See if we have a file to send...
+  * Range check input...
   */
 
-  if (filename != NULL)
+  if (!name)
   {
-    if (stat(filename, &fileinfo))
-    {
-     /*
-      * Can't get file information!
-      */
-
-      cups_set_error(errno == ENOENT ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
-                     strerror(errno));
-
-      ippDelete(request);
-
-      return (NULL);
-    }
-
-#ifdef WIN32
-    if (fileinfo.st_mode & _S_IFDIR)
-#else
-    if (S_ISDIR(fileinfo.st_mode))
-#endif /* WIN32 */
-    {
-     /*
-      * Can't send a directory...
-      */
-
-      ippDelete(request);
-
-      cups_set_error(IPP_NOT_POSSIBLE, NULL);
-
-      return (NULL);
-    }
-
-    if ((file = fopen(filename, "rb")) == NULL)
-    {
-     /*
-      * Can't open file!
-      */
-
-      cups_set_error(errno == ENOENT ? IPP_NOT_FOUND : IPP_NOT_AUTHORIZED,
-                     strerror(errno));
-
-      ippDelete(request);
-
-      return (NULL);
-    }
+    _cupsSetError(IPP_INTERNAL_ERROR, NULL);
+    return (0);
   }
-  else
-    file = NULL;
 
  /*
-  * Loop until we can send the request without authorization problems.
+  * Build a Create-Job request...
   */
 
-  response = NULL;
-  status   = HTTP_ERROR;
-
-  while (response == NULL)
+  if ((request = ippNewRequest(IPP_CREATE_JOB)) == NULL)
   {
-    DEBUG_puts("cupsDoFileRequest: setup...");
-
-   /*
-    * Setup the HTTP variables needed...
-    */
-
-    length = ippLength(request);
-    if (filename)
-      length += fileinfo.st_size;
-
-    httpClearFields(http);
-    httpSetLength(http, length);
-    httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
-    httpSetField(http, HTTP_FIELD_AUTHORIZATION, http->authstring);
-
-    DEBUG_printf(("cupsDoFileRequest: authstring=\"%s\"\n", http->authstring));
-
-   /*
-    * Try the request...
-    */
-
-    DEBUG_puts("cupsDoFileRequest: post...");
-
-    if (httpPost(http, resource))
-    {
-      if (httpReconnect(http))
-      {
-        status = HTTP_ERROR;
-        break;
-      }
-      else
-        continue;
-    }
-
-   /*
-    * Send the IPP data and wait for the response...
-    */
-
-    DEBUG_puts("cupsDoFileRequest: ipp write...");
-
-    request->state = IPP_IDLE;
-    status         = HTTP_CONTINUE;
-
-    if (ippWrite(http, request) != IPP_ERROR)
-      if (filename != NULL)
-      {
-        DEBUG_puts("cupsDoFileRequest: file write...");
-
-       /*
-        * Send the file...
-        */
-
-        rewind(file);
-
-        while ((bytes = (int)fread(buffer, 1, sizeof(buffer), file)) > 0)
-       {
-         if (httpCheck(http))
-         {
-           if ((status = httpUpdate(http)) != HTTP_CONTINUE)
-             break;
-          }
-
-         if (httpWrite2(http, buffer, bytes) < bytes)
-            break;
-        }
-      }
-
-   /*
-    * Get the server's return status...
-    */
-
-    DEBUG_puts("cupsDoFileRequest: update...");
-
-    while (status == HTTP_CONTINUE)
-      status = httpUpdate(http);
-
-    DEBUG_printf(("cupsDoFileRequest: status = %d\n", status));
-
-    if (status == HTTP_UNAUTHORIZED)
-    {
-      DEBUG_puts("cupsDoFileRequest: unauthorized...");
-
-     /*
-      * Flush any error message...
-      */
-
-      httpFlush(http);
-
-     /*
-      * See if we can do authentication...
-      */
-
-      if (cupsDoAuthentication(http, "POST", resource))
-        break;
-
-      if (httpReconnect(http))
-      {
-        status = HTTP_ERROR;
-       break;
-      }
-
-      continue;
-    }
-    else if (status == HTTP_ERROR)
-    {
-#ifdef WIN32
-      if (http->error != WSAENETDOWN && http->error != WSAENETUNREACH)
-#else
-      if (http->error != ENETDOWN && http->error != ENETUNREACH)
-#endif /* WIN32 */
-        continue;
-      else
-        break;
-    }
-#ifdef HAVE_SSL
-    else if (status == HTTP_UPGRADE_REQUIRED)
-    {
-      /* Flush any error message... */
-      httpFlush(http);
-
-      /* Reconnect... */
-      if (httpReconnect(http))
-      {
-        status = HTTP_ERROR;
-        break;
-      }
-
-      /* Upgrade with encryption... */
-      httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
-
-      /* Try again, this time with encryption enabled... */
-      continue;
-    }
-#endif /* HAVE_SSL */
-    else if (status != HTTP_OK)
-    {
-      DEBUG_printf(("cupsDoFileRequest: error %d...\n", status));
-
-     /*
-      * Flush any error message...
-      */
-
-      httpFlush(http);
-      break;
-    }
-    else
-    {
-     /*
-      * Read the response...
-      */
-
-      DEBUG_puts("cupsDoFileRequest: response...");
-
-      response = ippNew();
-
-      if (ippRead(http, response) == IPP_ERROR)
-      {
-       /*
-        * Delete the response...
-       */
-
-        DEBUG_puts("IPP read error!");
-       ippDelete(response);
-       response = NULL;
+    _cupsSetError(IPP_INTERNAL_ERROR, NULL);
+    return (0);
+  }
 
-        cups_set_error(IPP_SERVICE_UNAVAILABLE, strerror(errno));
+  httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
+                   NULL, "localhost", ippPort(), "/printers/%s", name);
+  snprintf(resource, sizeof(resource), "/printers/%s", name);
 
-       break;
-      }
-    }
-  }
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+               NULL, printer_uri);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+               NULL, cupsUser());
+  if (title)
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
+                 title);
+  cupsEncodeOptions(request, num_options, options);
 
  /*
-  * Close the file if needed...
+  * Send the request and get the job-id...
   */
 
-  if (filename != NULL)
-    fclose(file);
+  response = cupsDoRequest(http, request, resource);
 
- /*
-  * Flush any remaining data...
-  */
+  if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) != NULL)
+    job_id = attr->values[0].integer;
 
-  httpFlush(http);
+  ippDelete(response);
 
  /*
-  * Delete the original request and return the response...
+  * Return it...
   */
-  
-  ippDelete(request);
-
-  if (response)
-  {
-    ipp_attribute_t    *attr;          /* status-message attribute */
-
-
-    attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
 
-    cups_set_error(response->request.status.status_code,
-                   attr ? attr->values[0].string.text :
-                      ippErrorString(response->request.status.status_code));
-  }
-  else if (status != HTTP_OK)
-  {
-    switch (status)
-    {
-      case HTTP_NOT_FOUND :
-          cups_set_error(IPP_NOT_FOUND, httpStatus(status));
-         break;
-
-      case HTTP_UNAUTHORIZED :
-          cups_set_error(IPP_NOT_AUTHORIZED, httpStatus(status));
-         break;
+  return (job_id);
+}
 
-      case HTTP_FORBIDDEN :
-          cups_set_error(IPP_FORBIDDEN, httpStatus(status));
-         break;
 
-      case HTTP_BAD_REQUEST :
-          cups_set_error(IPP_BAD_REQUEST, httpStatus(status));
-         break;
+/*
+ * 'cupsFinishDocument()' - Finish sending a document.
+ *
+ * @since CUPS 1.4@
+ */
 
-      case HTTP_REQUEST_TOO_LARGE :
-          cups_set_error(IPP_REQUEST_VALUE, httpStatus(status));
-         break;
+ipp_status_t                           /* O - Status of document submission */
+cupsFinishDocument(http_t     *http,   /* I - HTTP connection or CUPS_HTTP_DEFAULT */
+                   const char *name)   /* I - Printer or class name */
+{
+  char resource[1024];                 /* Printer resource */
 
-      case HTTP_NOT_IMPLEMENTED :
-          cups_set_error(IPP_OPERATION_NOT_SUPPORTED, httpStatus(status));
-         break;
 
-      case HTTP_NOT_SUPPORTED :
-          cups_set_error(IPP_VERSION_NOT_SUPPORTED, httpStatus(status));
-         break;
+  snprintf(resource, sizeof(resource), "/printers/%s", name);
 
-      default :
-         DEBUG_printf(("HTTP error %d mapped to IPP_SERVICE_UNAVAILABLE!\n",
-                       status));
-         cups_set_error(IPP_SERVICE_UNAVAILABLE, httpStatus(status));
-         break;
-    }
-  }
+  ippDelete(cupsGetResponse(http, resource));
 
-  return (response);
+  return (cupsLastError());
 }
 
 
@@ -528,18 +277,19 @@ void
 cupsFreeJobs(int        num_jobs,      /* I - Number of jobs */
              cups_job_t *jobs)         /* I - Jobs */
 {
-  int  i;                              /* Looping var */
+  int          i;                      /* Looping var */
+  cups_job_t   *job;                   /* Current job */
 
 
-  if (num_jobs <= 0 || jobs == NULL)
+  if (num_jobs <= 0 || !jobs)
     return;
 
-  for (i = 0; i < num_jobs; i ++)
+  for (i = num_jobs, job = jobs; i > 0; i --, job ++)
   {
-    free(jobs[i].dest);
-    free(jobs[i].user);
-    free(jobs[i].format);
-    free(jobs[i].title);
+    _cupsStrFree(job->dest);
+    _cupsStrFree(job->user);
+    _cupsStrFree(job->format);
+    _cupsStrFree(job->title);
   }
 
   free(jobs);
@@ -561,28 +311,21 @@ cupsGetClasses(char ***classes)           /* O - Classes */
   ipp_t                *request,               /* IPP Request */
                *response;              /* IPP Response */
   ipp_attribute_t *attr;               /* Current attribute */
-  cups_lang_t  *language;              /* Default language */
   char         **temp;                 /* Temporary pointer */
-  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
+  http_t       *http;                  /* Connection to server */
 
 
-  if (classes == NULL)
+  if (!classes)
   {
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
+    _cupsSetError(IPP_INTERNAL_ERROR, NULL);
 
     return (0);
   }
 
- /*
-  * Try to connect to the server...
-  */
-
-  if (!cups_connect("default", NULL, NULL))
-  {
-    DEBUG_puts("Unable to connect to server!");
+  *classes = NULL;
 
+  if ((http = _cupsConnect()) == NULL)
     return (0);
-  }
 
  /*
   * Build a CUPS_GET_CLASSES request, which requires the following
@@ -593,20 +336,7 @@ cupsGetClasses(char ***classes)            /* O - Classes */
   *    requested-attributes
   */
 
-  request = ippNew();
-
-  request->request.op.operation_id = CUPS_GET_CLASSES;
-  request->request.op.request_id   = 1;
-
-  language = cupsLangDefault();
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL, language->language);
-
-  cupsLangFree(language);
+  request = ippNewRequest(CUPS_GET_CLASSES);
 
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                "requested-attributes", NULL, "printer-name");
@@ -615,10 +345,9 @@ cupsGetClasses(char ***classes)            /* O - Classes */
   * Do the request and get back a response...
   */
 
-  n        = 0;
-  *classes = NULL;
+  n = 0;
 
-  if ((response = cupsDoRequest(cg->http, request, "/")) != NULL)
+  if ((response = cupsDoRequest(http, request, "/")) != NULL)
   {
     for (attr = response->attrs; attr != NULL; attr = attr->next)
       if (attr->name != NULL &&
@@ -673,38 +402,11 @@ cupsGetClasses(char ***classes)           /* O - Classes */
 const char *                           /* O - Default printer or NULL */
 cupsGetDefault(void)
 {
-  const char   *var;                   /* Environment variable */
-  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
-
-
- /*
-  * First see if the LPDEST or PRINTER environment variables are
-  * set...  However, if PRINTER is set to "lp", ignore it to work
-  * around a "feature" in most Linux distributions - the default
-  * user login scripts set PRINTER to "lp"...
-  */
-
-  if ((var = getenv("LPDEST")) != NULL)
-    return (var);
-  else if ((var = getenv("PRINTER")) != NULL && strcmp(var, "lp") != 0)
-    return (var);
-
- /*
-  * Try to connect to the server...
-  */
-
-  if (!cups_connect("default", NULL, NULL))
-  {
-    DEBUG_puts("Unable to connect to server!");
-
-    return (NULL);
-  }
-
  /*
   * Return the default printer...
   */
 
-  return (cupsGetDefault2(cg->http));
+  return (cupsGetDefault2(CUPS_HTTP_DEFAULT));
 }
 
 
@@ -727,7 +429,6 @@ cupsGetDefault2(http_t *http)               /* I - HTTP connection */
   ipp_t                *request,               /* IPP Request */
                *response;              /* IPP Response */
   ipp_attribute_t *attr;               /* Current attribute */
-  cups_lang_t  *language;              /* Default language */
   const char   *var;                   /* Environment variable */
   _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
 
@@ -745,11 +446,12 @@ cupsGetDefault2(http_t *http)             /* I - HTTP connection */
     return (var);
 
  /*
-  * Range check input...
+  * Connect to the server as needed...
   */
 
   if (!http)
-    return (NULL);
+    if ((http = _cupsConnect()) == NULL)
+      return (NULL);
 
  /*
   * Build a CUPS_GET_DEFAULT request, which requires the following
@@ -759,20 +461,7 @@ cupsGetDefault2(http_t *http)              /* I - HTTP connection */
   *    attributes-natural-language
   */
 
-  request = ippNew();
-
-  request->request.op.operation_id = CUPS_GET_DEFAULT;
-  request->request.op.request_id   = 1;
-
-  language = cupsLangDefault();
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL, language->language);
-
-  cupsLangFree(language);
+  request = ippNewRequest(CUPS_GET_DEFAULT);
 
  /*
   * Do the request and get back a response...
@@ -780,9 +469,11 @@ cupsGetDefault2(http_t *http)              /* I - HTTP connection */
 
   if ((response = cupsDoRequest(http, request, "/")) != NULL)
   {
-    if ((attr = ippFindAttribute(response, "printer-name", IPP_TAG_NAME)) != NULL)
+    if ((attr = ippFindAttribute(response, "printer-name",
+                                 IPP_TAG_NAME)) != NULL)
     {
-      strlcpy(cg->def_printer, attr->values[0].string.text, sizeof(cg->def_printer));
+      strlcpy(cg->def_printer, attr->values[0].string.text,
+              sizeof(cg->def_printer));
       ippDelete(response);
       return (cg->def_printer);
     }
@@ -800,28 +491,17 @@ cupsGetDefault2(http_t *http)             /* I - HTTP connection */
 
 int                                    /* O - Number of jobs */
 cupsGetJobs(cups_job_t **jobs,         /* O - Job data */
-            const char *mydest,                /* I - Only show jobs for dest? */
-            int        myjobs,         /* I - Only show my jobs? */
-           int        completed)       /* I - Only show completed jobs? */
+            const char *mydest,                /* I - NULL = all destinations,       *
+                                        *     otherwise show jobs for mydest */
+            int        myjobs,         /* I - 0 = all users, 1 = mine */
+           int        completed)       /* I - -1 = show all, 0 = active, *
+                                        *     1 = completed jobs         */
 {
-  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
-
- /*
-  * Try to connect to the server...
-  */
-
-  if (!cups_connect("default", NULL, NULL))
-  {
-    DEBUG_puts("Unable to connect to server!");
-
-    return (-1);
-  }
-
  /*
   * Return the jobs...
   */
 
-  return (cupsGetJobs2(cg->http, jobs, mydest, myjobs, completed));
+  return (cupsGetJobs2(CUPS_HTTP_DEFAULT, jobs, mydest, myjobs, completed));
 }
 
 
@@ -833,17 +513,18 @@ cupsGetJobs(cups_job_t **jobs,            /* O - Job data */
  */
 
 int                                    /* O - Number of jobs */
-cupsGetJobs2(http_t     *http,         /* I - HTTP connection */
+cupsGetJobs2(http_t     *http,         /* I - HTTP connection or CUPS_HTTP_DEFAULT */
              cups_job_t **jobs,                /* O - Job data */
-             const char *mydest,       /* I - Only show jobs for dest? */
-             int        myjobs,                /* I - Only show my jobs? */
-            int        completed)      /* I - Only show completed jobs? */
+             const char *mydest,       /* I - NULL = all destinations,       *
+                                        *     otherwise show jobs for mydest */
+             int        myjobs,                /* I - 0 = all users, 1 = mine */
+            int        completed)      /* I - -1 = show all, 0 = active, *
+                                        *     1 = completed jobs         */
 {
   int          n;                      /* Number of jobs */
   ipp_t                *request,               /* IPP Request */
                *response;              /* IPP Response */
   ipp_attribute_t *attr;               /* Current attribute */
-  cups_lang_t  *language;              /* Default language */
   cups_job_t   *temp;                  /* Temporary pointer */
   int          id,                     /* job-id */
                priority,               /* job-priority */
@@ -878,9 +559,9 @@ cupsGetJobs2(http_t     *http,              /* I - HTTP connection */
   * Range check input...
   */
 
-  if (!http || !jobs)
+  if (!jobs)
   {
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
+    _cupsSetError(IPP_INTERNAL_ERROR, NULL);
 
     return (-1);
   }
@@ -894,7 +575,7 @@ cupsGetJobs2(http_t     *http,              /* I - HTTP connection */
     if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
                          "localhost", 0, "/printers/%s", mydest) != HTTP_URI_OK)
     {
-      cups_set_error(IPP_INTERNAL_ERROR, NULL);
+      _cupsSetError(IPP_INTERNAL_ERROR, NULL);
 
       return (-1);
     }
@@ -902,6 +583,9 @@ cupsGetJobs2(http_t     *http,              /* I - HTTP connection */
   else
     strcpy(uri, "ipp://localhost/jobs");
 
+  if (!http)
+    if ((http = _cupsConnect()) == NULL)
+      return (-1);
 
  /*
   * Build an IPP_GET_JOBS request, which requires the following
@@ -916,20 +600,7 @@ cupsGetJobs2(http_t     *http,             /* I - HTTP connection */
   *    requested-attributes
   */
 
-  request = ippNew();
-
-  request->request.op.operation_id = IPP_GET_JOBS;
-  request->request.op.request_id   = 1;
-
-  language = cupsLangDefault();
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL, language->language);
-
-  cupsLangFree(language);
+  request = ippNewRequest(IPP_GET_JOBS);
 
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
                "printer-uri", NULL, uri);
@@ -940,9 +611,12 @@ cupsGetJobs2(http_t     *http,             /* I - HTTP connection */
   if (myjobs)
     ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
 
-  if (completed)
+  if (completed > 0)
     ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                  "which-jobs", NULL, "completed");
+  else if (completed < 0)
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+                 "which-jobs", NULL, "all");
 
   ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                 "requested-attributes", sizeof(attrs) / sizeof(attrs[0]),
@@ -957,16 +631,16 @@ cupsGetJobs2(http_t     *http,            /* I - HTTP connection */
 
   if ((response = cupsDoRequest(http, request, "/")) != NULL)
   {
-    for (attr = response->attrs; attr != NULL; attr = attr->next)
+    for (attr = response->attrs; attr; attr = attr->next)
     {
      /*
       * Skip leading attributes until we hit a job...
       */
 
-      while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
+      while (attr && attr->group_tag != IPP_TAG_JOB)
         attr = attr->next;
 
-      if (attr == NULL)
+      if (!attr)
         break;
 
      /*
@@ -985,42 +659,42 @@ cupsGetJobs2(http_t     *http,            /* I - HTTP connection */
       completed_time  = 0;
       processing_time = 0;
 
-      while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
+      while (attr && attr->group_tag == IPP_TAG_JOB)
       {
-        if (strcmp(attr->name, "job-id") == 0 &&
+        if (!strcmp(attr->name, "job-id") &&
            attr->value_tag == IPP_TAG_INTEGER)
          id = attr->values[0].integer;
-        else if (strcmp(attr->name, "job-state") == 0 &&
+        else if (!strcmp(attr->name, "job-state") &&
                 attr->value_tag == IPP_TAG_ENUM)
          state = (ipp_jstate_t)attr->values[0].integer;
-        else if (strcmp(attr->name, "job-priority") == 0 &&
+        else if (!strcmp(attr->name, "job-priority") &&
                 attr->value_tag == IPP_TAG_INTEGER)
          priority = attr->values[0].integer;
-        else if (strcmp(attr->name, "job-k-octets") == 0 &&
+        else if (!strcmp(attr->name, "job-k-octets") &&
                 attr->value_tag == IPP_TAG_INTEGER)
          size = attr->values[0].integer;
-        else if (strcmp(attr->name, "time-at-completed") == 0 &&
+        else if (!strcmp(attr->name, "time-at-completed") &&
                 attr->value_tag == IPP_TAG_INTEGER)
          completed_time = attr->values[0].integer;
-        else if (strcmp(attr->name, "time-at-creation") == 0 &&
+        else if (!strcmp(attr->name, "time-at-creation") &&
                 attr->value_tag == IPP_TAG_INTEGER)
          creation_time = attr->values[0].integer;
-        else if (strcmp(attr->name, "time-at-processing") == 0 &&
+        else if (!strcmp(attr->name, "time-at-processing") &&
                 attr->value_tag == IPP_TAG_INTEGER)
          processing_time = attr->values[0].integer;
-        else if (strcmp(attr->name, "job-printer-uri") == 0 &&
+        else if (!strcmp(attr->name, "job-printer-uri") &&
                 attr->value_tag == IPP_TAG_URI)
        {
          if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
            dest ++;
         }
-        else if (strcmp(attr->name, "job-originating-user-name") == 0 &&
+        else if (!strcmp(attr->name, "job-originating-user-name") &&
                 attr->value_tag == IPP_TAG_NAME)
          user = attr->values[0].string.text;
-        else if (strcmp(attr->name, "document-format") == 0 &&
+        else if (!strcmp(attr->name, "document-format") &&
                 attr->value_tag == IPP_TAG_MIMETYPE)
          format = attr->values[0].string.text;
-        else if (strcmp(attr->name, "job-name") == 0 &&
+        else if (!strcmp(attr->name, "job-name") &&
                 (attr->value_tag == IPP_TAG_TEXT ||
                  attr->value_tag == IPP_TAG_NAME))
          title = attr->values[0].string.text;
@@ -1032,9 +706,9 @@ cupsGetJobs2(http_t     *http,             /* I - HTTP connection */
       * See if we have everything needed...
       */
 
-      if (dest == NULL || id == 0)
+      if (!dest || !id)
       {
-        if (attr == NULL)
+        if (!attr)
          break;
        else
           continue;
@@ -1049,17 +723,20 @@ cupsGetJobs2(http_t     *http,           /* I - HTTP connection */
       else
        temp = realloc(*jobs, sizeof(cups_job_t) * (n + 1));
 
-      if (temp == NULL)
+      if (!temp)
       {
        /*
         * Ran out of memory!
         */
 
+        _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno));
+
        cupsFreeJobs(n, *jobs);
        *jobs = NULL;
 
         ippDelete(response);
-       return (0);
+
+       return (-1);
       }
 
       *jobs = temp;
@@ -1070,10 +747,10 @@ cupsGetJobs2(http_t     *http,           /* I - HTTP connection */
       * Copy the data over...
       */
 
-      temp->dest            = strdup(dest);
-      temp->user            = strdup(user);
-      temp->format          = strdup(format);
-      temp->title           = strdup(title);
+      temp->dest            = _cupsStrAlloc(dest);
+      temp->user            = _cupsStrAlloc(user);
+      temp->format          = _cupsStrAlloc(format);
+      temp->title           = _cupsStrAlloc(title);
       temp->id              = id;
       temp->priority        = priority;
       temp->state           = state;
@@ -1082,7 +759,7 @@ cupsGetJobs2(http_t     *http,             /* I - HTTP connection */
       temp->creation_time   = creation_time;
       temp->processing_time = processing_time;
 
-      if (attr == NULL)
+      if (!attr)
         break;
     }
 
@@ -1107,40 +784,76 @@ const char *                             /* O - Filename for PPD file */
 cupsGetPPD(const char *name)           /* I - Printer name */
 {
   _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
+  time_t       modtime = 0;            /* Modification time */
+
 
  /*
-  * See if we can connect to the server...
+  * Return the PPD file...
   */
 
-  if (!cups_connect(name, NULL, NULL))
-  {
-    DEBUG_puts("Unable to connect to server!");
+  cg->ppd_filename[0] = '\0';
 
+  if (cupsGetPPD3(CUPS_HTTP_DEFAULT, name, &modtime, cg->ppd_filename,
+                  sizeof(cg->ppd_filename)) == HTTP_OK)
+    return (cg->ppd_filename);
+  else
     return (NULL);
-  }
+}
 
- /*
-  * Return the PPD file...
-  */
 
-  return (cupsGetPPD2(cg->http, name));
+/*
+ * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server.
+ *
+ * For classes, cupsGetPPD2() returns the PPD file for the first printer
+ * in the class.
+ *
+ * @since CUPS 1.1.21@
+ */
+
+const char *                           /* O - Filename for PPD file */
+cupsGetPPD2(http_t     *http,          /* I - HTTP connection or CUPS_HTTP_DEFAULT */
+            const char *name)          /* I - Printer name */
+{
+  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
+  time_t       modtime = 0;            /* Modification time */
+
+
+  cg->ppd_filename[0] = '\0';
+
+  if (cupsGetPPD3(http, name, &modtime, cg->ppd_filename,
+                  sizeof(cg->ppd_filename)) == HTTP_OK)
+    return (cg->ppd_filename);
+  else
+    return (NULL);
 }
 
 
 /*
- * 'cupsGetPPD2()' - Get the PPD file for a printer from the specified server.
+ * 'cupsGetPPD3()' - Get the PPD file for a printer on the specified
+ *                   server if it has changed.
  *
- * For classes, cupsGetPPD2() returns the PPD file for the first printer
- * in the class.
+ * The "modtime" parameter contains the modification time of any
+ * locally-cached content and is updated with the time from the PPD file on
+ * the server.
  *
- * @since CUPS 1.1.21@
+ * The "buffer" parameter contains the local PPD filename.  If it contains
+ * the empty string, a new temporary file is created, otherwise the existing
+ * file will be overwritten as needed.
+ *
+ * On success, HTTP_OK is returned for a new PPD file and HTTP_NOT_MODIFIED
+ * if the existing PPD file is up-to-date.  Any other status is an error.
  */
 
-const char *                           /* O - Filename for PPD file */
-cupsGetPPD2(http_t     *http,          /* I - HTTP connection */
-            const char *name)          /* I - Printer name */
+http_status_t                          /* O  - HTTP status */
+cupsGetPPD3(http_t     *http,          /* I  - HTTP connection or CUPS_HTTP_DEFAULT */
+            const char *name,          /* I  - Printer name */
+           time_t     *modtime,        /* IO - Modification time */
+           char       *buffer,         /* I  - Filename buffer */
+           size_t     bufsize)         /* I  - Size of filename buffer */
 {
   int          http_port;              /* Port number */
+  char         http_hostname[HTTP_MAX_HOST];
+                                       /* Hostname associated with connection */
   http_t       *http2;                 /* Alternate HTTP connection */
   int          fd;                     /* PPD file */
   char         localhost[HTTP_MAX_URI],/* Local hostname */
@@ -1155,37 +868,58 @@ cupsGetPPD2(http_t     *http,            /* I - HTTP connection */
   * Range check input...
   */
 
-  DEBUG_printf(("cupsGetPPD2(http=%p, name=\"%s\")\n", http,
-                name ? name : "(null)"));
+  DEBUG_printf(("cupsGetPPD3(http=%p, name=\"%s\", modtime=%p(%d), buffer=%p, "
+                "bufsize=%d)\n", http, name ? name : "(null)", modtime,
+               modtime ? *modtime : 0, buffer, (int)bufsize));
+
+  if (!name)
+  {
+    _cupsSetError(IPP_INTERNAL_ERROR, "No printer name!");
+    return (HTTP_NOT_ACCEPTABLE);
+  }
 
-  if (!http || !name)
+  if (!modtime)
   {
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
+    _cupsSetError(IPP_INTERNAL_ERROR, "No modification time!");
+    return (HTTP_NOT_ACCEPTABLE);
+  }
 
-    return (NULL);
+  if (!buffer || bufsize <= 1)
+  {
+    _cupsSetError(IPP_INTERNAL_ERROR, "Bad filename buffer!");
+    return (HTTP_NOT_ACCEPTABLE);
   }
 
  /*
   * Try finding a printer URI for this printer...
   */
 
+  if (!http)
+    http = _cupsConnect();
+
   if (!cups_get_printer_uri(http, name, hostname, sizeof(hostname), &port,
                             resource, sizeof(resource), 0))
-    return (NULL);
+    return (HTTP_NOT_FOUND);
+
+  DEBUG_printf(("Printer hostname=\"%s\", port=%d\n", hostname, port));
 
  /*
   * Remap local hostname to localhost...
   */
 
-  httpGetHostname(localhost, sizeof(localhost));
+  httpGetHostname(NULL, localhost, sizeof(localhost));
+
+  DEBUG_printf(("Local hostname=\"%s\"\n", localhost));
 
   if (!strcasecmp(localhost, hostname))
     strcpy(hostname, "localhost");
 
  /*
-  * Get the port number we are connected to...
+  * Get the hostname and port number we are connected to...
   */
 
+  httpGetHostname(http, http_hostname, sizeof(http_hostname));
+
 #ifdef AF_INET6
   if (http->hostaddr->addr.sa_family == AF_INET6)
     http_port = ntohs(http->hostaddr->ipv6.sin6_port);
@@ -1196,36 +930,44 @@ cupsGetPPD2(http_t     *http,            /* I - HTTP connection */
   else
     http_port = ippPort(); 
 
+  DEBUG_printf(("Connection hostname=\"%s\", port=%d\n", http_hostname,
+                http_port));
+
  /*
   * Reconnect to the correct server as needed...
   */
 
-  if (!strcasecmp(http->hostname, hostname) && port == http_port)
+  if (!strcasecmp(http_hostname, hostname) && port == http_port)
     http2 = http;
   else if ((http2 = httpConnectEncrypt(hostname, port,
                                        cupsEncryption())) == NULL)
   {
     DEBUG_puts("Unable to connect to server!");
 
-    return (NULL);
+    return (HTTP_SERVICE_UNAVAILABLE);
   }
 
  /*
   * Get a temp file...
   */
 
-  if ((fd = cupsTempFd(cg->ppd_filename, sizeof(cg->ppd_filename))) < 0)
+  if (buffer[0])
+    fd = open(buffer, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+  else
+    fd = cupsTempFd(buffer, bufsize);
+
+  if (fd < 0)
   {
    /*
     * Can't open file; close the server connection and return NULL...
     */
 
-    cups_set_error(IPP_INTERNAL_ERROR, strerror(errno));
+    _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno));
 
     if (http2 != http)
       httpClose(http2);
 
-    return (NULL);
+    return (HTTP_SERVER_ERROR);
   }
 
  /*
@@ -1234,46 +976,50 @@ cupsGetPPD2(http_t     *http,            /* I - HTTP connection */
 
   strlcat(resource, ".ppd", sizeof(resource));
 
+  if (*modtime > 0)
+    httpSetField(http2, HTTP_FIELD_IF_MODIFIED_SINCE,
+                 httpGetDateString(*modtime));
+
   status = cupsGetFd(http2, resource, fd);
 
   close(fd);
 
-  if (http2 != http)
-    httpClose(http2);
-
  /*
   * See if we actually got the file or an error...
   */
 
-  if (status != HTTP_OK)
+  if (status == HTTP_OK)
+    *modtime = httpGetDateTime(httpGetField(http2, HTTP_FIELD_DATE));
+  else if (status != HTTP_NOT_MODIFIED)
   {
     switch (status)
     {
       case HTTP_NOT_FOUND :
-          cups_set_error(IPP_NOT_FOUND, httpStatus(status));
+          _cupsSetError(IPP_NOT_FOUND, httpStatus(status));
          break;
 
       case HTTP_UNAUTHORIZED :
-          cups_set_error(IPP_NOT_AUTHORIZED, httpStatus(status));
+          _cupsSetError(IPP_NOT_AUTHORIZED, httpStatus(status));
          break;
 
       default :
          DEBUG_printf(("HTTP error %d mapped to IPP_SERVICE_UNAVAILABLE!\n",
                        status));
-         cups_set_error(IPP_SERVICE_UNAVAILABLE, httpStatus(status));
+         _cupsSetError(IPP_SERVICE_UNAVAILABLE, httpStatus(status));
          break;
     }
 
     unlink(cg->ppd_filename);
-
-    return (NULL);
   }
 
+  if (http2 != http)
+    httpClose(http2);
+
  /*
   * Return the PPD file...
   */
 
-  return (cg->ppd_filename);
+  return (status);
 }
 
 
@@ -1292,28 +1038,29 @@ cupsGetPrinters(char ***printers)       /* O - Printers */
   ipp_t                *request,               /* IPP Request */
                *response;              /* IPP Response */
   ipp_attribute_t *attr;               /* Current attribute */
-  cups_lang_t  *language;              /* Default language */
   char         **temp;                 /* Temporary pointer */
-  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
+  http_t       *http;                  /* Connection to server */
+
 
+ /*
+  * Range check input...
+  */
 
-  if (printers == NULL)
+  if (!printers)
   {
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
+    _cupsSetError(IPP_INTERNAL_ERROR, NULL);
 
     return (0);
   }
 
+  *printers = NULL;
+
  /*
   * Try to connect to the server...
   */
 
-  if (!cups_connect("default", NULL, NULL))
-  {
-    DEBUG_puts("Unable to connect to server!");
-
+  if ((http = _cupsConnect()) == NULL)
     return (0);
-  }
 
  /*
   * Build a CUPS_GET_PRINTERS request, which requires the following
@@ -1324,20 +1071,7 @@ cupsGetPrinters(char ***printers)        /* O - Printers */
   *    requested-attributes
   */
 
-  request = ippNew();
-
-  request->request.op.operation_id = CUPS_GET_PRINTERS;
-  request->request.op.request_id   = 1;
-
-  language = cupsLangDefault();
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL, language->language);
-
-  cupsLangFree(language);
+  request = ippNewRequest(CUPS_GET_PRINTERS);
 
   ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
                "requested-attributes", NULL, "printer-name");
@@ -1352,10 +1086,9 @@ cupsGetPrinters(char ***printers)        /* O - Printers */
   * Do the request and get back a response...
   */
 
-  n         = 0;
-  *printers = NULL;
+  n = 0;
 
-  if ((response = cupsDoRequest(cg->http, request, "/")) != NULL)
+  if ((response = cupsDoRequest(http, request, "/")) != NULL)
   {
     for (attr = response->attrs; attr != NULL; attr = attr->next)
       if (attr->name != NULL &&
@@ -1396,6 +1129,83 @@ cupsGetPrinters(char ***printers)        /* O - Printers */
 }
 
 
+/*
+ * 'cupsGetServerPPD()' - Get an available PPD file from the server.
+ *
+ * This function returns the named PPD file from the server.  The
+ * list of available PPDs is provided by the IPP CUPS_GET_PPDS
+ * operation.
+ *
+ * You must remove (unlink) the PPD file when you are finished with
+ * it. The PPD filename is stored in a static location that will be
+ * overwritten on the next call to cupsGetPPD(), cupsGetPPD2(), or
+ * cupsGetServerPPD().
+ *
+ * @since CUPS 1.3@
+ */
+
+char *                                 /* O - Name of PPD file or NULL on error */
+cupsGetServerPPD(http_t     *http,     /* I - HTTP connection or CUPS_HTTP_DEFAULT */
+                 const char *name)     /* I - Name of PPD file ("ppd-name") */
+{
+  int                  fd;             /* PPD file descriptor */
+  ipp_t                        *request;       /* IPP request */
+  _cups_globals_t      *cg = _cupsGlobals();
+                                       /* Pointer to library globals */
+
+
+ /*
+  * Range check input...
+  */
+
+  if (!name)
+  {
+    _cupsSetError(IPP_INTERNAL_ERROR, "No PPD name!");
+
+    return (NULL);
+  }
+
+  if (!http)
+    if ((http = _cupsConnect()) == NULL)
+      return (NULL);
+
+ /*
+  * Get a temp file...
+  */
+
+  if ((fd = cupsTempFd(cg->ppd_filename, sizeof(cg->ppd_filename))) < 0)
+  {
+   /*
+    * Can't open file; close the server connection and return NULL...
+    */
+
+    _cupsSetError(IPP_INTERNAL_ERROR, strerror(errno));
+
+    return (NULL);
+  }
+
+ /*
+  * Get the PPD file...
+  */
+
+  request = ippNewRequest(CUPS_GET_PPD);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "ppd-name", NULL,
+               name);
+
+  ippDelete(cupsDoIORequest(http, request, "/", -1, fd));
+
+  close(fd);
+
+  if (cupsLastError() != IPP_OK)
+  {
+    unlink(cg->ppd_filename);
+    return (NULL);
+  }
+  else
+    return (cg->ppd_filename);
+}
+
+
 /*
  * 'cupsLastError()' - Return the last IPP status code.
  */
@@ -1435,7 +1245,8 @@ cupsPrintFile(const char    *name,        /* I - Printer or class name */
                 "title=\"%s\", num_options=%d, options=%p)\n",
                 name, filename, title, num_options, options));
 
-  return (cupsPrintFiles(name, 1, &filename, title, num_options, options));
+  return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, 1, &filename, title,
+                          num_options, options));
 }
 
 
@@ -1446,19 +1257,20 @@ cupsPrintFile(const char    *name,      /* I - Printer or class name */
  */
 
 int                                    /* O - Job ID */
-cupsPrintFile2(http_t        *http,    /* I - HTTP connection */
-               const char    *name,    /* I - Printer or class name */
-               const char    *filename,        /* I - File to print */
-              const char    *title,    /* I - Title of job */
-               int           num_options,
-                                       /* I - Number of options */
-              cups_option_t *options)  /* I - Options */
+cupsPrintFile2(
+    http_t        *http,               /* I - HTTP connection */
+    const char    *name,               /* I - Printer or class name */
+    const char    *filename,           /* I - File to print */
+    const char    *title,              /* I - Title of job */
+    int           num_options,         /* I - Number of options */
+    cups_option_t *options)            /* I - Options */
 {
   DEBUG_printf(("cupsPrintFile2(http=%p, name=\"%s\", filename=\"%s\", "
                 "title=\"%s\", num_options=%d, options=%p)\n",
                 http, name, filename, title, num_options, options));
 
-  return (cupsPrintFiles2(http, name, 1, &filename, title, num_options, options));
+  return (cupsPrintFiles2(http, name, 1, &filename, title, num_options,
+                          options));
 }
 
 
@@ -1468,44 +1280,28 @@ cupsPrintFile2(http_t        *http,     /* I - HTTP connection */
  */
 
 int                                    /* O - Job ID */
-cupsPrintFiles(const char    *name,    /* I - Printer or class name */
-               int           num_files,        /* I - Number of files */
-               const char    **files,  /* I - File(s) to print */
-              const char    *title,    /* I - Title of job */
-               int           num_options,
-                                       /* I - Number of options */
-              cups_option_t *options)  /* I - Options */
+cupsPrintFiles(
+    const char    *name,               /* I - Printer or class name */
+    int           num_files,           /* I - Number of files */
+    const char    **files,             /* I - File(s) to print */
+    const char    *title,              /* I - Title of job */
+    int           num_options,         /* I - Number of options */
+    cups_option_t *options)            /* I - Options */
 {
-  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
-
   DEBUG_printf(("cupsPrintFiles(name=\"%s\", num_files=%d, "
                 "files=%p, title=\"%s\", num_options=%d, options=%p)\n",
                 name, num_files, files, title, num_options, options));
 
 
- /*
-  * Setup a connection and request data...
-  */
-
-  if (!cups_connect(name, NULL, NULL))
-  {
-    DEBUG_printf(("cupsPrintFiles: Unable to open connection - %s.\n",
-                  strerror(errno)));
-    DEBUG_puts("Unable to connect to server!");
-
-    return (0);
-  }
-
  /*
   * Print the file(s)...
   */
 
-  return (cupsPrintFiles2(cg->http, name, num_files, files, title,
+  return (cupsPrintFiles2(CUPS_HTTP_DEFAULT, name, num_files, files, title,
                           num_options, options));
 }
 
 
-
 /*
  * 'cupsPrintFiles2()' - Print one or more files to a printer or class on the
  *                       specified server.
@@ -1514,27 +1310,26 @@ cupsPrintFiles(const char    *name,     /* I - Printer or class name */
  */
 
 int                                    /* O - Job ID */
-cupsPrintFiles2(http_t        *http,   /* I - HTTP connection */
-                const char    *name,   /* I - Printer or class name */
-                int           num_files,/* I - Number of files */
-                const char    **files, /* I - File(s) to print */
-               const char    *title,   /* I - Title of job */
-                int           num_options,
-                                       /* I - Number of options */
-               cups_option_t *options) /* I - Options */
+cupsPrintFiles2(
+    http_t        *http,               /* I - HTTP connection or CUPS_HTTP_DEFAULT */
+    const char    *name,               /* I - Printer or class name */
+    int           num_files,           /* I - Number of files */
+    const char    **files,             /* I - File(s) to print */
+    const char    *title,              /* I - Title of job */
+    int           num_options,         /* I - Number of options */
+    cups_option_t *options)            /* I - Options */
 {
   int          i;                      /* Looping var */
-  const char   *val;                   /* Pointer to option value */
-  ipp_t                *request;               /* IPP request */
-  ipp_t                *response;              /* IPP response */
-  ipp_attribute_t *attr;               /* IPP job-id attribute */
-  char         uri[HTTP_MAX_URI];      /* Printer URI */
-  cups_lang_t  *language;              /* Language to use */
-  int          jobid;                  /* New job ID */
-  const char   *base;                  /* Basename of current filename */
+  int          job_id;                 /* New job ID */
+  const char   *docname;               /* Basename of current filename */
+  const char   *format;                /* Document format */
+  cups_file_t  *fp;                    /* Current file */
+  char         buffer[8192];           /* Copy buffer */
+  ssize_t      bytes;                  /* Bytes in buffer */
+  http_status_t        status;                 /* Status of write */
 
 
-  DEBUG_printf(("cupsPrintFiles(http=%p, name=\"%s\", num_files=%d, "
+  DEBUG_printf(("cupsPrintFiles2(http=%p, name=\"%s\", num_files=%d, "
                 "files=%p, title=\"%s\", num_options=%d, options=%p)\n",
                 http, name, num_files, files, title, num_options, options));
 
@@ -1542,248 +1337,212 @@ cupsPrintFiles2(http_t        *http,  /* I - HTTP connection */
   * Range check input...
   */
 
-  if (!http || !name || num_files < 1 || files == NULL)
-  {
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
-
-    return (0);
-  }
-
- /*
-  * Setup the printer URI...
-  */
-
-  if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
-                       "localhost", 0, "/printers/%s", name) != HTTP_URI_OK)
+  if (!name || num_files < 1 || !files)
   {
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
+    _cupsSetError(IPP_INTERNAL_ERROR, NULL);
 
     return (0);
   }
 
  /*
-  * Setup the request data...
-  */
-
-  language = cupsLangDefault();
-
- /*
-  * Build a standard CUPS URI for the printer and fill the standard IPP
-  * attributes...
+  * Create the print job...
   */
 
-  if ((request = ippNew()) == NULL)
-  {
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
-
+  if ((job_id = cupsCreateJob(http, name, title, num_options, options)) == 0)
     return (0);
-  }
-
-  request->request.op.operation_id = num_files == 1 ? IPP_PRINT_JOB :
-                                                      IPP_CREATE_JOB;
-  request->request.op.request_id   = 1;
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-               "attributes-charset", NULL, cupsLangEncoding(language));
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-               "attributes-natural-language", NULL,
-               language != NULL ? language->language : "C");
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-               NULL, uri);
-
-  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
-               NULL, cupsUser());
-
-  if (title)
-    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
-                 title);
-
- /*
-  * Then add all options...
-  */
-
-  cupsEncodeOptions(request, num_options, options);
 
  /*
-  * Do the request...
+  * Send each of the files...
   */
 
-  snprintf(uri, sizeof(uri), "/printers/%s", name);
-
-  if (num_files == 1)
-    response = cupsDoFileRequest(http, request, uri, *files);
-  else
-    response = cupsDoRequest(http, request, uri);
+  if (cupsGetOption("raw", num_options, options))
+    format = CUPS_FORMAT_RAW;
+  else if ((format = cupsGetOption("document-format", num_options,
+                                  options)) == NULL)
+    format = CUPS_FORMAT_AUTO;
 
-  if (response == NULL)
-    jobid = 0;
-  else if (response->request.status.status_code > IPP_OK_CONFLICT)
-  {
-    DEBUG_printf(("IPP response code was 0x%x!\n",
-                  response->request.status.status_code));
-    jobid = 0;
-  }
-  else if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
+  for (i = 0; i < num_files; i ++)
   {
-    DEBUG_puts("No job ID!");
-
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
-
-    jobid = 0;
-  }
-  else
-    jobid = attr->values[0].integer;
-
-  if (response != NULL)
-    ippDelete(response);
+   /*
+    * Start the next file...
+    */
 
- /*
-  * Handle multiple file jobs if the create-job operation worked...
-  */
+    if ((docname = strrchr(files[i], '/')) != NULL)
+      docname ++;
+    else
+      docname = files[i];
 
-  if (jobid > 0 && num_files > 1)
-    for (i = 0; i < num_files; i ++)
+    if ((fp = cupsFileOpen(files[i], "rb")) == NULL)
     {
      /*
-      * Build a standard CUPS URI for the job and fill the standard IPP
-      * attributes...
+      * Unable to open print file, cancel the job and return...
       */
 
-      if ((request = ippNew()) == NULL)
-       return (0);
-
-      request->request.op.operation_id = IPP_SEND_DOCUMENT;
-      request->request.op.request_id   = 1;
-
-      snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", jobid);
+      cupsCancelJob2(http, job_id, 0);
+      return (0);
+    }
 
-      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
-                  "attributes-charset", NULL, cupsLangEncoding(language));
+    status = cupsStartDocument(http, name, job_id, docname, format,
+                               i == (num_files - 1));
 
-      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
-                  "attributes-natural-language", NULL,
-                  language != NULL ? language->language : "C");
+    while (status == HTTP_CONTINUE &&
+           (bytes = cupsFileRead(fp, buffer, sizeof(buffer))) > 0)
+      status = cupsWriteRequestData(http, buffer, bytes);
 
-      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
-                  NULL, uri);
+    cupsFileClose(fp);
 
+    if (status != HTTP_CONTINUE || cupsFinishDocument(http, name) != IPP_OK)
+    {
      /*
-      * Handle raw print files...
+      * Unable to queue, cancel the job and return...
       */
 
-      if (cupsGetOption("raw", num_options, options))
-       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
-                    "document-format", NULL, "application/vnd.cups-raw");
-      else if ((val = cupsGetOption("document-format", num_options,
-                                    options)) != NULL)
-       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
-                    "document-format", NULL, val);
-      else
-       ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
-                    "document-format", NULL, "application/octet-stream");
+      cupsCancelJob2(http, job_id, 0);
+      return (0);
+    }
+  }
 
-      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
-                   "requesting-user-name", NULL, cupsUser());
+  return (job_id);
+}
 
-     /*
-      * Add the original document filename...
-      */
 
-      if ((base = strrchr(files[i], '/')) != NULL)
-        base ++;
-      else
-        base = files[i];
+/*
+ * 'cupsStartDocument()' - Add a document to a job created with cupsCreateJob().
+ *
+ * Use cupsWriteRequestData() to write data for the document and
+ * cupsFinishDocument() to finish the document and get the submission status.
+ *
+ * The MIME type constants CUPS_FORMAT_AUTO, CUPS_FORMAT_PDF,
+ * CUPS_FORMAT_POSTSCRIPT, CUPS_FORMAT_RAW, and CUPS_FORMAT_TEXT are provided
+ * for the "format" argument, although any supported MIME type string can be
+ * supplied.
+ *
+ * @since CUPS 1.4@
+ */
 
-      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
-                   NULL, base);
+http_status_t                          /* O - HTTP status of request */
+cupsStartDocument(
+    http_t     *http,                  /* I - HTTP connection or CUPS_HTTP_DEFAULT */
+    const char *name,                  /* I - Printer or class name */
+    int        job_id,                 /* I - Job ID from cupsCreateJob() */
+    const char *docname,               /* I - Name of document */
+    const char *format,                        /* I - MIME type or CUPS_FORMAT_foo */
+    int        last_document)          /* I - 1 for last document in job, 0 otherwise */
+{
+  char         resource[1024],         /* Resource for destinatio */
+               printer_uri[1024];      /* Printer URI */
+  ipp_t                *request;               /* Send-Document request */
+  http_status_t        status;                 /* HTTP status */
 
-     /*
-      * Is this the last document?
-      */
 
-      if (i == (num_files - 1))
-        ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
+ /*
+  * Create a Send-Document request...
+  */
+
+  if ((request = ippNewRequest(IPP_SEND_DOCUMENT)) == NULL)
+  {
+    _cupsSetError(IPP_INTERNAL_ERROR, NULL);
+    return (0);
+  }
+
+  httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri), "ipp",
+                   NULL, "localhost", ippPort(), "/printers/%s", name);
+  snprintf(resource, sizeof(resource), "/printers/%s", name);
 
-     /*
-      * Send the file...
-      */
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+               NULL, printer_uri);
+  ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", job_id);
+  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
+               NULL, cupsUser());
+  if (docname)
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
+                 NULL, docname);
+  if (format)
+    ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
+                 "document-format", NULL, format);
+  ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", last_document);
 
-      snprintf(uri, sizeof(uri), "/printers/%s", name);
+ /*
+  * Send and delete the request, then return the status...
+  */
 
-      if ((response = cupsDoFileRequest(http, request, uri,
-                                        files[i])) != NULL)
-       ippDelete(response);
-    }
+  status = cupsSendRequest(http, request, resource, CUPS_LENGTH_VARIABLE);
 
-  cupsLangFree(language);
+  ippDelete(request);
 
-  return (jobid);
+  return (status);
 }
 
 
 /*
- * 'cups_connect()' - Connect to the specified host...
+ * '_cupsConnect()' - Get the default server connection...
  */
 
-static char *                          /* I - Printer name or NULL */
-cups_connect(const char *name,         /* I - Destination (printer[@host]) */
-            char       *printer,       /* O - Printer name [HTTP_MAX_URI] */
-             char       *hostname)     /* O - Hostname [HTTP_MAX_URI] */
+http_t *                               /* O - HTTP connection */
+_cupsConnect(void)
 {
-  char hostbuf[HTTP_MAX_URI];          /* Name of host */
-  _cups_globals_t  *cg = _cupsGlobals();/* Pointer to library globals */
+  _cups_globals_t *cg = _cupsGlobals();        /* Pointer to library globals */
 
 
-  DEBUG_printf(("cups_connect(\"%s\", %p, %p)\n", name, printer, hostname));
+ /*
+  * See if we are connected to the same server...
+  */
 
-  if (name == NULL)
+  if (cg->http)
   {
-    cups_set_error(IPP_BAD_REQUEST, NULL);
-
-    return (NULL);
-  }
+    int        port;                           /* Port for connection */
 
- /*
-  * All jobs are now queued to cupsServer() to avoid hostname
-  * resolution problems and to ensure that the user sees all
-  * locally queued jobs locally.
-  */
 
-  strlcpy(hostbuf, cupsServer(), sizeof(hostbuf));
+   /*
+    * Get the port associated with the current connection...
+    */
 
-  if (hostname != NULL)
-    strlcpy(hostname, hostbuf, HTTP_MAX_URI);
-  else
-    hostname = hostbuf;
+#ifdef AF_INET6
+    if (cg->http->hostaddr->addr.sa_family == AF_INET6)
+      port = ntohs(cg->http->hostaddr->ipv6.sin6_port);
+    else
+#endif /* AF_INET6 */
+    if (cg->http->hostaddr->addr.sa_family == AF_INET)
+      port = ntohs(cg->http->hostaddr->ipv4.sin_port);
+    else
+      port = cg->ipp_port;
 
-  if (printer != NULL)
-    strlcpy(printer, name, HTTP_MAX_URI);
-  else
-    printer = (char *)name;
+   /*
+    * Compare the connection hostname, port, and encryption settings to
+    * the cached defaults; these were initialized the first time we
+    * connected...
+    */
 
-  if (cg->http != NULL)
-  {
-    if (!strcasecmp(cg->http->hostname, hostname))
-      return (printer);
+    if (strcmp(cg->http->hostname, cg->server) || cg->ipp_port != port ||
+        (cg->http->encryption != cg->encryption &&
+        cg->http->encryption == HTTP_ENCRYPT_NEVER))
+    {
+     /*
+      * Need to close the current connection because something has changed...
+      */
 
-    httpClose(cg->http);
+      httpClose(cg->http);
+      cg->http = NULL;
+    }
   }
 
-  DEBUG_printf(("connecting to %s on port %d...\n", hostname, ippPort()));
+ /*
+  * (Re)connect as needed...
+  */
 
-  if ((cg->http = httpConnectEncrypt(hostname, ippPort(),
-                                     cupsEncryption())) == NULL)
+  if (!cg->http)
   {
-    DEBUG_puts("Unable to connect to server!");
+    if ((cg->http = httpConnectEncrypt(cupsServer(), ippPort(),
+                                       cupsEncryption())) == NULL)
+      _cupsSetError(IPP_SERVICE_UNAVAILABLE,
+                    errno ? strerror(errno) : "Unable to connect to host.");
+  }
 
-    cups_set_error(IPP_SERVICE_UNAVAILABLE, strerror(errno));
+ /*
+  * Return the cached connection...
+  */
 
-    return (NULL);
-  }
-  else
-    return (printer);
+  return (cg->http);
 }
 
 
@@ -1811,7 +1570,9 @@ cups_get_printer_uri(
   char         uri[HTTP_MAX_URI],      /* printer-uri attribute */
                scheme[HTTP_MAX_URI],   /* Scheme name */
                username[HTTP_MAX_URI], /* Username:password */
-               classname[255];         /* Temporary class name */
+               classname[255],         /* Temporary class name */
+               http_hostname[HTTP_MAX_HOST];
+                                       /* Hostname associated with connection */
   static const char * const requested_attrs[] =
                {                       /* Requested attributes */
                  "printer-uri-supported",
@@ -1832,7 +1593,7 @@ cups_get_printer_uri(
   if (httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
                        "localhost", 0, "/printers/%s", name) != HTTP_URI_OK)
   {
-    cups_set_error(IPP_INTERNAL_ERROR, NULL);
+    _cupsSetError(IPP_INTERNAL_ERROR, "Unable to create printer-uri!");
 
     *host     = '\0';
     *resource = '\0';
@@ -1843,9 +1604,11 @@ cups_get_printer_uri(
   DEBUG_printf(("cups_get_printer_uri: printer-uri=\"%s\"\n", uri));
 
  /*
-  * Get the port number we are connected to...
+  * Get the hostname and port number we are connected to...
   */
 
+  httpGetHostname(http, http_hostname, sizeof(http_hostname));
+
 #ifdef AF_INET6
   if (http->hostaddr->addr.sa_family == AF_INET6)
     http_port = ntohs(http->hostaddr->ipv6.sin6_port);
@@ -1923,7 +1686,7 @@ cups_get_printer_uri(
            * Found a class!  Connect to the right server...
            */
 
-           if (!strcasecmp(http->hostname, host) && *port == http_port)
+           if (!strcasecmp(http_hostname, host) && *port == http_port)
              http2 = http;
            else if ((http2 = httpConnectEncrypt(host, *port,
                                                 cupsEncryption())) == NULL)
@@ -1969,6 +1732,9 @@ cups_get_printer_uri(
     ippDelete(response);
   }
 
+  if (cupsLastError() != IPP_NOT_FOUND)
+    _cupsSetError(IPP_INTERNAL_ERROR, "No printer-uri found!");
+
   *host     = '\0';
   *resource = '\0';
 
@@ -1977,31 +1743,5 @@ cups_get_printer_uri(
 
 
 /*
- * 'cups_set_error()' - Set the last IPP status code and status-message.
- */
-
-static void
-cups_set_error(ipp_status_t status,    /* I - IPP status code */
-               const char   *message)  /* I - status-message value */
-{
-  _cups_globals_t      *cg;            /* Global data */
-
-
-  cg             = _cupsGlobals();
-  cg->last_error = status;
-
-  if (cg->last_status_message)
-  {
-    free(cg->last_status_message);
-
-    cg->last_status_message = NULL;
-  }
-
-  if (message)
-    cg->last_status_message = strdup(message);
-}
-
-
-/*
- * End of "$Id: util.c 5064 2006-02-03 16:51:05Z mike $".
+ * End of "$Id: util.c 7014 2007-10-10 21:57:43Z mike $".
  */