]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/ipp.c
Import CUPS v1.7.1
[thirdparty/cups.git] / scheduler / ipp.c
index e5a464737b5b1dfc8be97e9fff1127bd40537bc1..a3e55c2bd474e33d9701df0c4df39ebfd769b1ec 100644 (file)
 /*
- * "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $"
+ * "$Id: ipp.c 11500 2014-01-06 22:21:15Z msweet $"
  *
- *   IPP routines for the CUPS scheduler.
+ * IPP routines for the CUPS scheduler.
  *
- *   Copyright 2007-2012 by Apple Inc.
- *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
- *   This file contains Kerberos support code, copyright 2006 by
- *   Jelmer Vernooij.
+ * This file contains Kerberos support code, copyright 2006 by
+ * Jelmer Vernooij.
  *
- *   These coded instructions, statements, and computer programs are the
- *   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/".
- *
- * Contents:
- *
- *   cupsdProcessIPPRequest()    - Process an incoming IPP request.
- *   cupsdTimeoutJob()           - Timeout a job waiting on job files.
- *   accept_jobs()               - Accept print jobs to a printer.
- *   add_class()                 - Add a class to the system.
- *   add_file()                  - Add a file to a job.
- *   add_job()                   - Add a job to a print queue.
- *   add_job_subscriptions()     - Add any subscriptions for a job.
- *   add_job_uuid()              - Add job-uuid attribute to a job.
- *   add_printer()               - Add a printer to the system.
- *   add_printer_state_reasons() - Add the "printer-state-reasons" attribute
- *                                 based upon the printer state...
- *   add_queued_job_count()      - Add the "queued-job-count" attribute for the
- *                                 specified printer or class.
- *   apple_init_profile()        - Initialize a color profile.
- *   apple_register_profiles()   - Register color profiles for a printer.
- *   apple_unregister_profiles() - Remove color profiles for the specified
- *                                 printer.
- *   apply_printer_defaults()    - Apply printer default options to a job.
- *   authenticate_job()          - Set job authentication info.
- *   cancel_all_jobs()           - Cancel all or selected print jobs.
- *   cancel_job()                - Cancel a print job.
- *   cancel_subscription()       - Cancel a subscription.
- *   check_rss_recipient()       - Check that we do not have a duplicate RSS
- *                                 feed URI.
- *   check_quotas()              - Check quotas for a printer and user.
- *   close_job()                 - Close a multi-file job.
- *   copy_attrs()                - Copy attributes from one request to another.
- *   copy_banner()               - Copy a banner file to the requests directory
- *                                 for the specified job.
- *   copy_file()                 - Copy a PPD file or interface script...
- *   copy_model()                - Copy a PPD model file, substituting default
- *                                 values as needed...
- *   copy_job_attrs()            - Copy job attributes.
- *   copy_printer_attrs()        - Copy printer attributes.
- *   copy_subscription_attrs()   - Copy subscription attributes.
- *   create_job()                - Print a file to a printer or class.
- *   create_requested_array()    - Create an array for the requested-attributes.
- *   create_subscription()       - Create a notification subscription.
- *   delete_printer()            - Remove a printer or class from the system.
- *   get_default()               - Get the default destination.
- *   get_devices()               - Get the list of available devices on the
- *                                 local system.
- *   get_document()              - Get a copy of a job file.
- *   get_job_attrs()             - Get job attributes.
- *   get_jobs()                  - Get a list of jobs for the specified printer.
- *   get_notifications()         - Get events for a subscription.
- *   get_ppd()                   - Get a named PPD from the local system.
- *   get_ppds()                  - Get the list of PPD files on the local
- *                                 system.
- *   get_printer_attrs()         - Get printer attributes.
- *   get_printer_supported()     - Get printer supported values.
- *   get_printers()              - Get a list of printers or classes.
- *   get_subscription_attrs()    - Get subscription attributes.
- *   get_subscriptions()         - Get subscriptions.
- *   get_username()              - Get the username associated with a request.
- *   hold_job()                  - Hold a print job.
- *   hold_new_jobs()             - Hold pending/new jobs on a printer or class.
- *   move_job()                  - Move a job to a new destination.
- *   ppd_parse_line()            - Parse a PPD default line.
- *   print_job()                 - Print a file to a printer or class.
- *   read_job_ticket()           - Read a job ticket embedded in a print file.
- *   reject_jobs()               - Reject print jobs to a printer.
- *   release_held_new_jobs()     - Release pending/new jobs on a printer or
- *                                 class.
- *   release_job()               - Release a held print job.
- *   renew_subscription()        - Renew an existing subscription...
- *   restart_job()               - Restart an old print job.
- *   save_auth_info()            - Save authentication information for a job.
- *   send_document()             - Send a file to a printer or class.
- *   send_http_error()           - Send a HTTP error back to the IPP client.
- *   send_ipp_status()           - Send a status back to the IPP client.
- *   set_default()               - Set the default destination...
- *   set_job_attrs()             - Set job attributes.
- *   set_printer_attrs()         - Set printer attributes.
- *   set_printer_defaults()      - Set printer default options from a request.
- *   start_printer()             - Start a printer.
- *   stop_printer()              - Stop a printer.
- *   url_encode_attr()           - URL-encode a string attribute.
- *   url_encode_string()         - URL-encode a string.
- *   user_allowed()              - See if a user is allowed to print to a queue.
- *   validate_job()              - Validate printer options and destination.
- *   validate_name()             - Make sure the printer name only contains
- *                                 valid chars.
- *   validate_user()             - Validate the user for the request.
+ * These coded instructions, statements, and computer programs are the
+ * 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/".
  */
 
 /*
 #include <cups/ppd-private.h>
 
 #ifdef __APPLE__
-#  include <ApplicationServices/ApplicationServices.h>
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
+/*#  include <ApplicationServices/ApplicationServices.h>
 extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-#  include <CoreFoundation/CoreFoundation.h>
+#  include <CoreFoundation/CoreFoundation.h>*/
 #  ifdef HAVE_MEMBERSHIP_H
 #    include <membership.h>
 #  endif /* HAVE_MEMBERSHIP_H */
@@ -145,18 +56,6 @@ static void add_printer(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    add_printer_state_reasons(cupsd_client_t *con,
                                          cupsd_printer_t *p);
 static void    add_queued_job_count(cupsd_client_t *con, cupsd_printer_t *p);
-#ifdef __APPLE__
-static void    apple_init_profile(ppd_file_t *ppd, cups_array_t *languages,
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-                                   CFMutableDictionaryRef profile,
-#  else
-                                  CMDeviceProfileInfo *profile,
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-                                  unsigned id, const char *name,
-                                  const char *text, const char *iccfile);
-static void    apple_register_profiles(cupsd_printer_t *p);
-static void    apple_unregister_profiles(cupsd_printer_t *p);
-#endif /* __APPLE__ */
 static void    apply_printer_defaults(cupsd_printer_t *printer,
                                       cupsd_job_t *job);
 static void    authenticate_job(cupsd_client_t *con, ipp_attribute_t *uri);
@@ -371,7 +270,20 @@ cupsdProcessIPPRequest(
       if (attr && attr->name &&
           !strcmp(attr->name, "attributes-natural-language") &&
          (attr->value_tag & IPP_TAG_MASK) == IPP_TAG_LANGUAGE)
+      {
        language = attr;
+
+       /*
+        * Reset language for this request if different from Accept-Language.
+        */
+
+       if (!con->language ||
+           strcmp(attr->values[0].string.text, con->language->language))
+       {
+         cupsLangFree(con->language);
+         con->language = cupsLangGet(attr->values[0].string.text);
+       }
+      }
       else
        language = NULL;
 
@@ -725,17 +637,13 @@ cupsdProcessIPPRequest(
                         >= IPP_BAD_REQUEST &&
                     con->response->request.status.status_code
                        != IPP_NOT_FOUND ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
-                    "Returning IPP %s for %s (%s) from %s",
+                    "[Client %d] Returning IPP %s for %s (%s) from %s",
+                   con->http.fd,
                    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);
 
-    if (LogLevel == CUPSD_LOG_DEBUG2)
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                     "cupsdProcessIPPRequest: ippLength(response)=%ld",
-                     (long)ippLength(con->response));
-
     if (cupsdSendHeader(con, HTTP_OK, "application/ipp", CUPSD_AUTH_NONE))
     {
 #ifdef CUPSD_USE_CHUNKING
@@ -749,6 +657,10 @@ cupsdProcessIPPRequest(
 
       if (con->http.version == HTTP_1_1)
       {
+        cupsdLogMessage(CUPSD_LOG_DEBUG,
+                        "[Client %d] Transfer-Encoding: chunked",
+                        con->http.fd);
+
        if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n") < 0)
          return (0);
 
@@ -769,11 +681,13 @@ cupsdProcessIPPRequest(
        {
          struct stat   fileinfo;       /* File information */
 
-
           if (!fstat(con->file, &fileinfo))
            length += fileinfo.st_size;
        }
 
+        cupsdLogMessage(CUPSD_LOG_DEBUG,
+                        "[Client %d] Content-Length: " CUPS_LLFMT,
+                        con->http.fd, CUPS_LLCAST length);
        if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
                       CUPS_LLCAST length) < 0)
          return (0);
@@ -1254,6 +1168,12 @@ add_file(cupsd_client_t *con,            /* I - Connection to client */
                                           sizeof(mime_type_t *));
   }
 
+  if (compressions)
+    job->compressions = compressions;
+
+  if (filetypes)
+    job->filetypes = filetypes;
+
   if (!compressions || !filetypes)
   {
     cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
@@ -1266,9 +1186,7 @@ add_file(cupsd_client_t *con,             /* I - Connection to client */
     return (-1);
   }
 
-  job->compressions                 = compressions;
   job->compressions[job->num_files] = compression;
-  job->filetypes                    = filetypes;
   job->filetypes[job->num_files]    = filetype;
 
   job->num_files ++;
@@ -1292,6 +1210,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
   http_status_t        status;                 /* Policy status */
   ipp_attribute_t *attr,               /* Current attribute */
                *auth_info;             /* auth-info attribute */
+  const char   *mandatory;             /* Current mandatory job attribute */
   const char   *val;                   /* Default option value */
   int          priority;               /* Job priority */
   cupsd_job_t  *job;                   /* Current job */
@@ -1303,6 +1222,19 @@ add_job(cupsd_client_t  *con,            /* I - Client connection */
   ipp_attribute_t *media_col,          /* media-col attribute */
                *media_margin;          /* media-*-margin attribute */
   ipp_t                *unsup_col;             /* media-col in unsupported response */
+  static const char * const readonly[] =/* List of read-only attributes */
+  {
+    "job-id",
+    "job-k-octets-completed",
+    "job-impressions-completed",
+    "job-media-sheets-completed",
+    "job-state",
+    "job-state-message",
+    "job-state-reasons",
+    "time-at-completed",
+    "time-at-creation",
+    "time-at-processing"
+  };
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))",
@@ -1368,9 +1300,51 @@ add_job(cupsd_client_t  *con,            /* I - Client connection */
 
  /*
   * Validate job template attributes; for now just document-format,
-  * copies, number-up, and page-ranges...
+  * copies, job-sheets, number-up, page-ranges, mandatory attributes, and
+  * media...
   */
 
+  for (i = 0; i < (int)(sizeof(readonly) / sizeof(readonly[0])); i ++)
+  {
+    if ((attr = ippFindAttribute(con->request, readonly[i],
+                                 IPP_TAG_ZERO)) != NULL)
+    {
+      ippDeleteAttribute(con->request, attr);
+
+      if (StrictConformance)
+      {
+       send_ipp_status(con, IPP_BAD_REQUEST,
+                       _("The '%s' Job Description attribute cannot be "
+                         "supplied in a job creation request."), readonly[i]);
+       return (NULL);
+      }
+
+      cupsdLogMessage(CUPSD_LOG_INFO,
+                      "Unexpected '%s' Job Description attribute in a job "
+                      "creation request.", readonly[i]);
+    }
+  }
+
+  if (printer->pc)
+  {
+    for (mandatory = (char *)cupsArrayFirst(printer->pc->mandatory);
+        mandatory;
+        mandatory = (char *)cupsArrayNext(printer->pc->mandatory))
+    {
+      if (!ippFindAttribute(con->request, mandatory, IPP_TAG_ZERO))
+      {
+       /*
+       * Missing a required attribute...
+       */
+
+       send_ipp_status(con, IPP_CONFLICT,
+                       _("The \"%s\" attribute is required for print jobs."),
+                       mandatory);
+       return (NULL);
+      }
+    }
+  }
+
   if (filetype && printer->filetypes &&
       !cupsArrayFind(printer->filetypes, filetype))
   {
@@ -1556,9 +1530,27 @@ add_job(cupsd_client_t  *con,            /* I - Client connection */
                   priority);
   }
 
-  if (!ippFindAttribute(con->request, "job-name", IPP_TAG_NAME))
+  if ((attr = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) == NULL)
     ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_NAME, "job-name", NULL,
                  "Untitled");
+  else if ((attr->value_tag != IPP_TAG_NAME &&
+            attr->value_tag != IPP_TAG_NAMELANG) ||
+           attr->num_values != 1)
+  {
+    send_ipp_status(con, IPP_ATTRIBUTES,
+                    _("Bad job-name value: Wrong type or count."));
+    if ((attr = ippCopyAttribute(con->response, attr, 0)) != NULL)
+      attr->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
+    return (NULL);
+  }
+  else if (!ippValidateAttribute(attr))
+  {
+    send_ipp_status(con, IPP_ATTRIBUTES, _("Bad job-name value: %s"),
+                    cupsLastErrorString());
+    if ((attr = ippCopyAttribute(con->response, attr, 0)) != NULL)
+      attr->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
+    return (NULL);
+  }
 
   if ((job = cupsdAddJob(priority, printer->name)) == NULL)
   {
@@ -1917,7 +1909,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
   */
 
   httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
-                   con->servername, con->serverport, "/jobs/%d", job->id);
+                   con->clientname, con->clientport, "/jobs/%d", job->id);
   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
                job_uri);
 
@@ -2352,6 +2344,21 @@ add_printer(cupsd_client_t  *con,        /* I - Client connection */
     http_uri_status_t  uri_status;     /* URI separation status */
     char               old_device_uri[1024];
                                        /* Old device URI */
+    static const char * const uri_status_strings[] =
+    {
+      "URI too large.",
+      "Bad arguments to function.",
+      "Bad resource path.",
+      "Bad port number.",
+      "Bad hostname/address.",
+      "Bad username/password.",
+      "Bad URI scheme.",
+      "Bad URI.",
+      "OK",
+      "Missing URI scheme.",
+      "Unknown URI scheme",
+      "Missing resource path."
+    };
 
 
     need_restart_job = 1;
@@ -2363,12 +2370,14 @@ add_printer(cupsd_client_t  *con,       /* I - Client connection */
                                 host, sizeof(host), &port,
                                 resource, sizeof(resource));
 
+    cupsdLogMessage(CUPSD_LOG_DEBUG,
+                   "%s device-uri: %s", printer->name,
+                   uri_status_strings[uri_status - HTTP_URI_STATUS_OVERFLOW]);
+
     if (uri_status < HTTP_URI_OK)
     {
       send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\"."),
                      attr->values[0].string.text);
-      cupsdLogMessage(CUPSD_LOG_DEBUG,
-                      "add_printer: httpSeparateURI returned %d", uri_status);
       return;
     }
 
@@ -2387,7 +2396,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
        send_ipp_status(con, IPP_NOT_POSSIBLE,
                        _("File device URIs have been disabled. "
                          "To enable, see the FileDevice directive in "
-                         "\"%s/cupsd.conf\"."),
+                         "\"%s/cups-files.conf\"."),
                        ServerRoot);
        return;
       }
@@ -2704,1070 +2713,182 @@ add_printer(cupsd_client_t  *con,    /* I - Client connection */
                printer->name);
       unlink(dstfile);
     }
-    else
-    {
-     /*
-      * PPD model file...
-      */
-
-      snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
-               printer->name);
-      unlink(dstfile);
-
-      snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
-               printer->name);
-
-      if (copy_model(con, attr->values[0].string.text, dstfile))
-      {
-        send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file."));
-       return;
-      }
-
-      cupsdLogMessage(CUPSD_LOG_DEBUG,
-                     "Copied PPD file successfully");
-      chmod(dstfile, 0644);
-    }
-  }
-
-  if (changed_driver)
-  {
-   /*
-    * If we changed the PPD/interface script, then remove the printer's cache
-    * file and clear the printer-state-reasons...
-    */
-
-    char cache_name[1024];             /* Cache filename for printer attrs */
-
-    snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir,
-             printer->name);
-    unlink(cache_name);
-
-    cupsdSetPrinterReasons(printer, "none");
-
-#ifdef __APPLE__
-   /*
-    * (Re)register color profiles...
-    */
-
-    if (!RunUser)
-    {
-      apple_unregister_profiles(printer);
-      apple_register_profiles(printer);
-    }
-#endif /* __APPLE__ */
-  }
-
- /*
-  * If we set the device URI but not the port monitor, check which port
-  * monitor to use by default...
-  */
-
-  if (set_device_uri && !set_port_monitor)
-  {
-    ppd_file_t *ppd;                   /* PPD file */
-    ppd_attr_t *ppdattr;               /* cupsPortMonitor attribute */
-
-
-    httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme,
-                    sizeof(scheme), username, sizeof(username), host,
-                   sizeof(host), &port, resource, sizeof(resource));
-
-    snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot,
-            printer->name);
-    if ((ppd = _ppdOpenFile(srcfile, _PPD_LOCALIZATION_NONE)) != NULL)
-    {
-      for (ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
-          ppdattr;
-          ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
-        if (!strcmp(scheme, ppdattr->spec))
-       {
-         cupsdLogMessage(CUPSD_LOG_INFO,
-                         "Setting %s port-monitor to \"%s\" (was \"%s\".)",
-                         printer->name, ppdattr->value,
-                         printer->port_monitor ? printer->port_monitor
-                                               : "none");
-
-         if (strcmp(ppdattr->value, "none"))
-           cupsdSetString(&printer->port_monitor, ppdattr->value);
-         else
-           cupsdClearString(&printer->port_monitor);
-
-         break;
-       }
-
-      ppdClose(ppd);
-    }
-  }
-
- /*
-  * Update the printer attributes and return...
-  */
-
-  cupsdSetPrinterAttrs(printer);
-  cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
-
-  if (need_restart_job && printer->job)
-  {
-   /*
-    * Restart the current job...
-    */
-
-    cupsdSetJobState(printer->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
-                     "Job restarted because the printer was modified.");
-  }
-
-  cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
-
-  if (modify)
-  {
-    cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
-                  printer, NULL, "Printer \"%s\" modified by \"%s\".",
-                 printer->name, get_username(con));
-
-    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" modified by \"%s\".",
-                    printer->name, get_username(con));
-  }
-  else
-  {
-    cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
-                  printer, NULL, "New printer \"%s\" added by \"%s\".",
-                 printer->name, get_username(con));
-
-    cupsdLogMessage(CUPSD_LOG_INFO, "New printer \"%s\" added by \"%s\".",
-                    printer->name, get_username(con));
-  }
-
-  con->response->request.status.status_code = IPP_OK;
-}
-
-
-/*
- * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
- *                                 based upon the printer state...
- */
-
-static void
-add_printer_state_reasons(
-    cupsd_client_t  *con,              /* I - Client connection */
-    cupsd_printer_t *p)                        /* I - Printer info */
-{
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "add_printer_state_reasons(%p[%d], %p[%s])",
-                  con, con->http.fd, p, p->name);
-
-  if (p->num_reasons == 0)
-    ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                 "printer-state-reasons", NULL, "none");
-  else
-    ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
-                  "printer-state-reasons", p->num_reasons, NULL,
-                 (const char * const *)p->reasons);
-}
-
-
-/*
- * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
- *                            the specified printer or class.
- */
-
-static void
-add_queued_job_count(
-    cupsd_client_t  *con,              /* I - Client connection */
-    cupsd_printer_t *p)                        /* I - Printer or class */
-{
-  int          count;                  /* Number of jobs on destination */
-
-
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])",
-                  con, con->http.fd, p, p->name);
-
-  count = cupsdGetPrinterJobCount(p->name);
-
-  ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
-                "queued-job-count", count);
-}
-
-
-#ifdef __APPLE__
-/*
- * 'apple_init_profile()' - Initialize a color profile.
- */
-
-static void
-apple_init_profile(
-    ppd_file_t             *ppd,       /* I - PPD file */
-    cups_array_t          *languages,  /* I - Languages in the PPD file */
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-    CFMutableDictionaryRef profile,    /* I - Profile dictionary */
-#  else
-    CMDeviceProfileInfo    *profile,   /* I - Profile record */
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-    unsigned               id,         /* I - Profile ID */
-    const char             *name,      /* I - Profile name */
-    const char             *text,      /* I - Profile UI text */
-    const char             *iccfile)   /* I - ICC filename */
-{
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-  CFURLRef             url;            /* URL for profile filename */
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-  CFMutableDictionaryRef dict;         /* Dictionary for name */
-  char                 *language;      /* Current language */
-  ppd_attr_t           *attr;          /* Profile attribute */
-  CFStringRef          cflang,         /* Language string */
-                       cftext;         /* Localized text */
-
-
-  (void)id;
-
- /*
-  * Build the profile name dictionary...
-  */
-
-  dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                  &kCFTypeDictionaryKeyCallBacks,
-                                  &kCFTypeDictionaryValueCallBacks);
-  if (!dict)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize profile \"%s\".",
-                    iccfile);
-    return;
-  }
-
-  cftext = CFStringCreateWithCString(kCFAllocatorDefault, text,
-                                    kCFStringEncodingUTF8);
-
-  if (cftext)
-  {
-    CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
-    CFRelease(cftext);
-  }
-
-  if (languages)
-  {
-   /*
-    * Find localized names for the color profiles...
-    */
-
-    cupsArraySave(ppd->sorted_attrs);
-
-    for (language = (char *)cupsArrayFirst(languages);
-        language;
-        language = (char *)cupsArrayNext(languages))
-    {
-      if (iccfile)
-      {
-        if ((attr = _ppdLocalizedAttr(ppd, "cupsICCProfile", name,
-                                     language)) == NULL)
-         attr = _ppdLocalizedAttr(ppd, "APTiogaProfile", name, language);
-      }
-      else
-        attr = _ppdLocalizedAttr(ppd, "ColorModel", name, language);
-
-      if (attr && attr->text[0])
-      {
-       cflang = CFStringCreateWithCString(kCFAllocatorDefault, language,
-                                          kCFStringEncodingUTF8);
-       cftext = CFStringCreateWithCString(kCFAllocatorDefault, attr->text,
-                                          kCFStringEncodingUTF8);
-
-        if (cflang && cftext)
-         CFDictionarySetValue(dict, cflang, cftext);
-
-        if (cflang)
-         CFRelease(cflang);
-
-        if (cftext)
-         CFRelease(cftext);
-      }
-    }
-
-    cupsArrayRestore(ppd->sorted_attrs);
-  }
-
- /*
-  * Fill in the profile data...
-  */
-
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
- if (iccfile)
- {
-    url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
-                                                 (const UInt8 *)iccfile,
-                                                  strlen(iccfile), false);
-
-    if (url)
-    {
-      CFDictionarySetValue(profile, kColorSyncDeviceProfileURL, url);
-      CFRelease(url);
-    }
-  }
-
-  CFDictionarySetValue(profile, kColorSyncDeviceModeDescriptions, dict);
-  CFRelease(dict);
-
-#  else
-  profile->dataVersion        = cmDeviceProfileInfoVersion1;
-  profile->profileID          = id;
-  profile->profileLoc.locType = iccfile ? cmPathBasedProfile : cmNoProfileBase;
-  profile->profileName        = dict;
-
-  if (iccfile)
-    strlcpy(profile->profileLoc.u.pathLoc.path, iccfile,
-           sizeof(profile->profileLoc.u.pathLoc.path));
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-}
-
-
-/*
- * 'apple_register_profiles()' - Register color profiles for a printer.
- */
-
-static void
-apple_register_profiles(
-    cupsd_printer_t *p)                        /* I - Printer */
-{
-  int                  i;              /* Looping var */
-  char                 ppdfile[1024],  /* PPD filename */
-                       iccfile[1024],  /* ICC filename */
-                       selector[PPD_MAX_NAME];
-                                       /* Profile selection string */
-  ppd_file_t           *ppd;           /* PPD file */
-  ppd_attr_t           *attr,          /* Profile attributes */
-                       *profileid_attr,/* cupsProfileID attribute */
-                       *q1_attr,       /* ColorModel (or other) qualifier */
-                       *q2_attr,       /* MediaType (or other) qualifier */
-                       *q3_attr;       /* Resolution (or other) qualifier */
-  char                 q_keyword[PPD_MAX_NAME];
-                                       /* Qualifier keyword */
-  const char           *q1_choice,     /* ColorModel (or other) choice */
-                       *q2_choice,     /* MediaType (or other) choice */
-                       *q3_choice;     /* Resolution (or other) choice */
-  const char           *profile_key;   /* Profile keyword */
-  ppd_option_t         *cm_option;     /* Color model option */
-  ppd_choice_t         *cm_choice;     /* Color model choice */
-  int                  num_profiles;   /* Number of profiles */
-  OSStatus             error = 0;      /* Last error */
-  unsigned             device_id,      /* Printer device ID */
-                       profile_id = 0, /* Profile ID */
-                       default_profile_id = 0;
-                                       /* Default profile ID */
-  CFMutableDictionaryRef device_name;  /* Printer device name dictionary */
-  CFStringRef          printer_name;   /* Printer name string */
-  cups_array_t         *languages;     /* Languages array */
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-  CFMutableDictionaryRef profiles,     /* Dictionary of profiles */
-                       profile;        /* Current profile info dictionary */
-  CFStringRef          dict_key;       /* Key in factory profile dictionary */
-#  else
-  CMDeviceScope                scope =         /* Scope of the registration */
-                       {
-                         kCFPreferencesAnyUser,
-                         kCFPreferencesCurrentHost
-                       };
-  CMDeviceProfileArrayPtr profiles;    /* Profiles */
-  CMDeviceProfileInfo  *profile;       /* Current profile */
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-
-
- /*
-  * Make sure ColorSync is available...
-  */
-
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-  if (ColorSyncRegisterDevice == NULL)
-    return;
-
-#  else
-  if (CMRegisterColorDevice == NULL)
-    return;
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-
- /*
-  * Try opening the PPD file for this printer...
-  */
-
-  snprintf(ppdfile, sizeof(ppdfile), "%s/ppd/%s.ppd", ServerRoot, p->name);
-  if ((ppd = _ppdOpenFile(ppdfile, _PPD_LOCALIZATION_ICC_PROFILES)) == NULL)
-    return;
-
- /*
-  * See if we have any profiles...
-  */
-
-  if ((attr = ppdFindAttr(ppd, "APTiogaProfile", NULL)) != NULL)
-    profile_key = "APTiogaProfile";
-  else
-  {
-    attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
-    profile_key = "cupsICCProfile";
-  }
-
-  for (num_profiles = 0; attr; attr = ppdFindNextAttr(ppd, profile_key, NULL))
-    if (attr->spec[0] && attr->value && attr->value[0])
-    {
-      if (attr->value[0] != '/')
-       snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
-                attr->value);
-      else
-       strlcpy(iccfile, attr->value, sizeof(iccfile));
-
-      if (access(iccfile, 0))
-      {
-        cupsdLogMessage(CUPSD_LOG_ERROR,
-                        "%s: ICC Profile \"%s\" does not exist.", p->name,
-                        iccfile);
-       continue;
-      }
-
-      num_profiles ++;
-    }
-
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
- /*
-  * Create a dictionary for the factory profiles...
-  */
-
-  profiles = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                      &kCFTypeDictionaryKeyCallBacks,
-                                      &kCFTypeDictionaryValueCallBacks);
-  if (!profiles)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                   "Unable to allocate memory for factory profiles.");
-    ppdClose(ppd);
-    return;
-  }
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-
- /*
-  * If we have profiles, add them...
-  */
-
-  if (num_profiles > 0)
-  {
-    if (profile_key[0] == 'A')
-    {
-     /*
-      * For Tioga PPDs, get the default profile using the DefaultAPTiogaProfile
-      * attribute...
-      */
-
-      if ((attr = ppdFindAttr(ppd, "DefaultAPTiogaProfile", NULL)) != NULL &&
-         attr->value)
-        default_profile_id = atoi(attr->value);
-
-      q1_choice = q2_choice = q3_choice = NULL;
-    }
-    else
-    {
-     /*
-      * For CUPS PPDs, figure out the default profile selector values...
-      */
-
-      if ((attr = ppdFindAttr(ppd, "cupsICCQualifier1", NULL)) != NULL &&
-         attr->value && attr->value[0])
-      {
-       snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
-       q1_attr = ppdFindAttr(ppd, q_keyword, NULL);
-      }
-      else if ((q1_attr = ppdFindAttr(ppd, "DefaultColorModel", NULL)) == NULL)
-       q1_attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
-
-      if (q1_attr && q1_attr->value && q1_attr->value[0])
-       q1_choice = q1_attr->value;
-      else
-       q1_choice = "";
-
-      if ((attr = ppdFindAttr(ppd, "cupsICCQualifier2", NULL)) != NULL &&
-         attr->value && attr->value[0])
-      {
-       snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
-       q2_attr = ppdFindAttr(ppd, q_keyword, NULL);
-      }
-      else
-       q2_attr = ppdFindAttr(ppd, "DefaultMediaType", NULL);
-
-      if (q2_attr && q2_attr->value && q2_attr->value[0])
-       q2_choice = q2_attr->value;
-      else
-       q2_choice = NULL;
-
-      if ((attr = ppdFindAttr(ppd, "cupsICCQualifier3", NULL)) != NULL &&
-         attr->value && attr->value[0])
-      {
-       snprintf(q_keyword, sizeof(q_keyword), "Default%s", attr->value);
-       q3_attr = ppdFindAttr(ppd, q_keyword, NULL);
-      }
-      else
-       q3_attr = ppdFindAttr(ppd, "DefaultResolution", NULL);
-
-      if (q3_attr && q3_attr->value && q3_attr->value[0])
-       q3_choice = q3_attr->value;
-      else
-       q3_choice = NULL;
-    }
-
-#  ifndef HAVE_COLORSYNCREGISTERDEVICE
-   /*
-    * Build the array of profiles...
-    *
-    * Note: This calloc actually requests slightly more memory than needed.
-    */
-
-    if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to allocate memory for factory profiles.");
-      ppdClose(ppd);
-      return;
-    }
-
-    profiles->profileCount = num_profiles;
-    profile = profiles->profiles;
-#  endif /* !HAVE_COLORSYNCREGISTERDEVICE */
-
-   /*
-    * Loop through the profiles listed in the PPD...
-    */
-
-    languages = _ppdGetLanguages(ppd);
-
-    for (attr = ppdFindAttr(ppd, profile_key, NULL);
-        attr;
-        attr = ppdFindNextAttr(ppd, profile_key, NULL))
-      if (attr->spec[0] && attr->value && attr->value[0])
-      {
-       /*
-        * Add this profile...
-       */
-
-        if (attr->value[0] != '/')
-         snprintf(iccfile, sizeof(iccfile), "%s/profiles/%s", DataDir,
-                  attr->value);
-        else
-         strlcpy(iccfile, attr->value, sizeof(iccfile));
-
-        if (_cupsFileCheck(iccfile, _CUPS_FILE_CHECK_FILE, !RunUser,
-                          cupsdLogFCMessage, p))
-         continue;
-
-        if (profile_key[0] == 'c')
-       {
-         cupsArraySave(ppd->sorted_attrs);
-
-         if ((profileid_attr = ppdFindAttr(ppd, "cupsProfileID",
-                                           attr->spec)) != NULL &&
-             profileid_attr->value && isdigit(profileid_attr->value[0] & 255))
-           profile_id = (unsigned)strtoul(profileid_attr->value, NULL, 10);
-         else
-           profile_id = _ppdHashName(attr->spec);
-
-         cupsArrayRestore(ppd->sorted_attrs);
-        }
-       else
-         profile_id = atoi(attr->spec);
-
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-       profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                           &kCFTypeDictionaryKeyCallBacks,
-                                           &kCFTypeDictionaryValueCallBacks);
-       if (!profile)
-       {
-         cupsdLogMessage(CUPSD_LOG_ERROR,
-                         "Unable to allocate memory for color profile.");
-         CFRelease(profiles);
-         ppdClose(ppd);
-         return;
-       }
-
-       apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
-                          attr->text[0] ? attr->text : attr->spec, iccfile);
-
-       dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
-                                           CFSTR("%u"), profile_id);
-       if (dict_key)
-       {
-         CFDictionarySetValue(profiles, dict_key, profile);
-         CFRelease(dict_key);
-       }
-
-       CFRelease(profile);
-
-#  else
-        apple_init_profile(ppd, languages, profile, profile_id, attr->spec,
-                          attr->text[0] ? attr->text : attr->spec, iccfile);
-
-       profile ++;
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-
-       /*
-        * See if this is the default profile...
-       */
-
-        if (!default_profile_id && q1_choice && q2_choice && q3_choice)
-       {
-         snprintf(selector, sizeof(selector), "%s.%s.%s", q1_choice, q2_choice,
-                  q3_choice);
-         if (!strcmp(selector, attr->spec))
-           default_profile_id = profile_id;
-       }
-
-        if (!default_profile_id && q1_choice && q2_choice)
-       {
-         snprintf(selector, sizeof(selector), "%s.%s.", q1_choice, q2_choice);
-         if (!strcmp(selector, attr->spec))
-           default_profile_id = profile_id;
-       }
-
-        if (!default_profile_id && q1_choice && q3_choice)
-       {
-         snprintf(selector, sizeof(selector), "%s..%s", q1_choice, q3_choice);
-         if (!strcmp(selector, attr->spec))
-           default_profile_id = profile_id;
-       }
-
-        if (!default_profile_id && q1_choice)
-       {
-         snprintf(selector, sizeof(selector), "%s..", q1_choice);
-         if (!strcmp(selector, attr->spec))
-           default_profile_id = profile_id;
-       }
-
-        if (!default_profile_id && q2_choice && q3_choice)
-       {
-         snprintf(selector, sizeof(selector), ".%s.%s", q2_choice, q3_choice);
-         if (!strcmp(selector, attr->spec))
-           default_profile_id = profile_id;
-       }
-
-        if (!default_profile_id && q2_choice)
-       {
-         snprintf(selector, sizeof(selector), ".%s.", q2_choice);
-         if (!strcmp(selector, attr->spec))
-           default_profile_id = profile_id;
-       }
-
-        if (!default_profile_id && q3_choice)
-       {
-         snprintf(selector, sizeof(selector), "..%s", q3_choice);
-         if (!strcmp(selector, attr->spec))
-           default_profile_id = profile_id;
-       }
-      }
-
-    _ppdFreeLanguages(languages);
-  }
-  else if ((cm_option = ppdFindOption(ppd, "ColorModel")) != NULL)
-  {
-   /*
-    * Extract profiles from ColorModel option...
-    */
-
-    const char *profile_name;          /* Name of generic profile */
-
-
-    num_profiles = cm_option->num_choices;
-
-#  ifndef HAVE_COLORSYNCREGISTERDEVICE
-   /*
-    * Create an array for the factory profiles...
-    */
-
-    if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to allocate memory for factory profiles.");
-      ppdClose(ppd);
-      return;
-    }
-
-    profiles->profileCount = num_profiles;
-    profile = profiles->profiles;
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-
-    for (i = cm_option->num_choices, cm_choice = cm_option->choices;
-         i > 0;
-        i --, cm_choice ++)
-    {
-      if (!strcmp(cm_choice->choice, "Gray") ||
-          !strcmp(cm_choice->choice, "Black"))
-        profile_name = "Gray";
-      else if (!strcmp(cm_choice->choice, "RGB") ||
-               !strcmp(cm_choice->choice, "CMY"))
-        profile_name = "RGB";
-      else if (!strcmp(cm_choice->choice, "CMYK") ||
-               !strcmp(cm_choice->choice, "KCMY"))
-        profile_name = "CMYK";
-      else
-        profile_name = "DeviceN";
-
-      snprintf(selector, sizeof(selector), "%s..", profile_name);
-      profile_id = _ppdHashName(selector);
-
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-      profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                         &kCFTypeDictionaryKeyCallBacks,
-                                         &kCFTypeDictionaryValueCallBacks);
-      if (!profile)
-      {
-       cupsdLogMessage(CUPSD_LOG_ERROR,
-                       "Unable to allocate memory for color profile.");
-       CFRelease(profiles);
-       ppdClose(ppd);
-       return;
-      }
-
-      apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
-                         cm_choice->text, NULL);
-
-      dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
-                                          CFSTR("%u"), profile_id);
-      if (dict_key)
-      {
-       CFDictionarySetValue(profiles, dict_key, profile);
-       CFRelease(dict_key);
-      }
-
-      CFRelease(profile);
-
-#  else
-      apple_init_profile(ppd, NULL, profile, profile_id, cm_choice->choice,
-                         cm_choice->text, NULL);
-      profile ++;
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
-
-      if (cm_choice->marked)
-        default_profile_id = profile_id;
-    }
-  }
-  else
-  {
-   /*
-    * Use the default colorspace...
-    */
-
-    attr = ppdFindAttr(ppd, "DefaultColorSpace", NULL);
-
-    num_profiles = (attr && ppd->colorspace == PPD_CS_GRAY) ? 1 : 2;
-
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-   /*
-    * Add the grayscale profile first.  We always have a grayscale profile.
-    */
-
-    profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                       &kCFTypeDictionaryKeyCallBacks,
-                                       &kCFTypeDictionaryValueCallBacks);
-
-    if (!profile)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to allocate memory for color profile.");
-      CFRelease(profiles);
-      ppdClose(ppd);
-      return;
-    }
-
-    profile_id = _ppdHashName("Gray..");
-    apple_init_profile(ppd, NULL, profile, profile_id, "Gray", "Gray", NULL);
-
-    dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
-                                        profile_id);
-    if (dict_key)
-    {
-      CFDictionarySetValue(profiles, dict_key, profile);
-      CFRelease(dict_key);
-    }
-
-    CFRelease(profile);
-
-   /*
-    * Then add the RGB/CMYK/DeviceN color profile...
-    */
-
-    profile = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                       &kCFTypeDictionaryKeyCallBacks,
-                                       &kCFTypeDictionaryValueCallBacks);
-
-    if (!profile)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to allocate memory for color profile.");
-      CFRelease(profiles);
-      ppdClose(ppd);
-      return;
-    }
-
-    switch (ppd->colorspace)
-    {
-      default :
-      case PPD_CS_RGB :
-      case PPD_CS_CMY :
-          profile_id = _ppdHashName("RGB..");
-          apple_init_profile(ppd, NULL, profile, profile_id, "RGB", "RGB",
-                            NULL);
-          break;
-
-      case PPD_CS_RGBK :
-      case PPD_CS_CMYK :
-          profile_id = _ppdHashName("CMYK..");
-          apple_init_profile(ppd, NULL, profile, profile_id, "CMYK", "CMYK",
-                            NULL);
-          break;
-
-      case PPD_CS_GRAY :
-          if (attr)
-            break;
-
-      case PPD_CS_N :
-          profile_id = _ppdHashName("DeviceN..");
-          apple_init_profile(ppd, NULL, profile, profile_id, "DeviceN",
-                            "DeviceN", NULL);
-          break;
-    }
-
-    if (CFDictionaryGetCount(profile) > 0)
-    {
-      dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
-                                          CFSTR("%u"), profile_id);
-      if (dict_key)
-      {
-        CFDictionarySetValue(profiles, dict_key, profile);
-        CFRelease(dict_key);
-      }
-    }
-
-    CFRelease(profile);
-
-#  else
-   /*
-    * Create an array for the factory profiles...
-    */
-
-    if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
-    {
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to allocate memory for factory profiles.");
-      ppdClose(ppd);
-      return;
-    }
-
-    profiles->profileCount = num_profiles;
-
-   /*
-    * Add the grayscale profile first.  We always have a grayscale profile.
-    */
-
-    profile_id = _ppdHashName("Gray..");
-    apple_init_profile(ppd, NULL, profiles->profiles, profile_id, "Gray",
-                       "Gray", NULL);
+    else
+    {
+     /*
+      * PPD model file...
+      */
 
-   /*
-    * Then add the RGB/CMYK/DeviceN color profile...
-    */
+      snprintf(dstfile, sizeof(dstfile), "%s/interfaces/%s", ServerRoot,
+               printer->name);
+      unlink(dstfile);
 
-    switch (ppd->colorspace)
-    {
-      default :
-      case PPD_CS_RGB :
-      case PPD_CS_CMY :
-          profile_id = _ppdHashName("RGB..");
-          apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
-                            "RGB", "RGB", NULL);
-          break;
-      case PPD_CS_RGBK :
-      case PPD_CS_CMYK :
-          profile_id = _ppdHashName("CMYK..");
-          apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
-                            "CMYK", "CMYK", NULL);
-          break;
+      snprintf(dstfile, sizeof(dstfile), "%s/ppd/%s.ppd", ServerRoot,
+               printer->name);
 
-      case PPD_CS_GRAY :
-          if (attr)
-           break;
+      if (copy_model(con, attr->values[0].string.text, dstfile))
+      {
+        send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file."));
+       return;
+      }
 
-      case PPD_CS_N :
-          profile_id = _ppdHashName("DeviceN..");
-          apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
-                            "DeviceN", "DeviceN", NULL);
-          break;
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                     "Copied PPD file successfully");
+      chmod(dstfile, 0644);
     }
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
   }
 
-  if (num_profiles > 0)
+  if (changed_driver)
   {
    /*
-    * Make sure we have a default profile ID...
+    * If we changed the PPD/interface script, then remove the printer's cache
+    * file and clear the printer-state-reasons...
     */
 
-    if (!default_profile_id)
-      default_profile_id = profile_id; /* Last profile */
+    char cache_name[1024];             /* Cache filename for printer attrs */
 
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-    dict_key = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"),
-                                        default_profile_id);
-    if (dict_key)
-    {
-      CFDictionarySetValue(profiles, kColorSyncDeviceDefaultProfileID,
-                           dict_key);
-      CFRelease(dict_key);
-    }
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
+    snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir,
+             printer->name);
+    unlink(cache_name);
+
+    cupsdSetPrinterReasons(printer, "none");
 
    /*
-    * Get the device ID hash and pathelogical name dictionary.
+    * (Re)register color profiles...
     */
 
-    cupsdLogMessage(CUPSD_LOG_INFO, "Registering ICC color profiles for \"%s\"",
-                   p->name);
+    cupsdRegisterColor(printer);
+  }
 
-    device_id    = _ppdHashName(p->name);
-    device_name  = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
-                                            &kCFTypeDictionaryKeyCallBacks,
-                                            &kCFTypeDictionaryValueCallBacks);
-    printer_name = CFStringCreateWithCString(kCFAllocatorDefault,
-                                             p->name, kCFStringEncodingUTF8);
+ /*
+  * If we set the device URI but not the port monitor, check which port
+  * monitor to use by default...
+  */
 
-    if (device_name && printer_name)
-    {
-      CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
+  if (set_device_uri && !set_port_monitor)
+  {
+    ppd_file_t *ppd;                   /* PPD file */
+    ppd_attr_t *ppdattr;               /* cupsPortMonitor attribute */
 
-     /*
-      * Register the device with ColorSync...
-      */
 
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-      CFTypeRef                deviceDictKeys[] =
-      {                                        /* Device keys */
-        kColorSyncDeviceDescriptions,
-       kColorSyncFactoryProfiles,
-       kColorSyncDeviceUserScope,
-       kColorSyncDeviceHostScope
-      };
-      CFTypeRef        deviceDictVals[] =
-      {                                        /* Device values */
-        device_name,
-       profiles,
-       kCFPreferencesAnyUser,
-       kCFPreferencesCurrentHost
-      };
-      CFDictionaryRef  deviceDict;     /* Device dictionary */
-      CFUUIDRef                deviceUUID;     /* Device UUID */
-
-      deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
-                                     (const void **)deviceDictKeys,
-                                     (const void **)deviceDictVals,
-                                     sizeof(deviceDictKeys) /
-                                         sizeof(deviceDictKeys[0]),
-                                     &kCFTypeDictionaryKeyCallBacks,
-                                     &kCFTypeDictionaryValueCallBacks);
-      deviceUUID = ColorSyncCreateUUIDFromUInt32(device_id);
-
-      if (!deviceDict || !deviceUUID ||
-         !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
-                                  deviceDict))
-       error = 1001;
-
-      if (deviceUUID)
-        CFRelease(deviceUUID);
-
-      if (deviceDict)
-        CFRelease(deviceDict);
+    httpSeparateURI(HTTP_URI_CODING_ALL, printer->device_uri, scheme,
+                    sizeof(scheme), username, sizeof(username), host,
+                   sizeof(host), &port, resource, sizeof(resource));
 
-#  else
-      error = CMRegisterColorDevice(cmPrinterDeviceClass, device_id,
-                                    device_name, &scope);
+    snprintf(srcfile, sizeof(srcfile), "%s/ppd/%s.ppd", ServerRoot,
+            printer->name);
+    if ((ppd = _ppdOpenFile(srcfile, _PPD_LOCALIZATION_NONE)) != NULL)
+    {
+      for (ppdattr = ppdFindAttr(ppd, "cupsPortMonitor", NULL);
+          ppdattr;
+          ppdattr = ppdFindNextAttr(ppd, "cupsPortMonitor", NULL))
+        if (!strcmp(scheme, ppdattr->spec))
+       {
+         cupsdLogMessage(CUPSD_LOG_INFO,
+                         "Setting %s port-monitor to \"%s\" (was \"%s\".)",
+                         printer->name, ppdattr->value,
+                         printer->port_monitor ? printer->port_monitor
+                                               : "none");
 
-     /*
-      * Register the profiles...
-      */
+         if (strcmp(ppdattr->value, "none"))
+           cupsdSetString(&printer->port_monitor, ppdattr->value);
+         else
+           cupsdClearString(&printer->port_monitor);
+
+         break;
+       }
 
-      if (error == noErr)
-       error = CMSetDeviceFactoryProfiles(cmPrinterDeviceClass, device_id,
-                                          default_profile_id, profiles);
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
+      ppdClose(ppd);
     }
-    else
-      error = 1000;
+  }
 
  /*
-    * Clean up...
-    */
+ /*
+  * Update the printer attributes and return...
+  */
 
-    if (error != noErr)
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                     "Unable to register ICC color profiles for \"%s\": %d",
-                     p->name, (int)error);
+  cupsdSetPrinterAttrs(printer);
+  cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
 
-    if (printer_name)
-      CFRelease(printer_name);
+  if (need_restart_job && printer->job)
+  {
+   /*
+    * Restart the current job...
+    */
 
-    if (device_name)
-      CFRelease(device_name);
+    cupsdSetJobState(printer->job, IPP_JOB_PENDING, CUPSD_JOB_FORCE,
+                     "Job restarted because the printer was modified.");
   }
 
- /*
-  * Free any memory we used...
-  */
+  cupsdMarkDirty(CUPSD_DIRTY_PRINTCAP);
 
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-  CFRelease(profiles);
+  if (modify)
+  {
+    cupsdAddEvent(CUPSD_EVENT_PRINTER_MODIFIED,
+                  printer, NULL, "Printer \"%s\" modified by \"%s\".",
+                 printer->name, get_username(con));
 
-#  else
-  if (num_profiles > 0)
+    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" modified by \"%s\".",
+                    printer->name, get_username(con));
+  }
+  else
   {
-    for (profile = profiles->profiles;
-        num_profiles > 0;
-        profile ++, num_profiles --)
-      CFRelease(profile->profileName);
+    cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
+                  printer, NULL, "New printer \"%s\" added by \"%s\".",
+                 printer->name, get_username(con));
 
-    free(profiles);
+    cupsdLogMessage(CUPSD_LOG_INFO, "New printer \"%s\" added by \"%s\".",
+                    printer->name, get_username(con));
   }
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
 
-  ppdClose(ppd);
+  con->response->request.status.status_code = IPP_OK;
 }
 
 
 /*
- * 'apple_unregister_profiles()' - Remove color profiles for the specified
- *                                 printer.
+ * 'add_printer_state_reasons()' - Add the "printer-state-reasons" attribute
+ *                                 based upon the printer state...
  */
 
 static void
-apple_unregister_profiles(
-    cupsd_printer_t *p)                        /* I - Printer */
+add_printer_state_reasons(
+    cupsd_client_t  *con,              /* I - Client connection */
+    cupsd_printer_t *p)                        /* I - Printer info */
 {
- /*
-  * Make sure ColorSync is available...
-  */
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "add_printer_state_reasons(%p[%d], %p[%s])",
+                  con, con->http.fd, p, p->name);
 
-#  ifdef HAVE_COLORSYNCREGISTERDEVICE
-  if (ColorSyncUnregisterDevice != NULL)
-  {
-   /*
-    * Because we may have registered the printer profiles using a prior device
-    * ID-based UUID, remove both the old style UUID and current UUID for the
-    * printer.
-    */
+  if (p->num_reasons == 0)
+    ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                 "printer-state-reasons", NULL, "none");
+  else
+    ippAddStrings(con->response, IPP_TAG_PRINTER, IPP_TAG_KEYWORD,
+                  "printer-state-reasons", p->num_reasons, NULL,
+                 (const char * const *)p->reasons);
+}
 
-    CFUUIDRef deviceUUID;              /* Device UUID */
 
-    deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
-    if (deviceUUID)
-    {
-      ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
-      CFRelease(deviceUUID);
-    }
-  }
+/*
+ * 'add_queued_job_count()' - Add the "queued-job-count" attribute for
+ *                            the specified printer or class.
+ */
 
-#  else
-  if (CMUnregisterColorDevice != NULL)
-    CMUnregisterColorDevice(cmPrinterDeviceClass, _ppdHashName(p->name));
-#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
+static void
+add_queued_job_count(
+    cupsd_client_t  *con,              /* I - Client connection */
+    cupsd_printer_t *p)                        /* I - Printer or class */
+{
+  int          count;                  /* Number of jobs on destination */
+
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_queued_job_count(%p[%d], %p[%s])",
+                  con, con->http.fd, p, p->name);
+
+  count = cupsdGetPrinterJobCount(p->name);
+
+  ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
+                "queued-job-count", count);
 }
-#endif /* __APPLE__ */
 
 
 /*
@@ -4119,8 +3240,12 @@ cancel_all_jobs(cupsd_client_t  *con,    /* I - Client connection */
     {
       for (i = 0; i < job_ids->num_values; i ++)
       {
-       if (!cupsdFindJob(job_ids->values[i].integer))
+       if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL)
          break;
+
+        if (con->request->request.op.operation_id == IPP_CANCEL_MY_JOBS &&
+            _cups_strcasecmp(job->username, username))
+          break;
       }
 
       if (i < job_ids->num_values)
@@ -4176,6 +3301,10 @@ cancel_all_jobs(cupsd_client_t  *con,    /* I - Client connection */
        if ((job = cupsdFindJob(job_ids->values[i].integer)) == NULL ||
            _cups_strcasecmp(job->dest, printer->name))
          break;
+
+        if (con->request->request.op.operation_id == IPP_CANCEL_MY_JOBS &&
+            _cups_strcasecmp(job->username, username))
+          break;
       }
 
       if (i < job_ids->num_values)
@@ -4852,7 +3981,7 @@ close_job(cupsd_client_t  *con,           /* I - Client connection */
   */
 
   httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
-                   con->servername, con->serverport, "/jobs/%d", job->id);
+                   con->clientname, con->clientport, "/jobs/%d", job->id);
   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
                job_uri);
 
@@ -4903,7 +4032,11 @@ copy_attrs(ipp_t        *to,             /* I - Destination request */
          fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
       continue;
 
-    if (!strcmp(fromattr->name, "job-printer-uri"))
+    if (!strcmp(fromattr->name, "document-password") ||
+        !strcmp(fromattr->name, "job-authorization-uri") ||
+        !strcmp(fromattr->name, "job-password") ||
+        !strcmp(fromattr->name, "job-password-encryption") ||
+        !strcmp(fromattr->name, "job-printer-uri"))
       continue;
 
     if (exclude &&
@@ -5159,7 +4292,7 @@ copy_banner(cupsd_client_t *con,  /* I - Client connection */
              cupsFilePrintf(out, "%dx%d%s", attr->values[i].resolution.xres,
                      attr->values[i].resolution.yres,
                      attr->values[i].resolution.units == IPP_RES_PER_INCH ?
-                         "dpi" : "dpc");
+                         "dpi" : "dpcm");
              break;
 
          case IPP_TAG_URI :
@@ -5600,7 +4733,7 @@ copy_job_attrs(cupsd_client_t *con,       /* I - Client connection */
         (!ra || cupsArrayFind(ra, "job-more-info")))
     {
       httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "http",
-                       NULL, con->servername, con->serverport, "/jobs/%d",
+                       NULL, con->clientname, con->clientport, "/jobs/%d",
                       job->id);
       ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
                   "job-more-info", NULL, job_uri);
@@ -5621,7 +4754,7 @@ copy_job_attrs(cupsd_client_t *con,       /* I - Client connection */
   if (!ra || cupsArrayFind(ra, "job-printer-uri"))
   {
     httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
-                    con->servername, con->serverport,
+                    con->clientname, con->clientport,
                     (job->dtype & CUPS_PRINTER_CLASS) ? "/classes/%s" :
                                                         "/printers/%s",
                     job->dest);
@@ -5632,7 +4765,7 @@ copy_job_attrs(cupsd_client_t *con,       /* I - Client connection */
   if (!ra || cupsArrayFind(ra, "job-uri"))
   {
     httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
-                    con->servername, con->serverport, "/jobs/%d",
+                    con->clientname, con->clientport, "/jobs/%d",
                     job->id);
     ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
                 "job-uri", NULL, job_uri);
@@ -5695,8 +4828,8 @@ copy_printer_attrs(
         else
        {
          httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri,
-                          sizeof(printer_uri), "ipp", NULL, con->servername,
-                          con->serverport,
+                          sizeof(printer_uri), "ipp", NULL, con->clientname,
+                          con->clientport,
                           (p2->type & CUPS_PRINTER_CLASS) ?
                               "/classes/%s" : "/printers/%s", p2->name);
          member_uris->values[i].string.text = _cupsStrAlloc(printer_uri);
@@ -5719,7 +4852,7 @@ copy_printer_attrs(
     ippAddDate(con->response, IPP_TAG_PRINTER, "printer-current-time",
                ippTimeToDate(curtime));
 
-#ifdef HAVE_DNSSD
+#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
   if (!ra || cupsArrayFind(ra, "printer-dns-sd-name"))
   {
     if (printer->reg_name)
@@ -5729,7 +4862,7 @@ copy_printer_attrs(
       ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_NOVALUE,
                    "printer-dns-sd-name", 0);
   }
-#endif /* HAVE_DNSSD */
+#endif /* HAVE_DNSSD || HAVE_AVAHI */
 
   if (!ra || cupsArrayFind(ra, "printer-error-policy"))
     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_NAME,
@@ -5757,7 +4890,7 @@ copy_printer_attrs(
   if (!ra || cupsArrayFind(ra, "printer-icons"))
   {
     httpAssembleURIf(HTTP_URI_CODING_ALL, printer_icons, sizeof(printer_icons),
-                     "http", NULL, con->servername, con->serverport,
+                     "http", NULL, con->clientname, con->clientport,
                     "/icons/%s.png", printer->name);
     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons",
                  NULL, printer_icons);
@@ -5775,7 +4908,7 @@ copy_printer_attrs(
   if (!ra || cupsArrayFind(ra, "printer-more-info"))
   {
     httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
-                     "http", NULL, con->servername, con->serverport,
+                     "http", NULL, con->clientname, con->clientport,
                     (printer->type & CUPS_PRINTER_CLASS) ?
                         "/classes/%s" : "/printers/%s", printer->name);
     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
@@ -5831,7 +4964,7 @@ copy_printer_attrs(
   if (!ra || cupsArrayFind(ra, "printer-uri-supported"))
   {
     httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
-                     "ipp", NULL, con->servername, con->serverport,
+                     "ipp", NULL, con->clientname, con->clientport,
                     (printer->type & CUPS_PRINTER_CLASS) ?
                         "/classes/%s" : "/printers/%s", printer->name);
     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI,
@@ -5958,7 +5091,7 @@ copy_subscription_attrs(
   if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
   {
     httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
-                     "ipp", NULL, con->servername, con->serverport,
+                     "ipp", NULL, con->clientname, con->clientport,
                     "/printers/%s", sub->dest->name);
     ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
                 "notify-printer-uri", NULL, printer_uri);
@@ -5978,8 +5111,16 @@ static void
 create_job(cupsd_client_t  *con,       /* I - Client connection */
           ipp_attribute_t *uri)        /* I - Printer URI */
 {
+  int                  i;              /* Looping var */
   cupsd_printer_t      *printer;       /* Printer */
   cupsd_job_t          *job;           /* New job */
+  static const char * const forbidden_attrs[] =
+  {                                    /* List of forbidden attributes */
+    "compression",
+    "document-format",
+    "document-name",
+    "document-natural-language"
+  };
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
@@ -6000,6 +5141,29 @@ create_job(cupsd_client_t  *con, /* I - Client connection */
     return;
   }
 
+ /*
+  * Check for invalid Create-Job attributes and log a warning or error depending
+  * on whether cupsd is running in "strict conformance" mode...
+  */
+
+  for (i = 0;
+       i < (int)(sizeof(forbidden_attrs) / sizeof(forbidden_attrs[0]));
+       i ++)
+    if (ippFindAttribute(con->request, forbidden_attrs[i], IPP_TAG_ZERO))
+    {
+      if (StrictConformance)
+      {
+       send_ipp_status(con, IPP_BAD_REQUEST,
+                       _("The '%s' operation attribute cannot be supplied in a "
+                         "Create-Job request."), forbidden_attrs[i]);
+       return;
+      }
+
+      cupsdLogMessage(CUPSD_LOG_WARN,
+                      "Unexpected '%s' operation attribute in a Create-Job "
+                      "request.", forbidden_attrs[i]);
+    }
+
  /*
   * Create the job object...
   */
@@ -6025,251 +5189,34 @@ create_job(cupsd_client_t  *con,       /* I - Client connection */
 static cups_array_t *                  /* O - Array of attributes or NULL */
 create_requested_array(ipp_t *request) /* I - IPP request */
 {
-  int                  i;              /* Looping var */
-  ipp_attribute_t      *requested;     /* requested-attributes attribute */
   cups_array_t         *ra;            /* Requested attributes array */
-  char                 *value;         /* Current value */
 
 
  /*
-  * Get the requested-attributes attribute, and return NULL if we don't
-  * have one...
+  * Create the array for standard attributes...
   */
 
-  if ((requested = ippFindAttribute(request, "requested-attributes",
-                                    IPP_TAG_KEYWORD)) == NULL)
-    return (NULL);
+  ra = ippCreateRequestedArray(request);
 
  /*
-  * If the attribute contains a single "all" keyword, return NULL...
+  * Add CUPS defaults as needed...
   */
 
-  if (requested->num_values == 1 &&
-      !strcmp(requested->values[0].string.text, "all"))
-    return (NULL);
+  if (cupsArrayFind(ra, "printer-defaults"))
+  {
+   /*
+    * Include user-set defaults...
+    */
 
- /*
-  * Create an array using "strcmp" as the comparison function...
-  */
-
-  ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
-
-  for (i = 0; i < requested->num_values; i ++)
-  {
-    value = requested->values[i].string.text;
-
-    if (!strcmp(value, "job-template"))
-    {
-      /* Only includes the set of Job Template attributes supported by CUPS */
-      cupsArrayAdd(ra, "copies");
-      cupsArrayAdd(ra, "copies-default");
-      cupsArrayAdd(ra, "copies-supported");
-      cupsArrayAdd(ra, "finishings");
-      cupsArrayAdd(ra, "finishings-default");
-      cupsArrayAdd(ra, "finishings-supported");
-      cupsArrayAdd(ra, "job-hold-until");
-      cupsArrayAdd(ra, "job-hold-until-default");
-      cupsArrayAdd(ra, "job-hold-until-supported");
-      cupsArrayAdd(ra, "job-priority");
-      cupsArrayAdd(ra, "job-priority-default");
-      cupsArrayAdd(ra, "job-priority-supported");
-      cupsArrayAdd(ra, "job-sheets");
-      cupsArrayAdd(ra, "job-sheets-default");
-      cupsArrayAdd(ra, "job-sheets-supported");
-      cupsArrayAdd(ra, "media");
-      cupsArrayAdd(ra, "media-col");
-      cupsArrayAdd(ra, "media-col-default");
-      cupsArrayAdd(ra, "media-default");
-      cupsArrayAdd(ra, "media-supported");
-      cupsArrayAdd(ra, "multiple-document-handling");
-      cupsArrayAdd(ra, "multiple-document-handling-default");
-      cupsArrayAdd(ra, "multiple-document-handling-supported");
-      cupsArrayAdd(ra, "number-up");
-      cupsArrayAdd(ra, "number-up-default");
-      cupsArrayAdd(ra, "number-up-layout");
-      cupsArrayAdd(ra, "number-up-layout-default");
-      cupsArrayAdd(ra, "number-up-layout-supported");
-      cupsArrayAdd(ra, "number-up-supported");
-      cupsArrayAdd(ra, "orientation-requested");
-      cupsArrayAdd(ra, "orientation-requested-default");
-      cupsArrayAdd(ra, "orientation-requested-supported");
-      cupsArrayAdd(ra, "output-bin");
-      cupsArrayAdd(ra, "output-bin-default");
-      cupsArrayAdd(ra, "output-bin-supported");
-      cupsArrayAdd(ra, "page-delivery");
-      cupsArrayAdd(ra, "page-delivery-default");
-      cupsArrayAdd(ra, "page-delivery-supported");
-      cupsArrayAdd(ra, "page-order-received");
-      cupsArrayAdd(ra, "page-order-received-default");
-      cupsArrayAdd(ra, "page-order-received-supported");
-      cupsArrayAdd(ra, "page-ranges");
-      cupsArrayAdd(ra, "page-ranges-supported");
-      cupsArrayAdd(ra, "presentation-direction-number-up");
-      cupsArrayAdd(ra, "presentation-direction-number-up-default");
-      cupsArrayAdd(ra, "presentation-direction-number-up-supported");
-      cupsArrayAdd(ra, "print-color-mode");
-      cupsArrayAdd(ra, "print-color-mode-default");
-      cupsArrayAdd(ra, "print-color-mode-supported");
-      cupsArrayAdd(ra, "print-content-optimize");
-      cupsArrayAdd(ra, "print-content-optimize-default");
-      cupsArrayAdd(ra, "print-content-optimize-supported");
-      cupsArrayAdd(ra, "print-quality");
-      cupsArrayAdd(ra, "print-quality-default");
-      cupsArrayAdd(ra, "print-quality-supported");
-      cupsArrayAdd(ra, "printer-resolution");
-      cupsArrayAdd(ra, "printer-resolution-default");
-      cupsArrayAdd(ra, "printer-resolution-supported");
-      cupsArrayAdd(ra, "sheet-collate");
-      cupsArrayAdd(ra, "sheet-collate-default");
-      cupsArrayAdd(ra, "sheet-collate-supported");
-      cupsArrayAdd(ra, "sides");
-      cupsArrayAdd(ra, "sides-default");
-      cupsArrayAdd(ra, "sides-supported");
-    }
-    else if (!strcmp(value, "job-description"))
-    {
-      /* Only includes the set of Job Description attributes supported by CUPS */
-      cupsArrayAdd(ra, "date-time-at-completed");
-      cupsArrayAdd(ra, "date-time-at-creation");
-      cupsArrayAdd(ra, "date-time-at-processing");
-      cupsArrayAdd(ra, "job-detailed-status-message");
-      cupsArrayAdd(ra, "job-document-access-errors");
-      cupsArrayAdd(ra, "job-id");
-      cupsArrayAdd(ra, "job-impressions");
-      cupsArrayAdd(ra, "job-impressions-completed");
-      cupsArrayAdd(ra, "job-k-octets");
-      cupsArrayAdd(ra, "job-k-octets-processed");
-      cupsArrayAdd(ra, "job-mandatory-attributes");
-      cupsArrayAdd(ra, "job-media-progress");
-      cupsArrayAdd(ra, "job-media-sheets");
-      cupsArrayAdd(ra, "job-media-sheets-completed");
-      cupsArrayAdd(ra, "job-message-from-operator");
-      cupsArrayAdd(ra, "job-more-info");
-      cupsArrayAdd(ra, "job-name");
-      cupsArrayAdd(ra, "job-originating-user-name");
-      cupsArrayAdd(ra, "job-printer-up-time");
-      cupsArrayAdd(ra, "job-printer-uri");
-      cupsArrayAdd(ra, "job-state");
-      cupsArrayAdd(ra, "job-state-message");
-      cupsArrayAdd(ra, "job-state-reasons");
-      cupsArrayAdd(ra, "job-uri");
-      cupsArrayAdd(ra, "number-of-documents");
-      cupsArrayAdd(ra, "number-of-intervening-jobs");
-      cupsArrayAdd(ra, "output-device-assigned");
-      cupsArrayAdd(ra, "time-at-completed");
-      cupsArrayAdd(ra, "time-at-creation");
-      cupsArrayAdd(ra, "time-at-processing");
-    }
-    else if (!strcmp(value, "printer-description"))
-    {
-      /* Only includes the set of Printer Description attributes supported by CUPS */
-      cupsArrayAdd(ra, "charset-configured");
-      cupsArrayAdd(ra, "charset-supported");
-      cupsArrayAdd(ra, "color-supported");
-      cupsArrayAdd(ra, "compression-supported");
-      cupsArrayAdd(ra, "document-format-default");
-      cupsArrayAdd(ra, "document-format-supported");
-      cupsArrayAdd(ra, "generated-natural-language-supported");
-      cupsArrayAdd(ra, "ipp-versions-supported");
-      cupsArrayAdd(ra, "job-creation-attributes-supported");
-      cupsArrayAdd(ra, "job-ids-supported");
-      cupsArrayAdd(ra, "job-impressions-supported");
-      cupsArrayAdd(ra, "job-k-octets-supported");
-      cupsArrayAdd(ra, "job-media-sheets-supported");
-      cupsArrayAdd(ra, "job-settable-attributes-supported");
-      cupsArrayAdd(ra, "jpeg-k-octets-supported");
-      cupsArrayAdd(ra, "jpeg-x-dimension-supported");
-      cupsArrayAdd(ra, "jpeg-y-dimension-supported");
-      cupsArrayAdd(ra, "media-bottom-margin-supported");
-      cupsArrayAdd(ra, "media-col-supported");
-      cupsArrayAdd(ra, "media-key-supported");
-      cupsArrayAdd(ra, "media-left-margin-supported");
-      cupsArrayAdd(ra, "media-right-margin-supported");
-      cupsArrayAdd(ra, "media-size-supported");
-      cupsArrayAdd(ra, "media-source-supported");
-      cupsArrayAdd(ra, "media-top-margin-supported");
-      cupsArrayAdd(ra, "media-type-supported");
-      cupsArrayAdd(ra, "multiple-document-jobs-supported");
-      cupsArrayAdd(ra, "multiple-operation-time-out");
-      cupsArrayAdd(ra, "natural-language-configured");
-      cupsArrayAdd(ra, "notify-max-events-supported");
-      cupsArrayAdd(ra, "notify-schemes-supported");
-      cupsArrayAdd(ra, "operations-supported");
-      cupsArrayAdd(ra, "pages-per-minute");
-      cupsArrayAdd(ra, "pages-per-minute-color");
-      cupsArrayAdd(ra, "pdf-k-octets-supported");
-      cupsArrayAdd(ra, "pdl-override-supported");
-      cupsArrayAdd(ra, "printer-alert");
-      cupsArrayAdd(ra, "printer-alert-description");
-      cupsArrayAdd(ra, "printer-commands");
-      cupsArrayAdd(ra, "printer-current-time");
-      cupsArrayAdd(ra, "printer-dns-sd-name");
-      cupsArrayAdd(ra, "printer-info");
-      cupsArrayAdd(ra, "printer-is-accepting-jobs");
-      cupsArrayAdd(ra, "printer-is-shared");
-      cupsArrayAdd(ra, "printer-location");
-      cupsArrayAdd(ra, "printer-make-and-model");
-      cupsArrayAdd(ra, "printer-message-from-operator");
-      cupsArrayAdd(ra, "printer-more-info");
-      cupsArrayAdd(ra, "printer-more-info-manufacturer");
-      cupsArrayAdd(ra, "printer-name");
-      cupsArrayAdd(ra, "printer-settable-attributes-supported");
-      cupsArrayAdd(ra, "printer-state");
-      cupsArrayAdd(ra, "printer-state-change-date-time");
-      cupsArrayAdd(ra, "printer-state-change-time");
-      cupsArrayAdd(ra, "printer-state-message");
-      cupsArrayAdd(ra, "printer-state-reasons");
-      cupsArrayAdd(ra, "printer-type");
-      cupsArrayAdd(ra, "printer-up-time");
-      cupsArrayAdd(ra, "printer-uri-supported");
-      cupsArrayAdd(ra, "queued-job-count");
-      cupsArrayAdd(ra, "reference-uri-schemes-supported");
-      cupsArrayAdd(ra, "uri-authentication-supported");
-      cupsArrayAdd(ra, "uri-security-supported");
-      cupsArrayAdd(ra, "which-jobs-supported");
-    }
-    else if (!strcmp(value, "printer-defaults"))
-    {
-      char     *name;                  /* Option name */
-
-
-      for (name = (char *)cupsArrayFirst(CommonDefaults);
-           name;
-          name = (char *)cupsArrayNext(CommonDefaults))
+    char       *name;                  /* Option name */
+
+    cupsArrayRemove(ra, "printer-defaults");
+
+    for (name = (char *)cupsArrayFirst(CommonDefaults);
+        name;
+        name = (char *)cupsArrayNext(CommonDefaults))
+      if (!cupsArrayFind(ra, name))
         cupsArrayAdd(ra, name);
-    }
-    else if (!strcmp(value, "subscription-description"))
-    {
-      /* Only includes the set of Subscription Description attributes supported by CUPS */
-      cupsArrayAdd(ra, "notify-job-id");
-      cupsArrayAdd(ra, "notify-lease-expiration-time");
-      cupsArrayAdd(ra, "notify-printer-up-time");
-      cupsArrayAdd(ra, "notify-printer-uri");
-      cupsArrayAdd(ra, "notify-sequence-number");
-      cupsArrayAdd(ra, "notify-subscriber-user-name");
-      cupsArrayAdd(ra, "notify-subscription-id");
-    }
-    else if (!strcmp(value, "subscription-template"))
-    {
-      /* Only includes the set of Subscription Template attributes supported by CUPS */
-      cupsArrayAdd(ra, "notify-attributes");
-      cupsArrayAdd(ra, "notify-attributes-supported");
-      cupsArrayAdd(ra, "notify-charset");
-      cupsArrayAdd(ra, "notify-events");
-      cupsArrayAdd(ra, "notify-events-default");
-      cupsArrayAdd(ra, "notify-events-supported");
-      cupsArrayAdd(ra, "notify-lease-duration");
-      cupsArrayAdd(ra, "notify-lease-duration-default");
-      cupsArrayAdd(ra, "notify-lease-duration-supported");
-      cupsArrayAdd(ra, "notify-natural-language");
-      cupsArrayAdd(ra, "notify-pull-method");
-      cupsArrayAdd(ra, "notify-pull-method-supported");
-      cupsArrayAdd(ra, "notify-recipient-uri");
-      cupsArrayAdd(ra, "notify-time-interval");
-      cupsArrayAdd(ra, "notify-user-data");
-    }
-    else
-      cupsArrayAdd(ra, value);
   }
 
   return (ra);
@@ -6711,10 +5658,16 @@ delete_printer(cupsd_client_t  *con,    /* I - Client connection */
   snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
            printer->name);
   unlink(filename);
+  snprintf(filename, sizeof(filename), "%s/interfaces/%s.O", ServerRoot,
+           printer->name);
+  unlink(filename);
 
   snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
            printer->name);
   unlink(filename);
+  snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd.O", ServerRoot,
+           printer->name);
+  unlink(filename);
 
   snprintf(filename, sizeof(filename), "%s/%s.png", CacheDir, printer->name);
   unlink(filename);
@@ -6722,13 +5675,11 @@ delete_printer(cupsd_client_t  *con,    /* I - Client connection */
   snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name);
   unlink(filename);
 
-#ifdef __APPLE__
  /*
   * Unregister color profiles...
   */
 
-  apple_unregister_profiles(printer);
-#endif /* __APPLE__ */
+  cupsdUnregisterColor(printer);
 
   if (dtype & CUPS_PRINTER_CLASS)
   {
@@ -7275,7 +6226,7 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
   {
     job_comparison = -1;
     job_state      = IPP_JOB_STOPPED;
-    list           = Jobs;
+    list           = ActiveJobs;
   }
   else if (!strcmp(attr->values[0].string.text, "completed"))
   {
@@ -7388,18 +6339,7 @@ get_jobs(cupsd_client_t  *con,           /* I - Client connection */
   else
     username[0] = '\0';
 
-  if ((ra = create_requested_array(con->request)) == NULL &&
-      !ippFindAttribute(con->request, "requested-attributes", IPP_TAG_KEYWORD))
-  {
-   /*
-    * IPP conformance - Get-Jobs has a default requested-attributes value of
-    * "job-id" and "job-uri".
-    */
-
-    ra = cupsArrayNew((cups_array_func_t)strcmp, NULL);
-    cupsArrayAdd(ra, "job-id");
-    cupsArrayAdd(ra, "job-uri");
-  }
+  ra = create_requested_array(con->request);
 
  /*
   * OK, build a list of jobs for this printer...
@@ -9094,7 +8034,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
     * Grab format from client...
     */
 
-    if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super,
+    if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]", super,
                type) != 2)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
@@ -9111,7 +8051,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
     * Use default document format...
     */
 
-    if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
+    if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
                       _("Bad document-format \"%s\"."),
@@ -9125,8 +8065,8 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
     * Auto-type it!
     */
 
-    strcpy(super, "application");
-    strcpy(type, "octet-stream");
+    strlcpy(super, "application", sizeof(super));
+    strlcpy(type, "octet-stream", sizeof(type));
   }
 
   if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
@@ -9494,7 +8434,8 @@ reject_jobs(cupsd_client_t  *con, /* I - Client connection */
 
   if ((attr = ippFindAttribute(con->request, "printer-state-message",
                                IPP_TAG_TEXT)) == NULL)
-    strcpy(printer->state_message, "Rejecting Jobs");
+    strlcpy(printer->state_message, "Rejecting Jobs",
+            sizeof(printer->state_message));
   else
     strlcpy(printer->state_message, attr->values[0].string.text,
             sizeof(printer->state_message));
@@ -9713,6 +8654,7 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
 
     cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, cupsdFindDest(job->dest), job,
                   "Job job-hold-until value changed by user.");
+    ippSetString(job->attrs, &job->reasons, 0, "none");
   }
 
  /*
@@ -10040,7 +8982,7 @@ save_auth_info(
   fchown(cupsFileNumber(fp), 0, 0);
   fchmod(cupsFileNumber(fp), 0400);
 
-  cupsFilePuts(fp, "CUPSD-AUTH-V2\n");
+  cupsFilePuts(fp, "CUPSD-AUTH-V3\n");
 
   for (i = 0;
        i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
@@ -10058,9 +9000,15 @@ save_auth_info(
             i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
         i ++)
     {
-      httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text,
-                     strlen(auth_info->values[i].string.text));
-      cupsFilePutConf(fp, dest->auth_info_required[i], line);
+      if (strcmp(dest->auth_info_required[i], "negotiate"))
+      {
+       httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text,
+                      strlen(auth_info->values[i].string.text));
+       cupsFilePutConf(fp, dest->auth_info_required[i], line);
+      }
+      else
+       cupsFilePutConf(fp, dest->auth_info_required[i],
+                       auth_info->values[i].string.text);
 
       if (!strcmp(dest->auth_info_required[i], "username"))
         cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s",
@@ -10324,7 +9272,7 @@ send_document(cupsd_client_t  *con,      /* I - Client connection */
     * Grab format from client...
     */
 
-    if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
+    if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
                super, type) != 2)
     {
       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
@@ -10340,7 +9288,7 @@ send_document(cupsd_client_t  *con,      /* I - Client connection */
     * Use default document format...
     */
 
-    if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
+    if (sscanf(default_format, "%15[^/]/%255[^;]", super, type) != 2)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
                       _("Bad document-format-default \"%s\"."), default_format);
@@ -10353,8 +9301,8 @@ send_document(cupsd_client_t  *con,      /* I - Client connection */
     * No document format attribute?  Auto-type it!
     */
 
-    strcpy(super, "application");
-    strcpy(type, "octet-stream");
+    strlcpy(super, "application", sizeof(super));
+    strlcpy(type, "octet-stream", sizeof(type));
   }
 
   if (!strcmp(super, "application") && !strcmp(type, "octet-stream"))
@@ -10533,7 +9481,7 @@ send_document(cupsd_client_t  *con,      /* I - Client connection */
   */
 
   httpAssembleURIf(HTTP_URI_CODING_ALL, job_uri, sizeof(job_uri), "ipp", NULL,
-                   con->servername, con->serverport, "/jobs/%d", jobid);
+                   con->clientname, con->clientport, "/jobs/%d", jobid);
   ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI, "job-uri", NULL,
                job_uri);
 
@@ -10573,8 +9521,8 @@ send_http_error(
     uri = ippFindAttribute(con->request, "job-uri", IPP_TAG_URI);
 
   cupsdLogMessage(status == HTTP_FORBIDDEN ? CUPSD_LOG_ERROR : CUPSD_LOG_DEBUG,
-                  "Returning HTTP %s for %s (%s) from %s",
-                  httpStatus(status),
+                  "[Client %d] Returning HTTP %s for %s (%s) from %s",
+                  con->http.fd, httpStatus(status),
                  con->request ?
                      ippOpString(con->request->request.op.operation_id) :
                      "no operation-id",
@@ -11407,6 +10355,7 @@ set_printer_defaults(
           break;
 
       case IPP_TAG_NAME :
+      case IPP_TAG_TEXT :
       case IPP_TAG_KEYWORD :
       case IPP_TAG_URI :
           printer->num_options = cupsAddOption(name,
@@ -11453,7 +10402,7 @@ set_printer_defaults(
           sprintf(value, "%dx%d%s", attr->values[0].resolution.xres,
                  attr->values[0].resolution.yres,
                  attr->values[0].resolution.units == IPP_RES_PER_INCH ?
-                     "dpi" : "dpc");
+                     "dpi" : "dpcm");
           printer->num_options = cupsAddOption(name, value,
                                               printer->num_options,
                                               &(printer->options));
@@ -11599,7 +10548,7 @@ stop_printer(cupsd_client_t  *con,      /* I - Client connection */
 
   if ((attr = ippFindAttribute(con->request, "printer-state-message",
                                IPP_TAG_TEXT)) == NULL)
-    strcpy(printer->state_message, "Paused");
+    strlcpy(printer->state_message, "Paused", sizeof(printer->state_message));
   else
   {
     strlcpy(printer->state_message, attr->values[0].string.text,
@@ -11796,7 +10745,8 @@ validate_job(cupsd_client_t  *con,      /* I - Client connection */
   http_status_t                status;         /* Policy status */
   ipp_attribute_t      *attr,          /* Current attribute */
                        *auth_info;     /* auth-info attribute */
-  ipp_attribute_t      *format;        /* Document-format attribute */
+  ipp_attribute_t      *format,        /* Document-format attribute */
+                       *name;          /* Job-name attribute */
   cups_ptype_t         dtype;          /* Destination type (printer/class) */
   char                 super[MIME_MAX_SUPER],
                                        /* Supertype of file */
@@ -11823,7 +10773,7 @@ validate_job(cupsd_client_t  *con,      /* I - Client connection */
       )
     {
       send_ipp_status(con, IPP_ATTRIBUTES,
-                      _("Unsupported compression \"%s\"."),
+                      _("Unsupported 'compression' value \"%s\"."),
                      attr->values[0].string.text);
       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
                   "compression", NULL, attr->values[0].string.text);
@@ -11838,10 +10788,11 @@ validate_job(cupsd_client_t  *con,    /* I - Client connection */
   if ((format = ippFindAttribute(con->request, "document-format",
                                  IPP_TAG_MIMETYPE)) != NULL)
   {
-    if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
+    if (sscanf(format->values[0].string.text, "%15[^/]/%255[^;]",
                super, type) != 2)
     {
-      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
+      send_ipp_status(con, IPP_BAD_REQUEST,
+                      _("Bad 'document-format' value \"%s\"."),
                      format->values[0].string.text);
       return;
     }
@@ -11852,7 +10803,7 @@ validate_job(cupsd_client_t  *con,      /* I - Client connection */
       cupsdLogMessage(CUPSD_LOG_INFO,
                       "Hint: Do you have the raw file printing rules enabled?");
       send_ipp_status(con, IPP_DOCUMENT_FORMAT,
-                      _("Unsupported document-format \"%s\"."),
+                      _("Unsupported 'document-format' value \"%s\"."),
                      format->values[0].string.text);
       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
                    "document-format", NULL, format->values[0].string.text);
@@ -11860,6 +10811,86 @@ validate_job(cupsd_client_t  *con,     /* I - Client connection */
     }
   }
 
+ /*
+  * Is the job-name valid?
+  */
+
+  if ((name = ippFindAttribute(con->request, "job-name", IPP_TAG_ZERO)) != NULL)
+  {
+    int bad_name = 0;                  /* Is the job-name value bad? */
+
+    if ((name->value_tag != IPP_TAG_NAME && name->value_tag != IPP_TAG_NAMELANG) ||
+        name->num_values != 1)
+    {
+      bad_name = 1;
+    }
+    else
+    {
+     /*
+      * Validate that job-name conforms to RFC 5198 (Network Unicode) and
+      * IPP Everywhere requirements for "name" values...
+      */
+
+      const unsigned char *nameptr;    /* Pointer into "job-name" attribute */
+
+      for (nameptr = (unsigned char *)name->values[0].string.text;
+           *nameptr;
+           nameptr ++)
+      {
+        if (*nameptr < ' ' && *nameptr != '\t')
+          break;
+        else if (*nameptr == 0x7f)
+          break;
+        else if ((*nameptr & 0xe0) == 0xc0)
+        {
+          if ((nameptr[1] & 0xc0) != 0x80)
+            break;
+
+          nameptr ++;
+        }
+        else if ((*nameptr & 0xf0) == 0xe0)
+        {
+          if ((nameptr[1] & 0xc0) != 0x80 ||
+              (nameptr[2] & 0xc0) != 0x80)
+           break;
+
+         nameptr += 2;
+       }
+        else if ((*nameptr & 0xf8) == 0xf0)
+        {
+          if ((nameptr[1] & 0xc0) != 0x80 ||
+             (nameptr[2] & 0xc0) != 0x80 ||
+             (nameptr[3] & 0xc0) != 0x80)
+           break;
+
+         nameptr += 3;
+       }
+        else if (*nameptr & 0x80)
+          break;
+      }
+
+      if (*nameptr)
+        bad_name = 1;
+    }
+
+    if (bad_name)
+    {
+      if (StrictConformance)
+      {
+       send_ipp_status(con, IPP_ATTRIBUTES,
+                       _("Unsupported 'job-name' value."));
+       ippCopyAttribute(con->response, name, 0);
+       return;
+      }
+      else
+      {
+        cupsdLogMessage(CUPSD_LOG_WARN,
+                        "Unsupported 'job-name' value, deleting from request.");
+        ippDeleteAttribute(con->request, name);
+      }
+    }
+  }
+
  /*
   * Is the destination valid?
   */
@@ -11985,5 +11016,5 @@ validate_user(cupsd_job_t    *job,      /* I - Job */
 
 
 /*
- * End of "$Id: ipp.c 7944 2008-09-16 22:32:42Z mike $".
+ * End of "$Id: ipp.c 11500 2014-01-06 22:21:15Z msweet $".
  */