From df3718b09f395d59b454e0aa233490ba7f2aa857 Mon Sep 17 00:00:00 2001
From: Michael R Sweet <msweet@msweet.org>
Date: Thu, 4 Apr 2024 16:25:25 -0400
Subject: [PATCH] Update CUPS-Add-Modify-Printer and CUPS-Create-Local-Printer
 code to delay responding until the PPD is successfully generated (Issue #347)

---
 scheduler/auth.c     |  15 +-
 scheduler/auth.h     |   2 -
 scheduler/client.h   |   2 +
 scheduler/cupsd.h    |   9 +
 scheduler/ipp.c      | 383 ++++++++++++++++++++++++++++---------------
 scheduler/policy.h   |   2 -
 scheduler/printers.c |   6 +-
 scheduler/printers.h |   2 -
 8 files changed, 271 insertions(+), 150 deletions(-)

--- a/scheduler/auth.c
+++ b/scheduler/auth.c
@@ -105,7 +105,7 @@
   cupsd_authmask_t	temp;		/* New host/domain mask */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddIPMask(masks=%p(%p), address=%x:%x:%x:%x, netmask=%x:%x:%x:%x)", masks, *masks, address[0], address[1], address[2], address[3], netmask[0], netmask[1], netmask[2], netmask[3]);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddIPMask(masks=%p(%p), address=%x:%x:%x:%x, netmask=%x:%x:%x:%x)", (void *)masks, (void *)*masks, address[0], address[1], address[2], address[3], netmask[0], netmask[1], netmask[2], netmask[3]);
 
   temp.type = CUPSD_AUTH_IP;
   memcpy(temp.mask.ip.address, address, sizeof(temp.mask.ip.address));
@@ -158,7 +158,7 @@
 cupsdAddName(cupsd_location_t *loc,	/* I - Location to add to */
              char             *name)	/* I - Name to add */
 {
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddName(loc=%p, name=\"%s\")", loc, name);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddName(loc=%p, name=\"%s\")", (void *)loc, name);
 
   if (!loc->names)
     loc->names = cupsArrayNew3(NULL, NULL, NULL, 0,
@@ -188,7 +188,7 @@
 			*ifptr;		/* Pointer to end of name */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddNameMask(masks=%p(%p), name=\"%s\")", masks, *masks, name);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddNameMask(masks=%p(%p), name=\"%s\")", (void *)masks, (void *)*masks, name);
 
   if (!_cups_strcasecmp(name, "@LOCAL"))
   {
@@ -274,7 +274,7 @@
   con->best = cupsdFindBest(con->uri, httpGetState(con->http));
   con->type = CUPSD_AUTH_NONE;
 
-  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "con->uri=\"%s\", con->best=%p(%s)", con->uri, con->best, con->best ? con->best->location : "");
+  cupsdLogClient(con, CUPSD_LOG_DEBUG2, "con->uri=\"%s\", con->best=%p(%s)", con->uri, (void *)con->best, con->best ? con->best->location : "");
 
   if (con->best && con->best->type != CUPSD_AUTH_NONE)
   {
@@ -1143,7 +1143,7 @@
 #endif /* HAVE_MBR_UID_TO_UUID */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCheckGroup(username=\"%s\", user=%p, groupname=\"%s\")", username, user, groupname);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCheckGroup(username=\"%s\", user=%p, groupname=\"%s\")", username, (void *)user, groupname);
 
  /*
   * Validate input...
@@ -1558,7 +1558,7 @@
 		};
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)", con->uri, con->best, con->best ? con->best->location ? con->best->location : "(null)" : "");
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: con->uri=\"%s\", con->best=%p(%s)", con->uri, (void *)con->best, con->best ? con->best->location ? con->best->location : "(null)" : "");
   if (owner)
     cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdIsAuthorized: owner=\"%s\"", owner);
 
@@ -2129,7 +2129,6 @@
 
   free(snap_name);
   g_clear_object(&snap);
-
 #  endif // CUPS_SNAP
 
   done:
@@ -2140,6 +2139,8 @@
   return (ret);
 
 #else
+  (void)con;
+
   // No AppArmor/snapd to deal with...
   return (1);
 #endif // HAVE_LIBAPPARMOR && HAVE_LIBSNAPDGLIB
--- a/scheduler/auth.h
+++ b/scheduler/auth.h
@@ -98,8 +98,6 @@
   http_encryption_t	encryption;	/* To encrypt or not to encrypt... */
 } cupsd_location_t;
 
-typedef struct cupsd_client_s cupsd_client_t;
-
 
 /*
  * Globals...
--- a/scheduler/client.h
+++ b/scheduler/client.h
@@ -42,6 +42,8 @@
 			*query_string;	/* QUERY_STRING environment variable */
   int			file;		/* Input/output file */
   int			file_ready;	/* Input ready on file/pipe? */
+  int			bg_pending;	/* Background response pending? */
+  cupsd_printer_t	*bg_printer;	/* Background printer */
   int			pipe_pid;	/* Pipe process ID (or 0 if not a pipe) */
   http_status_t		pipe_status;	/* HTTP status from pipe process */
   int			sent_header,	/* Non-zero if sent HTTP header */
--- a/scheduler/cupsd.h
+++ b/scheduler/cupsd.h
@@ -100,6 +100,15 @@
 
 
 /*
+ * Base types...
+ */
+
+typedef struct cupsd_client_s cupsd_client_t;
+typedef struct cupsd_job_s cupsd_job_t;
+typedef struct cupsd_printer_s cupsd_printer_t;
+
+
+/*
  * Other stuff for the scheduler...
  */
 
--- a/scheduler/ipp.c
+++ b/scheduler/ipp.c
@@ -73,7 +73,7 @@
 					cups_array_t *ra,
 					cups_array_t *exclude);
 static void	create_job(cupsd_client_t *con, ipp_attribute_t *uri);
-static void	*create_local_bg_thread(cupsd_printer_t *printer);
+static void	*create_local_bg_thread(cupsd_client_t *con);
 static void	create_local_printer(cupsd_client_t *con);
 static cups_array_t *create_requested_array(ipp_t *request);
 static void	create_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
@@ -111,6 +111,7 @@
 static void	send_http_error(cupsd_client_t *con, http_status_t status,
 		                cupsd_printer_t *printer);
 static void	send_ipp_status(cupsd_client_t *con, ipp_status_t status, const char *message, ...) _CUPS_FORMAT(3, 4);
+static int	send_response(cupsd_client_t *con);
 static void	set_default(cupsd_client_t *con, ipp_attribute_t *uri);
 static void	set_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
 static void	set_printer_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
@@ -143,7 +144,7 @@
   int			valid = 1;	/* Valid request? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdProcessIPPRequest(%p[%d]): operation_id=%04x(%s)", con, con->number, con->request->request.op.operation_id, ippOpString(con->request->request.op.operation_id));
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdProcessIPPRequest(%p[%d]): operation_id=%04x(%s)", (void *)con, con->number, con->request->request.op.operation_id, ippOpString(con->request->request.op.operation_id));
 
   if (LogLevel >= CUPSD_LOG_DEBUG2)
   {
@@ -615,68 +616,13 @@
     }
   }
 
-  if (con->response)
+  if (!con->bg_pending && con->response)
   {
    /*
     * Sending data from the scheduler...
     */
 
-    cupsdLogClient(con, con->response->request.status.status_code >= IPP_STATUS_ERROR_BAD_REQUEST && con->response->request.status.status_code != IPP_STATUS_ERROR_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG, "Returning IPP %s for %s (%s) from %s.",  ippErrorString(con->response->request.status.status_code), ippOpString(con->request->request.op.operation_id), uri ? uri->values[0].string.text : "no URI", con->http->hostname);
-
-    httpClearFields(con->http);
-
-#ifdef CUPSD_USE_CHUNKING
-   /*
-    * Because older versions of CUPS (1.1.17 and older) and some IPP
-    * clients do not implement chunking properly, we cannot use
-    * chunking by default.  This may become the default in future
-    * CUPS releases, or we might add a configuration directive for
-    * it.
-    */
-
-    if (con->http->version == HTTP_1_1)
-    {
-      cupsdLogClient(con, CUPSD_LOG_DEBUG, "Transfer-Encoding: chunked");
-      cupsdSetLength(con->http, 0);
-    }
-    else
-#endif /* CUPSD_USE_CHUNKING */
-    {
-      size_t	length;			/* Length of response */
-
-
-      length = ippLength(con->response);
-
-      if (con->file >= 0 && !con->pipe_pid)
-      {
-	struct stat	fileinfo;	/* File information */
-
-	if (!fstat(con->file, &fileinfo))
-	  length += (size_t)fileinfo.st_size;
-      }
-
-      cupsdLogClient(con, CUPSD_LOG_DEBUG, "Content-Length: " CUPS_LLFMT, CUPS_LLCAST length);
-      httpSetLength(con->http, length);
-    }
-
-    if (cupsdSendHeader(con, HTTP_OK, "application/ipp", CUPSD_AUTH_NONE))
-    {
-     /*
-      * Tell the caller the response header was sent successfully...
-      */
-
-      cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, (cupsd_selfunc_t)cupsdWriteClient, con);
-
-      return (1);
-    }
-    else
-    {
-     /*
-      * Tell the caller the response header could not be sent...
-      */
-
-      return (0);
-    }
+    return (send_response(con));
   }
   else
   {
@@ -747,7 +693,7 @@
   cupsd_printer_t *printer;		/* Printer data */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "accept_jobs(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "accept_jobs(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -832,7 +778,7 @@
   int		need_restart_job;	/* Need to restart job? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_class(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_class(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -1142,7 +1088,7 @@
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
         	  "add_file(con=%p[%d], job=%d, filetype=%s/%s, "
-		  "compression=%d)", con, con ? con->number : -1, job->id,
+		  "compression=%d)", (void *)con, con ? con->number : -1, job->id,
 		  filetype->super, filetype->type, compression);
 
  /*
@@ -1245,8 +1191,8 @@
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))",
-                  con, con->number, printer, printer->name,
-		  filetype, filetype ? filetype->super : "none",
+                  (void *)con, con->number, (void *)printer, printer->name,
+		  (void *)filetype, filetype ? filetype->super : "none",
 		  filetype ? filetype->type : "none");
 
  /*
@@ -2226,7 +2172,7 @@
 		set_port_monitor;	/* Did we set the port monitor? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_printer(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -2717,8 +2663,21 @@
 	return;
       }
 
-      // Run a background thread to create the PPD...
-      _cupsThreadCreate((_cups_thread_func_t)create_local_bg_thread, printer);
+      if (!printer->printer_id)
+	printer->printer_id = NextPrinterId ++;
+
+      cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
+
+      cupsdSetPrinterAttrs(printer);
+
+      /* Run a background thread to create the PPD... */
+      cupsdLogClient(con, CUPSD_LOG_DEBUG, "Creating PPD in background thread.");
+
+      con->bg_pending = 1;
+      con->bg_printer = printer;
+
+      _cupsThreadCreate((_cups_thread_func_t)create_local_bg_thread, con);
+      return;
     }
     else if (!strcmp(ppd_name, "raw"))
     {
@@ -2883,7 +2842,7 @@
 {
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "add_printer_state_reasons(%p[%d], %p[%s])",
-                  con, con->number, p, p->name);
+                  (void *)con, con->number, (void *)p, p->name);
 
   if (p->num_reasons == 0)
     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
@@ -2909,7 +2868,7 @@
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])",
-                  con, con->number, p, p->name);
+                  (void *)con, con->number, (void *)p, p->name);
 
   count = cupsdGetPrinterJobCount(p->name);
 
@@ -2999,7 +2958,7 @@
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "authenticate_job(%p[%d], %s)",
-                  con, con->number, uri->values[0].string.text);
+                  (void *)con, con->number, uri->values[0].string.text);
 
  /*
   * Start with "everything is OK" status...
@@ -3177,7 +3136,7 @@
   cupsd_job_t	*job;			/* Job */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_all_jobs(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -3414,7 +3373,7 @@
   cupsd_jobaction_t purge;		/* Purge the job? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -3615,7 +3574,7 @@
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "cancel_subscription(con=%p[%d], sub_id=%d)",
-                  con, con->number, sub_id);
+                  (void *)con, con->number, sub_id);
 
  /*
   * Is the subscription ID valid?
@@ -3721,7 +3680,7 @@
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
-                  con, con->number, p, p->name);
+                  (void *)con, con->number, (void *)p, p->name);
 
  /*
   * Figure out who is printing...
@@ -3943,7 +3902,7 @@
 			username[256];	/* User name */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "close_job(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "close_job(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -4063,7 +4022,7 @@
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "copy_attrs(to=%p, from=%p, ra=%p, group=%x, quickcopy=%d)",
-		  to, from, ra, group, quickcopy);
+		  (void *)to, (void *)from, (void *)ra, group, quickcopy);
 
   if (!to || !from)
     return;
@@ -4147,7 +4106,7 @@
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "copy_banner(con=%p[%d], job=%p[%d], name=\"%s\")",
-                  con, con ? con->number : -1, job, job->id,
+                  (void *)con, con ? con->number : -1, (void *)job, job->id,
 		  name ? name : "(null)");
 
  /*
@@ -4492,7 +4451,7 @@
 					/* cupsProtocol attribute */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_model(con=%p, from=\"%s\", to=\"%s\")", con, from, to);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_model(con=%p, from=\"%s\", to=\"%s\")", (void *)con, from, to);
 
  /*
   * Run cups-driverd to get the PPD file...
@@ -5106,7 +5065,7 @@
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "copy_subscription_attrs(con=%p, sub=%p, ra=%p, exclude=%p)",
-		  con, sub, ra, exclude);
+		  (void *)con, (void *)sub, (void *)ra, (void *)exclude);
 
  /*
   * Copy the subscription attributes to the response using the
@@ -5220,7 +5179,7 @@
   };
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -5285,11 +5244,14 @@
 
 static void *				/* O - Exit status */
 create_local_bg_thread(
-    cupsd_printer_t *printer)		/* I - Printer */
+    cupsd_client_t *con)		/* I - Client */
 {
+  cupsd_printer_t *printer = con->bg_printer;
+					/* Printer */
   cups_file_t	*from,			/* Source file */
 		*to;			/* Destination file */
-  char		fromppd[1024],		/* Source PPD */
+  char		device_uri[1024],	/* Device URI */
+		fromppd[1024],		/* Source PPD */
 		toppd[1024],		/* Destination PPD */
 		scheme[32],		/* URI scheme */
 		userpass[256],		/* User:pass */
@@ -5301,7 +5263,7 @@
   http_encryption_t encryption;		/* Type of encryption to use */
   http_t	*http;			/* Connection to printer */
   ipp_t		*request,		/* Request to printer */
-		*response;		/* Response from printer */
+		*response = NULL;	/* Response from printer */
   ipp_attribute_t *attr;		/* Attribute in response */
   ipp_status_t	status;			/* Status code */
   static const char * const pattrs[] =	/* Printer attributes we need */
@@ -5315,26 +5277,47 @@
   * Try connecting to the printer...
   */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Generating PPD file from \"%s\"...", printer->name, printer->device_uri);
+  _cupsRWLockRead(&printer->lock);
+  strlcpy(device_uri, printer->device_uri, sizeof(device_uri));
+  _cupsRWUnlock(&printer->lock);
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "%s: Generating PPD file from \"%s\"...", printer->name, device_uri);
 
-  if (strstr(printer->device_uri, "._tcp"))
+  if (strstr(device_uri, "._tcp"))
   {
-    cupsdLogMessage(CUPSD_LOG_DEBUG2, "%s: Resolving mDNS URI \"%s\".", printer->name, printer->device_uri);
+    cupsdLogMessage(CUPSD_LOG_DEBUG2, "%s: Resolving mDNS URI \"%s\".", printer->name, device_uri);
 
-    if (!_httpResolveURI(printer->device_uri, uri, sizeof(uri), _HTTP_RESOLVE_DEFAULT, NULL, NULL))
+    if (!_httpResolveURI(device_uri, uri, sizeof(uri), _HTTP_RESOLVE_DEFAULT, NULL, NULL))
     {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Couldn't resolve mDNS URI \"%s\".", printer->name, printer->device_uri);
-      return (NULL);
+      cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Couldn't resolve mDNS URI \"%s\".", printer->name, device_uri);
+
+      /* Force printer to timeout and be deleted */
+      _cupsRWLockWrite(&printer->lock);
+      printer->state_time = 0;
+      _cupsRWUnlock(&printer->lock);
+
+      send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Couldn't resolve mDNS URI \"%s\"."), printer->device_uri);
+      goto finish_response;
     }
 
+    _cupsRWLockWrite(&printer->lock);
     cupsdSetString(&printer->device_uri, uri);
+    _cupsRWUnlock(&printer->lock);
+
+    strlcpy(device_uri, uri, sizeof(device_uri));
   }
 
-  if (httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
+  if (httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), userpass, sizeof(userpass), host, sizeof(host), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Bad device URI \"%s\".", printer->name, printer->device_uri);
-    return (NULL);
+    cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Bad device URI \"%s\".", printer->name, device_uri);
+
+    /* Force printer to timeout and be deleted */
+    _cupsRWLockWrite(&printer->lock);
+    printer->state_time = 0;
+    _cupsRWUnlock(&printer->lock);
+
+    send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Bad device URI \"%s\"."), device_uri);
+    goto finish_response;
   }
 
   if (!strcmp(scheme, "ipps") || port == 443)
@@ -5345,7 +5328,14 @@
   if ((http = httpConnect2(host, port, NULL, AF_UNSPEC, encryption, 1, 30000, NULL)) == NULL)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to connect to %s:%d: %s", printer->name, host, port, cupsLastErrorString());
-    return (NULL);
+
+    /* Force printer to timeout and be deleted */
+    _cupsRWLockWrite(&printer->lock);
+    printer->state_time = 0;
+    _cupsRWUnlock(&printer->lock);
+
+    send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to connect to %s:%d: %s"), host, port, cupsLastErrorString());
+    goto finish_response;
   }
 
  /*
@@ -5389,6 +5379,7 @@
   * If we did not succeed to obtain the "media-col-database" attribute
   * try to get it separately
   */
+
   if (ippFindAttribute(response, "media-col-database", IPP_TAG_ZERO) ==
       NULL)
   {
@@ -5447,17 +5438,29 @@
     if ((from = cupsFileOpen(fromppd, "r")) == NULL)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to read generated PPD: %s", printer->name, strerror(errno));
-      ippDelete(response);
-      return (NULL);
+
+      /* Force printer to timeout and be deleted */
+      _cupsRWLockWrite(&printer->lock);
+      printer->state_time = 0;
+      _cupsRWUnlock(&printer->lock);
+
+      send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to read generated PPD: %s"), strerror(errno));
+      goto finish_response;
     }
 
     snprintf(toppd, sizeof(toppd), "%s/ppd/%s.ppd", ServerRoot, printer->name);
     if ((to = cupsdCreateConfFile(toppd, ConfigFilePerm)) == NULL)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR, "%s: Unable to create PPD for printer: %s", printer->name, strerror(errno));
-      ippDelete(response);
       cupsFileClose(from);
-      return (NULL);
+
+      /* Force printer to timeout and be deleted */
+      _cupsRWLockWrite(&printer->lock);
+      printer->state_time = 0;
+      _cupsRWUnlock(&printer->lock);
+
+      send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to create PPD for printer: %s"), strerror(errno));
+      goto finish_response;
     }
 
     while (cupsFileGets(from, line, sizeof(line)))
@@ -5466,10 +5469,14 @@
     cupsFileClose(from);
     if (!cupsdCloseCreatedConfFile(to, toppd))
     {
+      _cupsRWLockWrite(&printer->lock);
+
       printer->config_time = time(NULL);
       printer->state       = IPP_PSTATE_IDLE;
       printer->accepting   = 1;
 
+      _cupsRWUnlock(&printer->lock);
+
       cupsdSetPrinterAttrs(printer);
 
       cupsdAddEvent(CUPSD_EVENT_PRINTER_CONFIG, printer, NULL, "Printer \"%s\" is now available.", printer->name);
@@ -5477,10 +5484,39 @@
     }
   }
   else
+  {
     cupsdLogMessage(CUPSD_LOG_ERROR, "%s: PPD creation failed: %s", printer->name, cupsLastErrorString());
 
+    /* Force printer to timeout and be deleted */
+    _cupsRWLockWrite(&printer->lock);
+    printer->state_time = 0;
+    _cupsRWUnlock(&printer->lock);
+
+    send_ipp_status(con, IPP_STATUS_ERROR_DEVICE, _("Unable to create PPD: %s"), cupsLastErrorString());
+    goto finish_response;
+  }
+
+ /*
+  * Respond to the client...
+  */
+
+  send_ipp_status(con, IPP_STATUS_OK, _("Local printer created."));
+
+  ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
+  ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_ENUM, "printer-state", (int)printer->state);
+  add_printer_state_reasons(con, printer);
+
+  httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), httpIsEncrypted(con->http) ? "ipps" : "ipp", NULL, con->clientname, con->clientport, "/printers/%s", printer->name);
+  ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-uri-supported", NULL, uri);
+
+  finish_response:
+
   ippDelete(response);
 
+  send_response(con);
+
+  con->bg_pending = 0;
+
   return (NULL);
 }
 
@@ -5592,10 +5628,21 @@
 
   if ((printer = cupsdFindDest(name)) != NULL)
   {
-    send_ipp_status(con, IPP_STATUS_ERROR_NOT_POSSIBLE, _("Printer \"%s\" already exists."), name);
+    printer->state_time = time(NULL);
+    send_ipp_status(con, IPP_STATUS_OK, _("Printer \"%s\" already exists."), name);
     goto add_printer_attributes;
   }
 
+  for (printer = (cupsd_printer_t *)cupsArrayFirst(Printers); printer; printer = (cupsd_printer_t *)cupsArrayNext(Printers))
+  {
+    if (printer->device_uri && !strcmp(ptr, printer->device_uri))
+    {
+      printer->state_time = time(NULL);
+      send_ipp_status(con, IPP_STATUS_OK, _("Printer \"%s\" already exists."), printer->name);
+      goto add_printer_attributes;
+    }
+  }
+
  /*
   * Create the printer...
   */
@@ -5682,14 +5729,17 @@
   * Run a background thread to create the PPD...
   */
 
-  _cupsThreadCreate((_cups_thread_func_t)create_local_bg_thread, printer);
+  con->bg_pending = 1;
+  con->bg_printer = printer;
+
+  _cupsThreadCreate((_cups_thread_func_t)create_local_bg_thread, con);
+
+  return;
 
  /*
   * Return printer attributes...
   */
 
-  send_ipp_status(con, IPP_STATUS_OK, _("Local printer created."));
-
   add_printer_attributes:
 
   ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs", (char)printer->accepting);
@@ -5795,7 +5845,7 @@
   * Is the destination valid?
   */
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG, "create_subscriptions(con=%p(%d), uri=\"%s\")", con, con->number, uri->values[0].string.text);
+  cupsdLogMessage(CUPSD_LOG_DEBUG, "create_subscriptions(con=%p(%d), uri=\"%s\")", (void *)con, con->number, uri->values[0].string.text);
 
   httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
                   sizeof(scheme), userpass, sizeof(userpass), host,
@@ -6143,7 +6193,7 @@
   int		temporary;		/* Temporary queue? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "delete_printer(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -6256,7 +6306,7 @@
   cups_array_t	*ra;			/* Requested attributes array */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", con, con->number);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_default(%p[%d])", (void *)con, con->number);
 
  /*
   * Check policy...
@@ -6306,7 +6356,7 @@
 					/* String for included schemes */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", con, con->number);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_devices(%p[%d])", (void *)con, con->number);
 
  /*
   * Check policy...
@@ -6399,7 +6449,7 @@
 		format[1024];		/* Format for document */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -6546,7 +6596,7 @@
 		*exclude;		/* Private attributes array */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -6677,7 +6727,7 @@
   cupsd_policy_t *policy;		/* Current policy */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->number,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", (void *)con, con->number,
                   uri->values[0].string.text);
 
  /*
@@ -6988,7 +7038,7 @@
       cupsdLogMessage(CUPSD_LOG_DEBUG2,
 		      "get_jobs: job->id=%d, dest=\"%s\", username=\"%s\", "
 		      "state_value=%d, attrs=%p", job->id, job->dest,
-		      job->username, job->state_value, job->attrs);
+		      job->username, job->state_value, (void *)job->attrs);
 
       if (!job->dest || !job->username)
 	cupsdLoadJob(job);
@@ -7067,7 +7117,7 @@
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
-                  con, con->number);
+                  (void *)con, con->number);
 
  /*
   * Get subscription attributes...
@@ -7200,8 +7250,8 @@
   cups_ptype_t		dtype;		/* Destination type */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", con,
-                  con->number, uri, uri->name, uri->values[0].string.text);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppd(%p[%d], %p[%s=%s])", (void *)con,
+                  con->number, (void *)uri, uri->name, uri->values[0].string.text);
 
   if (!strcmp(ippGetName(uri), "ppd-name"))
   {
@@ -7373,7 +7423,7 @@
 					/* String for included schemes */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", con, con->number);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_ppds(%p[%d])", (void *)con, con->number);
 
  /*
   * Check policy...
@@ -7517,7 +7567,7 @@
   cups_array_t		*ra;		/* Requested attributes array */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_attrs(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -7573,7 +7623,7 @@
   cupsd_printer_t	*printer;	/* Printer/class */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printer_supported(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -7643,7 +7693,7 @@
   int		local;			/* Local connection? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_printers(%p[%d], %x)", (void *)con,
                   con->number, type);
 
  /*
@@ -7804,7 +7854,7 @@
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "get_subscription_attrs(con=%p[%d], sub_id=%d)",
-                  con, con->number, sub_id);
+                  (void *)con, con->number, sub_id);
 
  /*
   * Expire subscriptions as needed...
@@ -7891,7 +7941,7 @@
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "get_subscriptions(con=%p[%d], uri=%s)",
-                  con, con->number, uri->values[0].string.text);
+                  (void *)con, con->number, uri->values[0].string.text);
 
  /*
   * Is the destination valid?
@@ -8059,7 +8109,7 @@
   cupsd_job_t	*job;			/* Job information */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", con, con->number,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_job(%p[%d], %s)", (void *)con, con->number,
                   uri->values[0].string.text);
 
  /*
@@ -8190,7 +8240,7 @@
   cupsd_printer_t	*printer;	/* Printer data */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "hold_new_jobs(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -8267,7 +8317,7 @@
 		*dprinter;		/* Destination printer */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", con, con->number,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "move_job(%p[%d], %s)", (void *)con, con->number,
                   uri->values[0].string.text);
 
  /*
@@ -8596,7 +8646,7 @@
   int		compression;		/* Document compression */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", con, con->number,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "print_job(%p[%d], %s)", (void *)con, con->number,
                   uri->values[0].string.text);
 
  /*
@@ -9047,7 +9097,7 @@
   ipp_attribute_t *attr;		/* printer-state-message text */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "reject_jobs(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -9129,7 +9179,7 @@
   cupsd_printer_t	*printer;	/* Printer data */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_held_new_jobs(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -9202,7 +9252,7 @@
   cupsd_job_t	*job;			/* Job information */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "release_job(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -9339,7 +9389,7 @@
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "renew_subscription(con=%p[%d], sub_id=%d)",
-                  con, con->number, sub_id);
+                  (void *)con, con->number, sub_id);
 
  /*
   * Is the subscription ID valid?
@@ -9426,7 +9476,7 @@
   int		port;			/* Port portion of URI */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "restart_job(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -9777,7 +9827,7 @@
   int			start_job;	/* Start the job? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "send_document(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -10289,6 +10339,82 @@
 
 
 /*
+ * 'send_response()' - Send the IPP response.
+ */
+
+static int				/* O - 1 on success, 0 on failure */
+send_response(cupsd_client_t *con)	/* I - Client */
+{
+  ipp_attribute_t	*uri;		/* Target URI */
+  int			ret = 0;	/* Return value */
+  static _cups_mutex_t	mutex = _CUPS_MUTEX_INITIALIZER;
+					/* Mutex for logging/access */
+
+
+  _cupsMutexLock(&mutex);
+
+  if ((uri = ippFindAttribute(con->request, "printer-uri", IPP_TAG_URI)) == NULL)
+  {
+    if ((uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI)) == NULL)
+      uri = ippFindAttribute(con->request, "ppd-name", IPP_TAG_NAME);
+  }
+
+  cupsdLogClient(con, con->response->request.status.status_code >= IPP_STATUS_ERROR_BAD_REQUEST && con->response->request.status.status_code != IPP_STATUS_ERROR_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG, "Returning IPP %s for %s (%s) from %s.",  ippErrorString(con->response->request.status.status_code), ippOpString(con->request->request.op.operation_id), uri ? uri->values[0].string.text : "no URI", con->http->hostname);
+
+  httpClearFields(con->http);
+
+#ifdef CUPSD_USE_CHUNKING
+ /*
+  * Because older versions of CUPS (1.1.17 and older) and some IPP
+  * clients do not implement chunking properly, we cannot use
+  * chunking by default.  This may become the default in future
+  * CUPS releases, or we might add a configuration directive for
+  * it.
+  */
+
+  if (con->http->version == HTTP_1_1)
+  {
+    cupsdLogClient(con, CUPSD_LOG_DEBUG, "Transfer-Encoding: chunked");
+    cupsdSetLength(con->http, 0);
+  }
+  else
+#endif /* CUPSD_USE_CHUNKING */
+  {
+    size_t	length;			/* Length of response */
+
+
+    length = ippLength(con->response);
+
+    if (con->file >= 0 && !con->pipe_pid)
+    {
+      struct stat	fileinfo;	/* File information */
+
+      if (!fstat(con->file, &fileinfo))
+	length += (size_t)fileinfo.st_size;
+    }
+
+    cupsdLogClient(con, CUPSD_LOG_DEBUG, "Content-Length: " CUPS_LLFMT, CUPS_LLCAST length);
+    httpSetLength(con->http, length);
+  }
+
+  if (cupsdSendHeader(con, HTTP_STATUS_OK, "application/ipp", CUPSD_AUTH_NONE))
+  {
+   /*
+    * Tell the caller the response header was sent successfully...
+    */
+
+    cupsdAddSelect(httpGetFd(con->http), (cupsd_selfunc_t)cupsdReadClient, (cupsd_selfunc_t)cupsdWriteClient, con);
+
+    ret = 1;
+  }
+
+  _cupsMutexUnlock(&mutex);
+
+  return (ret);
+}
+
+
+/*
  * 'set_default()' - Set the default destination...
  */
 
@@ -10302,7 +10428,7 @@
 			*oldprinter;	/* Old default printer */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_default(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -10384,7 +10510,7 @@
   int			check_jobs;	/* Check jobs? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -10764,7 +10890,7 @@
   int			changed = 0;	/* Was anything changed? */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_printer_attrs(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -11110,7 +11236,7 @@
   cupsd_printer_t	*printer;	/* Printer data */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_printer(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -11192,7 +11318,7 @@
   ipp_attribute_t	*attr;		/* printer-state-message attribute */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_printer(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -11434,7 +11560,7 @@
   cupsd_printer_t	*printer;	/* Printer */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", con,
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_job(%p[%d], %s)", (void *)con,
                   con->number, uri->values[0].string.text);
 
  /*
@@ -11627,7 +11753,7 @@
   cupsd_printer_t	*printer;	/* Printer for job */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, userlen=" CUPS_LLFMT ")", job->id, con ? con->number : 0, owner ? owner : "(null)", username, CUPS_LLCAST userlen);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "validate_user(job=%d, con=%d, owner=\"%s\", username=%p, userlen=" CUPS_LLFMT ")", job->id, con ? con->number : 0, owner ? owner : "(null)", (void *)username, CUPS_LLCAST userlen);
 
  /*
   * Validate input...
--- a/scheduler/policy.h
+++ b/scheduler/policy.h
@@ -22,8 +22,6 @@
 			*ops;		/* Operations */
 } cupsd_policy_t;
 
-typedef struct cupsd_printer_s cupsd_printer_t;
-
 
 /*
  * Globals...
--- a/scheduler/printers.c
+++ b/scheduler/printers.c
@@ -620,7 +620,7 @@
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeletePrinter(p=%p(%s), update=%d)",
-                  p, p->name, update);
+                  (void *)p, p->name, update);
 
  /*
   * Save the current position in the Printers array...
@@ -782,11 +782,11 @@
 		  "cupsdDeleteTemporaryPrinters: Removing unused temporary printers");
 
  /*
-  * Allow temporary printers to stick around for 60 seconds after the last job
+  * Allow temporary printers to stick around for 5 minutes after the last job
   * completes.
   */
 
-  unused_time = time(NULL) - 60;
+  unused_time = time(NULL) - 300;
 
   for (p = (cupsd_printer_t *)cupsArrayFirst(Printers); p; p = (cupsd_printer_t *)cupsArrayNext(Printers))
   {
@@ -2550,7 +2550,7 @@
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
-		  "cupsdSetPrinterReasons(p=%p(%s),s=\"%s\"", p, p->name, s);
+		  "cupsdSetPrinterReasons(p=%p(%s),s=\"%s\"", (void *)p, p->name, s);
 
   if (s[0] == '-' || s[0] == '+')
   {
@@ -3396,7 +3396,7 @@
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
                   "add_printer_filter(p=%p(%s), filtertype=%p(%s/%s), "
-		  "filter=\"%s\")", p, p->name, filtertype, filtertype->super,
+		  "filter=\"%s\")", (void *)p, p->name, (void *)filtertype, filtertype->super,
 		  filtertype->type, filter);
 
  /*
--- a/scheduler/printers.h
+++ b/scheduler/printers.h
@@ -50,8 +50,6 @@
  * Printer/class information structure...
  */
 
-typedef struct cupsd_job_s cupsd_job_t;
-
 struct cupsd_printer_s
 {
   _cups_rwlock_t lock;			/* Concurrency lock for background updates */
--- a/scheduler/client.c
+++ b/scheduler/client.c
@@ -430,6 +430,14 @@
     con->file = -1;
   }
 
+  if (con->bg_pending)
+  {
+   /*
+    * Don't close connection when there is a background thread pending
+    */
+    partial = 1;
+  }
+
  /*
   * Close the socket and clear the file from the input set for select()...
   */
