]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/ipp.c
Merge changes from CUPS 1.5svn-r9641
[thirdparty/cups.git] / scheduler / ipp.c
index 897c2f46d3e4e1ac4ef65358c5f5919cec4b9ee8..ef466a1c827b616568990ee3d76593eca0cd2f99 100644 (file)
@@ -3,7 +3,7 @@
  *
  *   IPP routines for the CUPS scheduler.
  *
- *   Copyright 2007-2010 by Apple Inc.
+ *   Copyright 2007-2011 by Apple Inc.
  *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  *   This file contains Kerberos support code, copyright 2006 by
 
 #ifdef __APPLE__
 #  include <ApplicationServices/ApplicationServices.h>
+#  ifdef HAVE_COLORSYNCREGISTERDEVICE
+extern CFUUIDRef ColorSyncCreateUUIDFromUInt32(unsigned id);
+#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
 #  include <CoreFoundation/CoreFoundation.h>
 #  ifdef HAVE_MEMBERSHIP_H
 #    include <membership.h>
@@ -149,9 +152,13 @@ static void        add_printer_state_reasons(cupsd_client_t *con,
 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,
-                                  CMDeviceProfileInfo *profile, unsigned id,
-                                  const char *name, const char *text,
-                                  const char *iccfile);
+#  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__ */
@@ -167,7 +174,8 @@ static ipp_attribute_t      *copy_attribute(ipp_t *to, ipp_attribute_t *attr,
                                        int quickcopy);
 static void    close_job(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    copy_attrs(ipp_t *to, ipp_t *from, cups_array_t *ra,
-                          ipp_tag_t group, int quickcopy);
+                          ipp_tag_t group, int quickcopy,
+                          cups_array_t *exclude);
 static int     copy_banner(cupsd_client_t *con, cupsd_job_t *job,
                            const char *name);
 static int     copy_file(const char *from, const char *to);
@@ -175,13 +183,14 @@ static int        copy_model(cupsd_client_t *con, const char *from,
                           const char *to);
 static void    copy_job_attrs(cupsd_client_t *con,
                               cupsd_job_t *job,
-                              cups_array_t *ra);
+                              cups_array_t *ra, cups_array_t *exclude);
 static void    copy_printer_attrs(cupsd_client_t *con,
                                   cupsd_printer_t *printer,
                                   cups_array_t *ra);
 static void    copy_subscription_attrs(cupsd_client_t *con,
                                        cupsd_subscription_t *sub,
-                                       cups_array_t *ra);
+                                       cups_array_t *ra,
+                                       cups_array_t *exclude);
 static void    create_job(cupsd_client_t *con, ipp_attribute_t *uri);
 static cups_array_t *create_requested_array(ipp_t *request);
 static void    create_subscription(cupsd_client_t *con, ipp_attribute_t *uri);
@@ -297,7 +306,7 @@ cupsdProcessIPPRequest(
                  con->request->request.any.version[1]);
 
     send_ipp_status(con, IPP_VERSION_NOT_SUPPORTED,
-                    _("Bad request version number %d.%d"),
+                    _("Bad request version number %d.%d."),
                    con->request->request.any.version[0],
                    con->request->request.any.version[1]);
   }
@@ -312,7 +321,7 @@ cupsdProcessIPPRequest(
                  IPP_BAD_REQUEST, con->http.hostname,
                   con->request->request.any.request_id);
 
-    send_ipp_status(con, IPP_BAD_REQUEST, _("Bad request ID %d"),
+    send_ipp_status(con, IPP_BAD_REQUEST, _("Bad request ID %d."),
                    con->request->request.any.request_id);
   }
   else if (!con->request->attrs)
@@ -321,7 +330,7 @@ cupsdProcessIPPRequest(
                   "%04X %s No attributes in request",
                  IPP_BAD_REQUEST, con->http.hostname);
 
-    send_ipp_status(con, IPP_BAD_REQUEST, _("No attributes in request"));
+    send_ipp_status(con, IPP_BAD_REQUEST, _("No attributes in request."));
   }
   else
   {
@@ -344,7 +353,7 @@ cupsdProcessIPPRequest(
                      IPP_BAD_REQUEST, con->http.hostname);
 
        send_ipp_status(con, IPP_BAD_REQUEST,
-                       _("Attribute groups are out of order (%x < %x)"),
+                       _("Attribute groups are out of order (%x < %x)."),
                        attr->group_tag, group);
        break;
       }
@@ -421,7 +430,7 @@ cupsdProcessIPPRequest(
                      IPP_CHARSET, con->http.hostname,
                      charset->values[0].string.text);
        send_ipp_status(con, IPP_BAD_REQUEST,
-                       _("Unsupported character set \"%s\""),
+                       _("Unsupported character set \"%s\"."),
                        charset->values[0].string.text);
       }
       else if (!charset || !language ||
@@ -480,7 +489,7 @@ cupsdProcessIPPRequest(
        cupsdLogMessage(CUPSD_LOG_DEBUG, "End of attributes...");
 
        send_ipp_status(con, IPP_BAD_REQUEST,
-                       _("Missing required attributes"));
+                       _("Missing required attributes."));
       }
       else
       {
@@ -710,7 +719,7 @@ cupsdProcessIPPRequest(
                            ippOpString(con->request->request.op.operation_id));
 
               send_ipp_status(con, IPP_OPERATION_NOT_SUPPORTED,
-                             _("%s not supported"),
+                             _("%s not supported."),
                              ippOpString(
                                  con->request->request.op.operation_id));
              break;
@@ -896,7 +905,7 @@ accept_jobs(cupsd_client_t  *con,   /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -917,8 +926,6 @@ accept_jobs(cupsd_client_t  *con,   /* I - Client connection */
   printer->accepting        = 1;
   printer->state_message[0] = '\0';
 
-  cupsdAddPrinterHistory(printer);
-
   cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
                 "Now accepting jobs.");
 
@@ -1028,7 +1035,7 @@ add_class(cupsd_client_t  *con,           /* I - Client connection */
       */
 
       send_ipp_status(con, IPP_NOT_POSSIBLE,
-                      _("A printer named \"%s\" already exists"),
+                      _("A printer named \"%s\" already exists."),
                      resource + 9);
       return;
     }
@@ -1128,7 +1135,6 @@ add_class(cupsd_client_t  *con,           /* I - Client connection */
                     pclass->name, attr->values[0].boolean, pclass->accepting);
 
     pclass->accepting = attr->values[0].boolean;
-    cupsdAddPrinterHistory(pclass);
 
     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s accepting jobs.",
                  pclass->accepting ? "Now" : "No longer");
@@ -1154,7 +1160,7 @@ add_class(cupsd_client_t  *con,           /* I - Client connection */
         attr->values[0].integer != IPP_PRINTER_STOPPED)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Attempt to set %s printer-state to bad value %d"),
+                      _("Attempt to set %s printer-state to bad value %d."),
                       pclass->name, attr->values[0].integer);
       return;
     }
@@ -1175,7 +1181,6 @@ add_class(cupsd_client_t  *con,           /* I - Client connection */
   {
     strlcpy(pclass->state_message, attr->values[0].string.text,
             sizeof(pclass->state_message));
-    cupsdAddPrinterHistory(pclass);
 
     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, pclass, NULL, "%s",
                   pclass->state_message);
@@ -1212,13 +1217,13 @@ add_class(cupsd_client_t  *con,         /* I - Client connection */
        */
 
        send_ipp_status(con, IPP_NOT_FOUND,
-                       _("The printer or class was not found."));
+                       _("The printer or class does not exist."));
        return;
       }
       else if (dtype & CUPS_PRINTER_CLASS)
       {
         send_ipp_status(con, IPP_BAD_REQUEST,
-                       _("Nested classes are not allowed"));
+                       _("Nested classes are not allowed."));
         return;
       }
 
@@ -1266,8 +1271,6 @@ add_class(cupsd_client_t  *con,           /* I - Client connection */
   }
   else
   {
-    cupsdAddPrinterHistory(pclass);
-
     cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
                  pclass, NULL, "New class \"%s\" added by \"%s\".",
                  pclass->name, get_username(con));
@@ -1324,7 +1327,7 @@ add_file(cupsd_client_t *con,             /* I - Connection to client */
 
     if (con)
       send_ipp_status(con, IPP_INTERNAL_ERROR,
-                     _("Unable to allocate memory for file types"));
+                     _("Unable to allocate memory for file types."));
 
     return (-1);
   }
@@ -1382,7 +1385,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
       strcasecmp(con->http.hostname, ServerName))
   {
     send_ipp_status(con, IPP_NOT_AUTHORIZED,
-                    _("The printer or class is not shared"));
+                    _("The printer or class is not shared."));
     return (NULL);
   }
 
@@ -1445,7 +1448,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
              filetype->type);
 
     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
-                    _("Unsupported format \'%s\'"), mimetype);
+                    _("Unsupported format \"%s\"."), mimetype);
 
     ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
                  "document-format", NULL, mimetype);
@@ -1472,14 +1475,14 @@ add_job(cupsd_client_t  *con,           /* I - Client connection */
     if (attr->value_tag != IPP_TAG_KEYWORD &&
         attr->value_tag != IPP_TAG_NAME)
     {
-      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value type"));
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value type."));
       return (NULL);
     }
 
     if (attr->num_values > 2)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Too many job-sheets values (%d > 2)"),
+                      _("Too many job-sheets values (%d > 2)."),
                      attr->num_values);
       return (NULL);
     }
@@ -1488,7 +1491,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
       if (strcmp(attr->values[i].string.text, "none") &&
           !cupsdFindBanner(attr->values[i].string.text))
       {
-       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value \"%s\""),
+       send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-sheets value \"%s\"."),
                        attr->values[i].string.text);
        return (NULL);
       }
@@ -1537,7 +1540,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
 
   if (!ippFindAttribute(con->request, "PageRegion", IPP_TAG_ZERO) &&
       !ippFindAttribute(con->request, "PageSize", IPP_TAG_ZERO) &&
-      _pwgGetPageSize(printer->pwg, con->request, NULL, &exact))
+      _ppdCacheGetPageSize(printer->pc, con->request, NULL, &exact))
   {
     if (!exact &&
         (media_col = ippFindAttribute(con->request, "media-col",
@@ -1585,8 +1588,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
 
   if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
   {
-    send_ipp_status(con, IPP_NOT_POSSIBLE,
-                    _("Too many active jobs."));
+    send_ipp_status(con, IPP_NOT_POSSIBLE, _("Too many active jobs."));
     return (NULL);
   }
 
@@ -1627,7 +1629,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
   if ((job = cupsdAddJob(priority, printer->name)) == NULL)
   {
     send_ipp_status(con, IPP_INTERNAL_ERROR,
-                    _("Unable to add job for destination \"%s\""),
+                    _("Unable to add job for destination \"%s\"."),
                    printer->name);
     return (NULL);
   }
@@ -1970,7 +1972,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
   }
   else if ((attr = ippFindAttribute(job->attrs, "job-sheets",
                                     IPP_TAG_ZERO)) != NULL)
-    job->sheets = attr;
+    job->job_sheets = attr;
 
  /*
   * Fill in the response info...
@@ -2157,7 +2159,7 @@ add_job_subscriptions(
                            resource, sizeof(resource)) < HTTP_URI_OK)
         {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("Bad notify-recipient-uri URI \"%s\""), recipient);
+                         _("Bad notify-recipient-uri \"%s\"."), recipient);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_URI_SCHEME);
          return;
@@ -2169,7 +2171,7 @@ add_job_subscriptions(
        {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
                          _("notify-recipient-uri URI \"%s\" uses unknown "
-                           "scheme"), recipient);
+                           "scheme."), recipient);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_URI_SCHEME);
          return;
@@ -2178,7 +2180,7 @@ add_job_subscriptions(
         if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
        {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("notify-recipient-uri URI \"%s\" is already used"),
+                         _("notify-recipient-uri URI \"%s\" is already used."),
                          recipient);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_ATTRIBUTES);
@@ -2193,7 +2195,7 @@ add_job_subscriptions(
         if (strcmp(pullmethod, "ippget"))
        {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("Bad notify-pull-method \"%s\""), pullmethod);
+                         _("Bad notify-pull-method \"%s\"."), pullmethod);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_ATTRIBUTES);
          return;
@@ -2205,7 +2207,7 @@ add_job_subscriptions(
               strcmp(attr->values[0].string.text, "utf-8"))
       {
         send_ipp_status(con, IPP_CHARSET,
-                       _("Character set \"%s\" not supported"),
+                       _("Character set \"%s\" not supported."),
                        attr->values[0].string.text);
        return;
       }
@@ -2214,7 +2216,7 @@ add_job_subscriptions(
                strcmp(attr->values[0].string.text, DefaultLanguage)))
       {
         send_ipp_status(con, IPP_CHARSET,
-                       _("Language \"%s\" not supported"),
+                       _("Language \"%s\" not supported."),
                        attr->values[0].string.text);
        return;
       }
@@ -2225,7 +2227,7 @@ add_job_subscriptions(
        {
           send_ipp_status(con, IPP_REQUEST_VALUE,
                          _("The notify-user-data value is too large "
-                           "(%d > 63 octets)"),
+                           "(%d > 63 octets)."),
                          attr->values[0].unknown.length);
          return;
        }
@@ -2330,45 +2332,16 @@ static void
 add_job_uuid(cupsd_client_t *con,      /* I - Client connection */
              cupsd_job_t    *job)      /* I - Job */
 {
-  char                 uuid[1024];     /* job-uuid string */
-  _cups_md5_state_t    md5state;       /* MD5 state */
-  unsigned char                md5sum[16];     /* MD5 digest/sum */
-
-
- /*
-  * First see if the job already has a job-uuid attribute; if so, return...
-  */
-
-  if (ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
-    return;
+  char                 uuid[64];       /* job-uuid string */
 
- /*
-  * No job-uuid attribute, so build a version 3 UUID with the local job
-  * ID at the end; see RFC 4122 for details.  Start with the MD5 sum of
-  * the ServerName, server name and port that the client connected to,
-  * and local job ID...
-  */
-
-  snprintf(uuid, sizeof(uuid), "%s:%s:%d:%d", ServerName, con->servername,
-          con->serverport, job->id);
-
-  _cupsMD5Init(&md5state);
-  _cupsMD5Append(&md5state, (unsigned char *)uuid, strlen(uuid));
-  _cupsMD5Finish(&md5state, md5sum);
 
  /*
-  * Format the UUID URI using the MD5 sum and job ID.
+  * Add a job-uuid attribute if none exists...
   */
 
-  snprintf(uuid, sizeof(uuid),
-           "urn:uuid:%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
-          "%02x%02x%02x%02x%02x%02x",
-          md5sum[0], md5sum[1], md5sum[2], md5sum[3], md5sum[4], md5sum[5],
-          (md5sum[6] & 15) | 0x30, md5sum[7], (md5sum[8] & 0x3f) | 0x40,
-          md5sum[9], md5sum[10], md5sum[11], md5sum[12], md5sum[13],
-          md5sum[14], md5sum[15]);
-
-  ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL, uuid);
+  if (!ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
+    ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-uuid", NULL,
+                cupsdMakeUUID(job->dest, job->id, uuid, sizeof(uuid)));
 }
 
 
@@ -2458,7 +2431,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
       */
 
       send_ipp_status(con, IPP_NOT_POSSIBLE,
-                      _("A class named \"%s\" already exists"),
+                      _("A class named \"%s\" already exists."),
                      resource + 10);
       return;
     }
@@ -2577,7 +2550,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
 
     if (uri_status < HTTP_URI_OK)
     {
-      send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\""),
+      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);
@@ -2597,7 +2570,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
        */
 
        send_ipp_status(con, IPP_NOT_POSSIBLE,
-                       _("File device URIs have been disabled! "
+                       _("File device URIs have been disabled. "
                          "To enable, see the FileDevice directive in "
                          "\"%s/cupsd.conf\"."),
                        ServerRoot);
@@ -2617,8 +2590,8 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
         * Could not find device in list!
        */
 
-       send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri scheme \"%s\""),
-                       scheme);
+       send_ipp_status(con, IPP_NOT_POSSIBLE,
+                        _("Bad device-uri scheme \"%s\"."), scheme);
        return;
       }
     }
@@ -2661,7 +2634,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
 
     if (!supported || i >= supported->num_values)
     {
-      send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad port-monitor \"%s\""),
+      send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad port-monitor \"%s\"."),
                      attr->values[0].string.text);
       return;
     }
@@ -2688,7 +2661,6 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
                     printer->name, attr->values[0].boolean, printer->accepting);
 
     printer->accepting = attr->values[0].boolean;
-    cupsdAddPrinterHistory(printer);
 
     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
                   "%s accepting jobs.",
@@ -2714,7 +2686,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
     if (attr->values[0].integer != IPP_PRINTER_IDLE &&
         attr->values[0].integer != IPP_PRINTER_STOPPED)
     {
-      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad printer-state value %d"),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad printer-state value %d."),
                       attr->values[0].integer);
       return;
     }
@@ -2736,7 +2708,6 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
   {
     strlcpy(printer->state_message, attr->values[0].string.text,
             sizeof(printer->state_message));
-    cupsdAddPrinterHistory(printer);
 
     cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL, "%s",
                   printer->state_message);
@@ -2749,7 +2720,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
             (int)(sizeof(printer->reasons) / sizeof(printer->reasons[0])))
     {
       send_ipp_status(con, IPP_NOT_POSSIBLE,
-                      _("Too many printer-state-reasons values (%d > %d)"),
+                      _("Too many printer-state-reasons values (%d > %d)."),
                      attr->num_values,
                      (int)(sizeof(printer->reasons) /
                            sizeof(printer->reasons[0])));
@@ -2924,7 +2895,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
 
       if (copy_model(con, attr->values[0].string.text, dstfile))
       {
-        send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file"));
+        send_ipp_status(con, IPP_INTERNAL_ERROR, _("Unable to copy PPD file."));
        return;
       }
 
@@ -2943,11 +2914,7 @@ add_printer(cupsd_client_t  *con,        /* I - Client connection */
 
     char cache_name[1024];             /* Cache filename for printer attrs */
 
-    snprintf(cache_name, sizeof(cache_name), "%s/%s.ipp3", CacheDir,
-             printer->name);
-    unlink(cache_name);
-
-    snprintf(cache_name, sizeof(cache_name), "%s/%s.pwg2", CacheDir,
+    snprintf(cache_name, sizeof(cache_name), "%s/%s.data", CacheDir,
              printer->name);
     unlink(cache_name);
 
@@ -3038,8 +3005,6 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
   }
   else
   {
-    cupsdAddPrinterHistory(printer);
-
     cupsdAddEvent(CUPSD_EVENT_PRINTER_ADDED,
                   printer, NULL, "New printer \"%s\" added by \"%s\".",
                  printer->name, get_username(con));
@@ -3106,15 +3071,21 @@ add_queued_job_count(
 
 static void
 apple_init_profile(
-    ppd_file_t          *ppd,          /* I - PPD file */
-    cups_array_t       *languages,     /* I - Languages in the PPD file */
-    CMDeviceProfileInfo *profile,      /* I - Profile record */
-    unsigned            id,            /* I - Profile ID */
-    const char          *name,         /* I - Profile name */
-    const char          *text,         /* I - Profile UI text */
-    const char          *iccfile)      /* I - ICC filename */
+    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 */
 {
-  char                 url[1024];      /* URL for profile 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 */
@@ -3135,7 +3106,7 @@ apple_init_profile(
 
   if (cftext)
   {
-    CFDictionarySetValue(dict, CFSTR("en"), cftext);
+    CFDictionarySetValue(dict, CFSTR("en_US"), cftext);
     CFRelease(cftext);
   }
 
@@ -3185,10 +3156,24 @@ apple_init_profile(
   * Fill in the profile data...
   */
 
-  if (iccfile)
-    httpAssembleURI(HTTP_URI_CODING_ALL, url, sizeof(url), "file", NULL, "", 0,
-                   iccfile);
+#  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;
@@ -3197,6 +3182,7 @@ apple_init_profile(
   if (iccfile)
     strlcpy(profile->profileLoc.u.pathLoc.path, iccfile,
            sizeof(profile->profileLoc.u.pathLoc.path));
+#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
 }
 
 
@@ -3228,13 +3214,19 @@ apple_register_profiles(
   ppd_option_t         *cm_option;     /* Color model option */
   ppd_choice_t         *cm_choice;     /* Color model choice */
   int                  num_profiles;   /* Number of profiles */
-  CMError              error;          /* Last error */
+  OSStatus             error = 0;      /* Last error */
   unsigned             device_id,      /* Printer device ID */
                        profile_id,     /* 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,
@@ -3242,15 +3234,21 @@ apple_register_profiles(
                        };
   CMDeviceProfileArrayPtr profiles;    /* Profiles */
   CMDeviceProfileInfo  *profile;       /* Current profile */
-  cups_array_t         *languages;     /* Languages array */
+#  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...
@@ -3287,6 +3285,22 @@ apple_register_profiles(
       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...
@@ -3356,6 +3370,7 @@ apple_register_profiles(
        q3_choice = NULL;
     }
 
+#  ifndef HAVE_COLORSYNCREGISTERDEVICE
    /*
     * Build the array of profiles...
     *
@@ -3365,17 +3380,22 @@ apple_register_profiles(
     if ((profiles = calloc(num_profiles, sizeof(CMDeviceProfileArray))) == NULL)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to allocate memory for %d profiles",
-                     num_profiles);
+                      "Unable to allocate memory for factory profiles.");
       ppdClose(ppd);
       return;
     }
 
     profiles->profileCount = num_profiles;
-    languages              = _ppdGetLanguages(ppd);
+    profile = profiles->profiles;
+#  endif /* !HAVE_COLORSYNCREGISTERDEVICE */
+
+   /*
+    * Loop through the profiles listed in the PPD...
+    */
+
+    languages = _ppdGetLanguages(ppd);
 
-    for (profile = profiles->profiles,
-             attr = ppdFindAttr(ppd, profile_key, NULL);
+    for (attr = ppdFindAttr(ppd, profile_key, NULL);
         attr;
         attr = ppdFindNextAttr(ppd, profile_key, NULL))
       if (attr->spec[0] && attr->value && attr->value[0])
@@ -3409,50 +3429,91 @@ apple_register_profiles(
        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)
+        if (!default_profile_id && q1_choice && q2_choice && q3_choice)
        {
-         if (q2_choice)
-         {
-           if (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;
-            }
+         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)
-           {
-             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 && 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 && 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 && 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)
-         {
-           snprintf(selector, sizeof(selector), "%s..", q1_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;
        }
       }
 
@@ -3469,21 +3530,26 @@ apple_register_profiles(
 
     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 %d profiles",
-                     num_profiles);
+                      "Unable to allocate memory for factory profiles.");
       ppdClose(ppd);
       return;
     }
 
     profiles->profileCount = num_profiles;
+    profile = profiles->profiles;
+#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
 
-    for (profile = profiles->profiles, i = cm_option->num_choices,
-             cm_choice = cm_option->choices;
+    for (i = cm_option->num_choices, cm_choice = cm_option->choices;
          i > 0;
-        i --, cm_choice ++, profile ++)
+        i --, cm_choice ++)
     {
       if (!strcmp(cm_choice->choice, "Gray") ||
           !strcmp(cm_choice->choice, "Black"))
@@ -3500,9 +3566,38 @@ apple_register_profiles(
       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;
     }
@@ -3517,31 +3612,133 @@ apple_register_profiles(
 
     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)
+    {
+      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 %d profiles",
-                     num_profiles);
+                      "Unable to allocate memory for factory profiles.");
       ppdClose(ppd);
       return;
     }
 
     profiles->profileCount = num_profiles;
 
-    apple_init_profile(ppd, NULL, profiles->profiles, _ppdHashName("Gray.."),
-                       "Gray", "Gray", NULL);
+   /*
+    * 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);
+
+   /*
+    * Then add the RGB/CMYK/DeviceN color profile...
+    */
 
     switch (ppd->colorspace)
     {
       case PPD_CS_RGB :
       case PPD_CS_CMY :
-          apple_init_profile(ppd, NULL, profiles->profiles + 1,
-                            _ppdHashName("RGB.."), "RGB", "RGB", NULL);
+          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 :
-          apple_init_profile(ppd, NULL, profiles->profiles + 1,
-                            _ppdHashName("CMYK.."), "CMYK", "CMYK", NULL);
+          profile_id = _ppdHashName("CMYK..");
+          apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
+                            "CMYK", "CMYK", NULL);
           break;
 
       case PPD_CS_GRAY :
@@ -3549,11 +3746,12 @@ apple_register_profiles(
            break;
 
       case PPD_CS_N :
-          apple_init_profile(ppd, NULL, profiles->profiles + 1,
-                            _ppdHashName("DeviceN.."), "DeviceN", "DeviceN",
-                            NULL);
+          profile_id = _ppdHashName("DeviceN..");
+          apple_init_profile(ppd, NULL, profiles->profiles + 1, profile_id,
+                            "DeviceN", "DeviceN", NULL);
           break;
     }
+#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
   }
 
   if (num_profiles > 0)
@@ -3563,7 +3761,18 @@ apple_register_profiles(
     */
 
     if (!default_profile_id)
-      default_profile_id = profiles->profiles[num_profiles - 1].profileID;
+      default_profile_id = profile_id; /* Last profile */
+
+#  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 */
 
    /*
     * Get the device ID hash and pathelogical name dictionary.
@@ -3581,12 +3790,61 @@ apple_register_profiles(
 
     if (device_name && printer_name)
     {
-      CFDictionarySetValue(device_name, CFSTR("en"), printer_name);
+      CFDictionarySetValue(device_name, CFSTR("en_US"), printer_name);
 
      /*
       * 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 */
+      CFStringRef      printerUUID;    /* Printer UUID */
+      CFUUIDRef                deviceUUID;     /* Device UUID */
+
+      deviceDict = CFDictionaryCreate(kCFAllocatorDefault,
+                                     (const void **)deviceDictKeys,
+                                     (const void **)deviceDictVals,
+                                     sizeof(deviceDictKeys) /
+                                         sizeof(deviceDictKeys[0]),
+                                     &kCFTypeDictionaryKeyCallBacks,
+                                     &kCFTypeDictionaryValueCallBacks);
+      printerUUID = CFStringCreateWithCString(kCFAllocatorDefault,
+                                              p->uuid + 9, /* Skip urn:uuid: */
+                                             kCFStringEncodingUTF8);
+      if (printerUUID)
+      {
+        deviceUUID = CFUUIDCreateFromString(kCFAllocatorDefault, printerUUID);
+       CFRelease(printerUUID);
+
+       if (!deviceDict || !deviceUUID ||
+           !ColorSyncRegisterDevice(kColorSyncPrinterDeviceClass, deviceUUID,
+                                    deviceDict))
+         error = 1001;
+      }
+      else
+        error = 1001;
+
+      if (deviceUUID)
+        CFRelease(deviceUUID);
+
+      if (deviceDict)
+        CFRelease(deviceDict);
+
+#  else
       error = CMRegisterColorDevice(cmPrinterDeviceClass, device_id,
                                     device_name, &scope);
 
@@ -3597,6 +3855,7 @@ apple_register_profiles(
       if (error == noErr)
        error = CMSetDeviceFactoryProfiles(cmPrinterDeviceClass, device_id,
                                           default_profile_id, profiles);
+#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
     }
     else
       error = 1000;
@@ -3607,22 +3866,34 @@ apple_register_profiles(
 
     if (error != noErr)
       cupsdLogMessage(CUPSD_LOG_ERROR,
-                     "Unable to register ICC color profiles for \"%s\" - %d",
+                     "Unable to register ICC color profiles for \"%s\": %d",
                      p->name, (int)error);
 
+    if (printer_name)
+      CFRelease(printer_name);
+
+    if (device_name)
+      CFRelease(device_name);
+  }
+
+ /*
+  * Free any memory we used...
+  */
+
+#  ifdef HAVE_COLORSYNCREGISTERDEVICE
+  CFRelease(profiles);
+
+#  else
+  if (num_profiles > 0)
+  {
     for (profile = profiles->profiles;
         num_profiles > 0;
         profile ++, num_profiles --)
       CFRelease(profile->profileName);
 
     free(profiles);
-
-    if (printer_name)
-      CFRelease(printer_name);
-
-    if (device_name)
-      CFRelease(device_name);
   }
+#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
 
   ppdClose(ppd);
 }
@@ -3641,11 +3912,48 @@ apple_unregister_profiles(
   * Make sure ColorSync is available...
   */
 
+#  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.
+    */
+
+    CFStringRef printerUUID;           /* Printer UUID */
+    CFUUIDRef deviceUUID;              /* Device UUID */
+
+    deviceUUID = ColorSyncCreateUUIDFromUInt32(_ppdHashName(p->name));
+    if (deviceUUID)
+    {
+      ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
+      CFRelease(deviceUUID);
+    }
+
+    printerUUID = CFStringCreateWithCString(kCFAllocatorDefault, p->uuid + 9,
+                                            kCFStringEncodingUTF8);
+    if (printerUUID)
+    {
+      deviceUUID = CFUUIDCreateFromString(kCFAllocatorDefault, printerUUID);
+      if (deviceUUID)
+      {
+       ColorSyncUnregisterDevice(kColorSyncPrinterDeviceClass, deviceUUID);
+       CFRelease(deviceUUID);
+      }
+
+      CFRelease(printerUUID);
+    }
+  }
+
+#  else
   if (CMUnregisterColorDevice != NULL)
     CMUnregisterColorDevice(cmPrinterDeviceClass, _ppdHashName(p->name));
+#  endif /* HAVE_COLORSYNCREGISTERDEVICE */
 }
 #endif /* __APPLE__ */
 
+
 /*
  * 'apply_printer_defaults()' - Apply printer default options to a job.
  */
@@ -3731,7 +4039,7 @@ authenticate_job(cupsd_client_t  *con,    /* I - Client connection */
                                  IPP_TAG_INTEGER)) == NULL)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Got a printer-uri attribute but no job-id"));
+                      _("Got a printer-uri attribute but no job-id."));
       return;
     }
 
@@ -3753,7 +4061,7 @@ authenticate_job(cupsd_client_t  *con,    /* I - Client connection */
       * Not a valid URI!
       */
 
-      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri attribute \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -3771,8 +4079,7 @@ authenticate_job(cupsd_client_t  *con,    /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND,
-                    _("Job #%d does not exist"), jobid);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
     return;
   }
 
@@ -3787,7 +4094,7 @@ authenticate_job(cupsd_client_t  *con,    /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_POSSIBLE,
-                    _("Job #%d is not held for authentication"),
+                    _("Job #%d is not held for authentication."),
                    jobid);
     return;
   }
@@ -3815,7 +4122,7 @@ authenticate_job(cupsd_client_t  *con,    /* I - Client connection */
       send_http_error(con, HTTP_UNAUTHORIZED, printer);
     else
       send_ipp_status(con, IPP_NOT_AUTHORIZED,
-                     _("No authentication information provided"));
+                     _("No authentication information provided."));
     return;
   }
 
@@ -3856,7 +4163,11 @@ authenticate_job(cupsd_client_t  *con,   /* I - Client connection */
 
   cupsdReleaseJob(job);
 
+  cupsdAddEvent(CUPSD_EVENT_JOB_STATE, NULL, job, "Job authenticated by user");
+
   cupsdLogJob(job, CUPSD_LOG_INFO, "Authenticated by \"%s\".", con->username);
+
+  cupsdCheckJobs();
 }
 
 
@@ -3910,7 +4221,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
          else
          {
            send_ipp_status(con, IPP_BAD_REQUEST,
-                           _("Missing requesting-user-name attribute"));
+                           _("Missing requesting-user-name attribute."));
            return;
          }
        }
@@ -3935,7 +4246,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
         else
         {
          send_ipp_status(con, IPP_BAD_REQUEST,
-                         _("Missing requesting-user-name attribute"));
+                         _("Missing requesting-user-name attribute."));
          return;
         }
 
@@ -3952,7 +4263,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
   if (strcmp(uri->name, "printer-uri"))
   {
     send_ipp_status(con, IPP_BAD_REQUEST,
-                    _("The printer-uri attribute is required"));
+                    _("The printer-uri attribute is required."));
     return;
   }
 
@@ -3975,7 +4286,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
         (!strncmp(resource, "/classes/", 9) && resource[9]))
     {
       send_ipp_status(con, IPP_NOT_FOUND,
-                      _("The printer or class was not found."));
+                      _("The printer or class does not exist."));
       return;
     }
 
@@ -3999,7 +4310,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
 
       if (i < job_ids->num_values)
       {
-       send_ipp_status(con, IPP_NOT_FOUND, _("job-id %d not found."),
+       send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
                        job_ids->values[i].integer);
        return;
       }
@@ -4054,7 +4365,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
 
       if (i < job_ids->num_values)
       {
-       send_ipp_status(con, IPP_NOT_FOUND, _("job-id %d not found."),
+       send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
                        job_ids->values[i].integer);
        return;
       }
@@ -4129,7 +4440,7 @@ cancel_job(cupsd_client_t  *con,  /* I - Client connection */
                                  IPP_TAG_INTEGER)) == NULL)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Got a printer-uri attribute but no job-id"));
+                      _("Got a printer-uri attribute but no job-id."));
       return;
     }
 
@@ -4146,7 +4457,7 @@ cancel_job(cupsd_client_t  *con,  /* I - Client connection */
        */
 
        send_ipp_status(con, IPP_NOT_FOUND,
-                       _("The printer or class was not found."));
+                       _("The printer or class does not exist."));
        return;
       }
 
@@ -4180,7 +4491,7 @@ cancel_job(cupsd_client_t  *con,  /* I - Client connection */
          jobid = job->id;
        else
        {
-         send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s"),
+         send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s."),
                          printer->name);
          return;
        }
@@ -4203,8 +4514,7 @@ cancel_job(cupsd_client_t  *con,  /* I - Client connection */
       * Not a valid URI!
       */
 
-      send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad job-uri attribute \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -4232,7 +4542,7 @@ cancel_job(cupsd_client_t  *con,  /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
     return;
   }
 
@@ -4327,7 +4637,7 @@ cancel_subscription(
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("notify-subscription-id %d no good"), sub_id);
+                    _("Subscription #%d does not exist."), sub_id);
     return;
   }
 
@@ -4391,12 +4701,13 @@ check_rss_recipient(
  * 'check_quotas()' - Check quotas for a printer and user.
  */
 
-static int                             /* O - 1 if OK, 0 if not */
+static int                             /* O - 1 if OK, 0 if forbidden,
+                                              -1 if limit reached */
 check_quotas(cupsd_client_t  *con,     /* I - Client connection */
              cupsd_printer_t *p)       /* I - Printer or class */
 {
-  int          i;                      /* Looping var */
-  char         username[33];           /* Username */
+  char         username[33],           /* Username */
+               *name;                  /* Current user name */
   cupsd_quota_t        *q;                     /* Quota data */
 #ifdef HAVE_MBR_UID_TO_UUID
  /*
@@ -4441,7 +4752,7 @@ check_quotas(cupsd_client_t  *con,        /* I - Client connection */
     {
       cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for printer \"%s\"...",
                       p->name);
-      return (0);
+      return (-1);
     }
   }
 
@@ -4455,7 +4766,7 @@ check_quotas(cupsd_client_t  *con,        /* I - Client connection */
     {
       cupsdLogMessage(CUPSD_LOG_INFO, "Too many jobs for user \"%s\"...",
                       username);
-      return (0);
+      return (-1);
     }
   }
 
@@ -4463,10 +4774,10 @@ check_quotas(cupsd_client_t  *con,      /* I - Client connection */
   * Check against users...
   */
 
-  if (p->num_users == 0 && p->k_limit == 0 && p->page_limit == 0)
+  if (cupsArrayCount(p->users) == 0 && p->k_limit == 0 && p->page_limit == 0)
     return (1);
 
-  if (p->num_users)
+  if (cupsArrayCount(p->users))
   {
 #ifdef HAVE_MBR_UID_TO_UUID
    /*
@@ -4497,21 +4808,22 @@ check_quotas(cupsd_client_t  *con,      /* I - Client connection */
     endpwent();
 #endif /* HAVE_MBR_UID_TO_UUID */
 
-    for (i = 0; i < p->num_users; i ++)
-      if (p->users[i][0] == '@')
+    for (name = (char *)cupsArrayFirst(p->users);
+         name;
+        name = (char *)cupsArrayNext(p->users))
+      if (name[0] == '@')
       {
        /*
         * Check group membership...
        */
 
 #ifdef HAVE_MBR_UID_TO_UUID
-        if (p->users[i][1] == '#')
+        if (name[1] == '#')
        {
-         if (uuid_parse((char *)p->users[i] + 2, grp_uuid))
+         if (uuid_parse(name + 2, grp_uuid))
            uuid_clear(grp_uuid);
        }
-       else if ((mbr_err = mbr_group_name_to_uuid((char *)p->users[i] + 1,
-                                                  grp_uuid)) != 0)
+       else if ((mbr_err = mbr_group_name_to_uuid(name + 1, grp_uuid)) != 0)
        {
         /*
          * Invalid ACL entries are ignored for matching; just record a
@@ -4520,10 +4832,10 @@ check_quotas(cupsd_client_t  *con,      /* I - Client connection */
 
          cupsdLogMessage(CUPSD_LOG_DEBUG,
                          "check_quotas: UUID lookup failed for ACL entry "
-                         "\"%s\" (err=%d)", p->users[i], mbr_err);
+                         "\"%s\" (err=%d)", name, mbr_err);
          cupsdLogMessage(CUPSD_LOG_WARN,
                          "Access control entry \"%s\" not a valid group name; "
-                         "entry ignored", p->users[i]);
+                         "entry ignored", name);
        }
 
        if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
@@ -4535,7 +4847,7 @@ check_quotas(cupsd_client_t  *con,        /* I - Client connection */
 
          cupsdLogMessage(CUPSD_LOG_DEBUG,
                          "check_quotas: group \"%s\" membership check "
-                         "failed (err=%d)", p->users[i] + 1, mbr_err);
+                         "failed (err=%d)", name + 1, mbr_err);
          is_member = 0;
        }
 
@@ -4547,20 +4859,19 @@ check_quotas(cupsd_client_t  *con,      /* I - Client connection */
          break;
 
 #else
-        if (cupsdCheckGroup(username, pw, p->users[i] + 1))
+        if (cupsdCheckGroup(username, pw, name + 1))
          break;
 #endif /* HAVE_MBR_UID_TO_UUID */
       }
 #ifdef HAVE_MBR_UID_TO_UUID
       else
       {
-        if (p->users[i][0] == '#')
+        if (name[0] == '#')
        {
-         if (uuid_parse((char *)p->users[i] + 1, usr2_uuid))
+         if (uuid_parse(name + 1, usr2_uuid))
            uuid_clear(usr2_uuid);
         }
-        else if ((mbr_err = mbr_user_name_to_uuid((char *)p->users[i],
-                                                 usr2_uuid)) != 0)
+        else if ((mbr_err = mbr_user_name_to_uuid(name, usr2_uuid)) != 0)
        {
         /*
          * Invalid ACL entries are ignored for matching; just record a
@@ -4569,21 +4880,21 @@ check_quotas(cupsd_client_t  *con,      /* I - Client connection */
 
           cupsdLogMessage(CUPSD_LOG_DEBUG,
                          "check_quotas: UUID lookup failed for ACL entry "
-                         "\"%s\" (err=%d)", p->users[i], mbr_err);
+                         "\"%s\" (err=%d)", name, mbr_err);
           cupsdLogMessage(CUPSD_LOG_WARN,
                          "Access control entry \"%s\" not a valid user name; "
-                         "entry ignored", p->users[i]);
+                         "entry ignored", name);
        }
 
        if (!uuid_compare(usr_uuid, usr2_uuid))
          break;
       }
 #else
-      else if (!strcasecmp(username, p->users[i]))
+      else if (!strcasecmp(username, name))
        break;
 #endif /* HAVE_MBR_UID_TO_UUID */
 
-    if ((i < p->num_users) == p->deny_users)
+    if ((name != NULL) == p->deny_users)
     {
       cupsdLogMessage(CUPSD_LOG_INFO,
                       "Denying user \"%s\" access to printer \"%s\"...",
@@ -4664,7 +4975,7 @@ close_job(cupsd_client_t  *con,           /* I - Client connection */
                               IPP_TAG_INTEGER)) == NULL)
   {
     send_ipp_status(con, IPP_BAD_REQUEST,
-                   _("Got a printer-uri attribute but no job-id"));
+                   _("Got a printer-uri attribute but no job-id."));
     return;
   }
 
@@ -4674,7 +4985,7 @@ close_job(cupsd_client_t  *con,           /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"),
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
                     attr->values[0].integer);
     return;
   }
@@ -4939,7 +5250,8 @@ copy_attrs(ipp_t        *to,              /* I - Destination request */
            ipp_t        *from,         /* I - Source request */
            cups_array_t *ra,           /* I - Requested attributes */
           ipp_tag_t    group,          /* I - Group to copy */
-          int          quickcopy)      /* I - Do a quick copy? */
+          int          quickcopy,      /* I - Do a quick copy? */
+          cups_array_t *exclude)       /* I - Attributes to exclude? */
 {
   ipp_attribute_t      *fromattr;      /* Source attribute */
 
@@ -4961,6 +5273,23 @@ copy_attrs(ipp_t        *to,             /* I - Destination request */
          fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
       continue;
 
+    if (exclude &&
+        (cupsArrayFind(exclude, fromattr->name) ||
+        cupsArrayFind(exclude, "all")))
+    {
+     /*
+      * We need to exclude this attribute for security reasons; we require the
+      * job-id and job-printer-uri attributes regardless of the security
+      * settings for IPP conformance.
+      *
+      * Subscription attribute security is handled by copy_subscription_attrs().
+      */
+
+      if (strcmp(fromattr->name, "job-id") &&
+         strcmp(fromattr->name, "job-printer-uri"))
+        continue;
+    }
+
     if (!ra || cupsArrayFind(ra, fromattr->name))
     {
      /*
@@ -5168,7 +5497,11 @@ copy_banner(cupsd_client_t *con, /* I - Client connection */
          case IPP_TAG_ENUM :
              if (!strncmp(s, "time-at-", 8))
              {
-               struct timeval tv = { attr->values[i].integer, 0 };
+               struct timeval tv;      /* Time value */
+
+               tv.tv_sec  = attr->values[i].integer;
+               tv.tv_usec = 0;
+
                cupsFilePuts(out, cupsdGetDateTime(&tv, CUPSD_TIME_STANDARD));
              }
              else
@@ -5607,7 +5940,8 @@ copy_model(cupsd_client_t *con,           /* I - Client connection */
 static void
 copy_job_attrs(cupsd_client_t *con,    /* I - Client connection */
               cupsd_job_t    *job,     /* I - Job */
-              cups_array_t   *ra)      /* I - Requested attributes array */
+              cups_array_t   *ra,      /* I - Requested attributes array */
+              cups_array_t   *exclude) /* I - Private attributes array */
 {
   char job_uri[HTTP_MAX_URI];          /* Job URI */
 
@@ -5620,26 +5954,34 @@ copy_job_attrs(cupsd_client_t *con,     /* I - Client connection */
                    con->servername, con->serverport, "/jobs/%d",
                   job->id);
 
-  if (!ra || cupsArrayFind(ra, "document-count"))
-    ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
-                 "document-count", job->num_files);
+  if (!cupsArrayFind(exclude, "all"))
+  {
+    if ((!exclude || !cupsArrayFind(exclude, "document-count")) &&
+        (!ra || cupsArrayFind(ra, "document-count")))
+      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+                   "document-count", job->num_files);
 
-  if (!ra || cupsArrayFind(ra, "job-media-progress"))
-    ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
-                 "job-media-progress", job->progress);
+    if ((!exclude || !cupsArrayFind(exclude, "job-media-progress")) &&
+        (!ra || cupsArrayFind(ra, "job-media-progress")))
+      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+                   "job-media-progress", job->progress);
 
-  if (!ra || cupsArrayFind(ra, "job-more-info"))
-    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
-                "job-more-info", NULL, job_uri);
+    if ((!exclude || !cupsArrayFind(exclude, "job-more-info")) &&
+        (!ra || cupsArrayFind(ra, "job-more-info")))
+      ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
+                  "job-more-info", NULL, job_uri);
 
-  if (job->state_value > IPP_JOB_PROCESSING &&
-      (!ra || cupsArrayFind(ra, "job-preserved")))
-    ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
-                  job->num_files > 0);
+    if (job->state_value > IPP_JOB_PROCESSING &&
+       (!exclude || !cupsArrayFind(exclude, "job-preserved")) &&
+        (!ra || cupsArrayFind(ra, "job-preserved")))
+      ippAddBoolean(con->response, IPP_TAG_JOB, "job-preserved",
+                   job->num_files > 0);
 
-  if (!ra || cupsArrayFind(ra, "job-printer-up-time"))
-    ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
-                  "job-printer-up-time", time(NULL));
+    if ((!exclude || !cupsArrayFind(exclude, "job-printer-up-time")) &&
+        (!ra || cupsArrayFind(ra, "job-printer-up-time")))
+      ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+                   "job-printer-up-time", time(NULL));
+  }
 
   if (!ra || cupsArrayFind(ra, "job-state-reasons"))
     add_job_state_reasons(con, job);
@@ -5648,7 +5990,7 @@ copy_job_attrs(cupsd_client_t *con,       /* I - Client connection */
     ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
                 "job-uri", NULL, job_uri);
 
-  copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0);
+  copy_attrs(con->response, job->attrs, ra, IPP_TAG_JOB, 0, exclude);
 }
 
 
@@ -5664,9 +6006,10 @@ copy_printer_attrs(
 {
   char                 printer_uri[HTTP_MAX_URI];
                                        /* Printer URI */
+  char                 printer_icons[HTTP_MAX_URI];
+                                       /* Printer icons */
   time_t               curtime;        /* Current time */
   int                  i;              /* Looping var */
-  ipp_attribute_t      *history;       /* History collection */
 
 
  /*
@@ -5763,6 +6106,16 @@ copy_printer_attrs(
                    sizeof(errors) / sizeof(errors[0]), NULL, errors);
   }
 
+  if (!ra || cupsArrayFind(ra, "printer-icons"))
+  {
+    httpAssembleURIf(HTTP_URI_CODING_ALL, printer_icons, sizeof(printer_icons),
+                     "http", NULL, con->servername, con->serverport,
+                    "/icons/%s.png", printer->name);
+    ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_URI, "printer-icons",
+                 NULL, printer_icons);
+    cupsdLogMessage(CUPSD_LOG_DEBUG2, "printer-icons=\"%s\"", printer_icons);
+  }
+
   if (!ra || cupsArrayFind(ra, "printer-is-accepting-jobs"))
     ippAddBoolean(con->response, IPP_TAG_PRINTER, "printer-is-accepting-jobs",
                   printer->accepting);
@@ -5794,23 +6147,6 @@ copy_printer_attrs(
     ippAddInteger(con->response, IPP_TAG_PRINTER, IPP_TAG_INTEGER,
                   "printer-state-change-time", printer->state_time);
 
-  if (MaxPrinterHistory > 0 && printer->num_history > 0 &&
-      cupsArrayFind(ra, "printer-state-history"))
-  {
-   /*
-    * Printer history is only sent if specifically requested, so that
-    * older CUPS/IPP clients won't barf on the collection attributes.
-    */
-
-    history = ippAddCollections(con->response, IPP_TAG_PRINTER,
-                                "printer-state-history",
-                                printer->num_history, NULL);
-
-    for (i = 0; i < printer->num_history; i ++)
-      copy_attrs(history->values[i].collection = ippNew(), printer->history[i],
-                 NULL, IPP_TAG_ZERO, 0);
-  }
-
   if (!ra || cupsArrayFind(ra, "printer-state-message"))
     ippAddString(con->response, IPP_TAG_PRINTER, IPP_TAG_TEXT,
                 "printer-state-message", NULL, printer->state_message);
@@ -5822,7 +6158,6 @@ copy_printer_attrs(
   {
     int type;                          /* printer-type value */
 
-
    /*
     * Add the CUPS-specific printer-type attribute...
     */
@@ -5862,10 +6197,10 @@ copy_printer_attrs(
   if (!ra || cupsArrayFind(ra, "queued-job-count"))
     add_queued_job_count(con, printer);
 
-  copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0);
+  copy_attrs(con->response, printer->attrs, ra, IPP_TAG_ZERO, 0, NULL);
   if (printer->ppd_attrs)
-    copy_attrs(con->response, printer->ppd_attrs, ra, IPP_TAG_ZERO, 0);
-  copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY);
+    copy_attrs(con->response, printer->ppd_attrs, ra, IPP_TAG_ZERO, 0, NULL);
+  copy_attrs(con->response, CommonData, ra, IPP_TAG_ZERO, IPP_TAG_COPY, NULL);
 }
 
 
@@ -5877,7 +6212,8 @@ static void
 copy_subscription_attrs(
     cupsd_client_t       *con,         /* I - Client connection */
     cupsd_subscription_t *sub,         /* I - Subscription */
-    cups_array_t         *ra)          /* I - Requested attributes array */
+    cups_array_t         *ra,          /* I - Requested attributes array */
+    cups_array_t         *exclude)     /* I - Private attributes array */
 {
   ipp_attribute_t      *attr;          /* Current attribute */
   char                 printer_uri[HTTP_MAX_URI];
@@ -5887,56 +6223,92 @@ copy_subscription_attrs(
   const char           *name;          /* Current event name */
 
 
+  cupsdLogMessage(CUPSD_LOG_DEBUG2,
+                  "copy_subscription_attrs(con=%p, sub=%p, ra=%p, exclude=%p)",
+                 con, sub, ra, exclude);
+
  /*
   * Copy the subscription attributes to the response using the
   * requested-attributes attribute that may be provided by the client.
   */
 
-  if (!ra || cupsArrayFind(ra, "notify-events"))
+  if (!exclude || !cupsArrayFind(exclude, "all"))
   {
-    if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
+    if ((!exclude || !cupsArrayFind(exclude, "notify-events")) &&
+        (!ra || cupsArrayFind(ra, "notify-events")))
     {
-     /*
-      * Simple event list...
-      */
+      cupsdLogMessage(CUPSD_LOG_DEBUG2, "copy_subscription_attrs: notify-events");
 
-      ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
-                   (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
-                   "notify-events", NULL, name);
-    }
-    else
-    {
-     /*
-      * Complex event list...
-      */
+      if ((name = cupsdEventName((cupsd_eventmask_t)sub->mask)) != NULL)
+      {
+       /*
+       * Simple event list...
+       */
 
-      for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
-       if (sub->mask & mask)
-          count ++;
+       ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
+                    (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
+                    "notify-events", NULL, name);
+      }
+      else
+      {
+       /*
+       * Complex event list...
+       */
 
-      attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
-                           (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
-                           "notify-events", count, NULL, NULL);
+       for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
+         if (sub->mask & mask)
+           count ++;
 
-      for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
-       if (sub->mask & mask)
-       {
-          attr->values[count].string.text =
-             (char *)cupsdEventName((cupsd_eventmask_t)mask);
+       attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
+                            (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
+                            "notify-events", count, NULL, NULL);
 
-          count ++;
-       }
+       for (mask = 1, count = 0; mask < CUPSD_EVENT_ALL; mask <<= 1)
+         if (sub->mask & mask)
+         {
+           attr->values[count].string.text =
+               (char *)cupsdEventName((cupsd_eventmask_t)mask);
+
+           count ++;
+         }
+      }
     }
+
+    if ((!exclude || !cupsArrayFind(exclude, "notify-lease-duration")) &&
+        (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration"))))
+      ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
+                   "notify-lease-duration", sub->lease);
+
+    if ((!exclude || !cupsArrayFind(exclude, "notify-recipient-uri")) &&
+        (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri"))))
+      ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
+                  "notify-recipient-uri", NULL, sub->recipient);
+    else if ((!exclude || !cupsArrayFind(exclude, "notify-pull-method")) &&
+             (!ra || cupsArrayFind(ra, "notify-pull-method")))
+      ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
+                  "notify-pull-method", NULL, "ippget");
+
+    if ((!exclude || !cupsArrayFind(exclude, "notify-subscriber-user-name")) &&
+        (!ra || cupsArrayFind(ra, "notify-subscriber-user-name")))
+      ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
+                  "notify-subscriber-user-name", NULL, sub->owner);
+
+    if ((!exclude || !cupsArrayFind(exclude, "notify-time-interval")) &&
+        (!ra || cupsArrayFind(ra, "notify-time-interval")))
+      ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
+                   "notify-time-interval", sub->interval);
+
+    if (sub->user_data_len > 0 &&
+       (!exclude || !cupsArrayFind(exclude, "notify-user-data")) &&
+        (!ra || cupsArrayFind(ra, "notify-user-data")))
+      ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
+                       sub->user_data, sub->user_data_len);
   }
 
   if (sub->job && (!ra || cupsArrayFind(ra, "notify-job-id")))
     ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
                   "notify-job-id", sub->job->id);
 
-  if (!sub->job && (!ra || cupsArrayFind(ra, "notify-lease-duration")))
-    ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
-                  "notify-lease-duration", sub->lease);
-
   if (sub->dest && (!ra || cupsArrayFind(ra, "notify-printer-uri")))
   {
     httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
@@ -5946,28 +6318,9 @@ copy_subscription_attrs(
                 "notify-printer-uri", NULL, printer_uri);
   }
 
-  if (sub->recipient && (!ra || cupsArrayFind(ra, "notify-recipient-uri")))
-    ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
-                "notify-recipient-uri", NULL, sub->recipient);
-  else if (!ra || cupsArrayFind(ra, "notify-pull-method"))
-    ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
-                 "notify-pull-method", NULL, "ippget");
-
-  if (!ra || cupsArrayFind(ra, "notify-subscriber-user-name"))
-    ippAddString(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_NAME,
-                "notify-subscriber-user-name", NULL, sub->owner);
-
   if (!ra || cupsArrayFind(ra, "notify-subscription-id"))
     ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
                   "notify-subscription-id", sub->id);
-
-  if (!ra || cupsArrayFind(ra, "notify-time-interval"))
-    ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
-                  "notify-time-interval", sub->interval);
-
-  if (sub->user_data_len > 0 && (!ra || cupsArrayFind(ra, "notify-user-data")))
-    ippAddOctetString(con->response, IPP_TAG_SUBSCRIPTION, "notify-user-data",
-                      sub->user_data, sub->user_data_len);
 }
 
 
@@ -5997,7 +6350,7 @@ create_job(cupsd_client_t  *con,  /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -6300,7 +6653,7 @@ create_subscription(
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -6341,7 +6694,7 @@ create_subscription(
   if (!attr)
   {
     send_ipp_status(con, IPP_BAD_REQUEST,
-                    _("No subscription attributes in request"));
+                    _("No subscription attributes in request."));
     return;
   }
 
@@ -6399,7 +6752,7 @@ create_subscription(
                            resource, sizeof(resource)) < HTTP_URI_OK)
         {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("Bad notify-recipient-uri URI \"%s\""), recipient);
+                         _("Bad notify-recipient-uri \"%s\"."), recipient);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_URI_SCHEME);
          return;
@@ -6411,7 +6764,7 @@ create_subscription(
        {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
                          _("notify-recipient-uri URI \"%s\" uses unknown "
-                           "scheme"), recipient);
+                           "scheme."), recipient);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_URI_SCHEME);
          return;
@@ -6420,7 +6773,7 @@ create_subscription(
         if (!strcmp(scheme, "rss") && !check_rss_recipient(recipient))
        {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("notify-recipient-uri URI \"%s\" is already used"),
+                         _("notify-recipient-uri URI \"%s\" is already used."),
                          recipient);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_ATTRIBUTES);
@@ -6435,7 +6788,7 @@ create_subscription(
         if (strcmp(pullmethod, "ippget"))
        {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("Bad notify-pull-method \"%s\""), pullmethod);
+                         _("Bad notify-pull-method \"%s\"."), pullmethod);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_ATTRIBUTES);
          return;
@@ -6447,7 +6800,7 @@ create_subscription(
               strcmp(attr->values[0].string.text, "utf-8"))
       {
         send_ipp_status(con, IPP_CHARSET,
-                       _("Character set \"%s\" not supported"),
+                       _("Character set \"%s\" not supported."),
                        attr->values[0].string.text);
        return;
       }
@@ -6456,7 +6809,7 @@ create_subscription(
                strcmp(attr->values[0].string.text, DefaultLanguage)))
       {
         send_ipp_status(con, IPP_CHARSET,
-                       _("Language \"%s\" not supported"),
+                       _("Language \"%s\" not supported."),
                        attr->values[0].string.text);
        return;
       }
@@ -6467,7 +6820,7 @@ create_subscription(
        {
           send_ipp_status(con, IPP_REQUEST_VALUE,
                          _("The notify-user-data value is too large "
-                           "(%d > 63 octets)"),
+                           "(%d > 63 octets)."),
                          attr->values[0].unknown.length);
          return;
        }
@@ -6515,7 +6868,7 @@ create_subscription(
       else
       {
         send_ipp_status(con, IPP_BAD_REQUEST,
-                       _("notify-events not specified"));
+                       _("notify-events not specified."));
        return;
       }
     }
@@ -6533,7 +6886,8 @@ create_subscription(
     {
       if ((job = cupsdFindJob(jobid)) == NULL)
       {
-       send_ipp_status(con, IPP_NOT_FOUND, _("Job %d not found"), jobid);
+       send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
+                       jobid);
        return;
       }
     }
@@ -6548,14 +6902,14 @@ create_subscription(
     }
 
     if (job)
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for job %d",
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for job %d.",
                      sub->id, job->id);
     else if (printer)
       cupsdLogMessage(CUPSD_LOG_DEBUG,
-                      "Added subscription %d for printer \"%s\"",
+                      "Added subscription #%d for printer \"%s\".",
                      sub->id, printer->name);
     else
-      cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription %d for server",
+      cupsdLogMessage(CUPSD_LOG_DEBUG, "Added subscription #%d for server.",
                      sub->id);
 
     sub->interval = interval;
@@ -6613,7 +6967,7 @@ delete_printer(cupsd_client_t  *con,      /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -6656,10 +7010,10 @@ delete_printer(cupsd_client_t  *con,    /* I - Client connection */
            printer->name);
   unlink(filename);
 
-  snprintf(filename, sizeof(filename), "%s/%s.ipp3", CacheDir, printer->name);
+  snprintf(filename, sizeof(filename), "%s/%s.png", CacheDir, printer->name);
   unlink(filename);
 
-  snprintf(filename, sizeof(filename), "%s/%s.pwg2", CacheDir, printer->name);
+  snprintf(filename, sizeof(filename), "%s/%s.data", CacheDir, printer->name);
   unlink(filename);
 
 #ifdef __APPLE__
@@ -6733,7 +7087,7 @@ get_default(cupsd_client_t *con)  /* I - Client connection */
     con->response->request.status.status_code = IPP_OK;
   }
   else
-    send_ipp_status(con, IPP_NOT_FOUND, _("No default printer"));
+    send_ipp_status(con, IPP_NOT_FOUND, _("No default printer."));
 }
 
 
@@ -6870,7 +7224,7 @@ get_document(cupsd_client_t  *con,        /* I - Client connection */
                                  IPP_TAG_INTEGER)) == NULL)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Got a printer-uri attribute but no job-id"));
+                      _("Got a printer-uri attribute but no job-id."));
       return;
     }
 
@@ -6892,8 +7246,7 @@ get_document(cupsd_client_t  *con,        /* I - Client connection */
       * Not a valid URI!
       */
 
-      send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad job-uri attribute \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -6911,7 +7264,7 @@ get_document(cupsd_client_t  *con,        /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
     return;
   }
 
@@ -6919,7 +7272,8 @@ get_document(cupsd_client_t  *con,        /* I - Client connection */
   * Check policy...
   */
 
-  if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+  if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con,
+                                 job->username)) != HTTP_OK)
   {
     send_http_error(con, status, NULL);
     return;
@@ -6933,15 +7287,16 @@ get_document(cupsd_client_t  *con,      /* I - Client connection */
                                IPP_TAG_INTEGER)) == NULL)
   {
     send_ipp_status(con, IPP_BAD_REQUEST,
-                    _("Missing document-number attribute"));
+                    _("Missing document-number attribute."));
     return;
   }
 
   if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
       attr->num_values > 1)
   {
-    send_ipp_status(con, IPP_NOT_FOUND, _("Document %d not found in job %d."),
-                    docnum, jobid);
+    send_ipp_status(con, IPP_NOT_FOUND,
+                    _("Document #%d does not exist in job #%d."), docnum,
+                   jobid);
     return;
   }
 
@@ -6953,7 +7308,8 @@ get_document(cupsd_client_t  *con,        /* I - Client connection */
                     "Unable to open document %d in job %d - %s", docnum, jobid,
                    strerror(errno));
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("Unable to open document %d in job %d"), docnum, jobid);
+                    _("Unable to open document #%d in job #%d."), docnum,
+                   jobid);
     return;
   }
 
@@ -6988,12 +7344,14 @@ get_job_attrs(cupsd_client_t  *con,     /* I - Client connection */
   int          jobid;                  /* Job ID */
   cupsd_job_t  *job;                   /* Current job */
   cupsd_printer_t *printer;            /* Current printer */
-  char         scheme[HTTP_MAX_URI],   /* Method portion of URI */
+  cupsd_policy_t *policy;              /* Current security policy */
+  char         scheme[HTTP_MAX_URI],   /* Scheme portion of URI */
                username[HTTP_MAX_URI], /* Username portion of URI */
                host[HTTP_MAX_URI],     /* Host portion of URI */
                resource[HTTP_MAX_URI]; /* Resource portion of URI */
   int          port;                   /* Port portion of URI */
-  cups_array_t *ra;                    /* Requested attributes array */
+  cups_array_t *ra,                    /* Requested attributes array */
+               *exclude;               /* Private attributes array */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_job_attrs(%p[%d], %s)", con,
@@ -7013,7 +7371,7 @@ get_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
                                  IPP_TAG_INTEGER)) == NULL)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Got a printer-uri attribute but no job-id"));
+                      _("Got a printer-uri attribute but no job-id."));
       return;
     }
 
@@ -7035,8 +7393,7 @@ get_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
       * Not a valid URI!
       */
 
-      send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad job-uri attribute \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -7054,7 +7411,7 @@ get_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
     return;
   }
 
@@ -7066,20 +7423,18 @@ get_job_attrs(cupsd_client_t  *con,     /* I - Client connection */
     printer = cupsdFindDest(job->dest);
 
   if (printer)
-  {
-    if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
-                                   NULL)) != HTTP_OK)
-    {
-      send_http_error(con, status, printer);
-      return;
-    }
-  }
-  else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+    policy = printer->op_policy_ptr;
+  else
+    policy = DefaultPolicyPtr;
+
+  if ((status = cupsdCheckPolicy(policy, con, job->username)) != HTTP_OK)
   {
     send_http_error(con, status, NULL);
     return;
   }
 
+  exclude = cupsdGetPrivateAttrs(policy, con, printer, job->username);
+
  /*
   * Copy attributes...
   */
@@ -7087,7 +7442,7 @@ get_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
   cupsdLoadJob(job);
 
   ra = create_requested_array(con->request);
-  copy_job_attrs(con, job, ra);
+  copy_job_attrs(con, job, ra, exclude);
   cupsArrayDelete(ra);
 
   con->response->request.status.status_code = IPP_OK;
@@ -7121,7 +7476,9 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
   cupsd_job_t  *job;                   /* Current job pointer */
   cupsd_printer_t *printer;            /* Printer */
   cups_array_t *list;                  /* Which job list... */
-  cups_array_t *ra;                    /* Requested attributes array */
+  cups_array_t *ra,                    /* Requested attributes array */
+               *exclude;               /* Private attributes array */
+  cupsd_policy_t *policy;              /* Current policy */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs(%p[%d], %s)", con, con->http.fd,
@@ -7133,7 +7490,7 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
 
   if (strcmp(uri->name, "printer-uri"))
   {
-    send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request"));
+    send_ipp_status(con, IPP_BAD_REQUEST, _("No printer-uri in request."));
     return;
   }
 
@@ -7170,7 +7527,7 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
   else
@@ -7184,15 +7541,11 @@ get_jobs(cupsd_client_t  *con,          /* I - Client connection */
   */
 
   if (printer)
-  {
-    if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
-                                   NULL)) != HTTP_OK)
-    {
-      send_http_error(con, status, printer);
-      return;
-    }
-  }
-  else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+    policy = printer->op_policy_ptr;
+  else
+    policy = DefaultPolicyPtr;
+
+  if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
   {
     send_http_error(con, status, NULL);
     return;
@@ -7358,7 +7711,7 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
 
     if (i < job_ids->num_values)
     {
-      send_ipp_status(con, IPP_NOT_FOUND, _("job-id %d not found."),
+      send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
                       job_ids->values[i].integer);
       return;
     }
@@ -7379,7 +7732,12 @@ get_jobs(cupsd_client_t  *con,           /* I - Client connection */
       if (i > 0)
        ippAddSeparator(con->response);
 
-      copy_job_attrs(con, job, ra);
+      exclude = cupsdGetPrivateAttrs(job->printer ?
+                                         job->printer->op_policy_ptr :
+                                        policy, con, job->printer,
+                                        job->username);
+
+      copy_job_attrs(con, job, ra, exclude);
     }
   }
   else
@@ -7435,7 +7793,12 @@ get_jobs(cupsd_client_t  *con,           /* I - Client connection */
 
       count ++;
 
-      copy_job_attrs(con, job, ra);
+      exclude = cupsdGetPrivateAttrs(job->printer ?
+                                         job->printer->op_policy_ptr :
+                                        policy, con, job->printer,
+                                        job->username);
+
+      copy_job_attrs(con, job, ra, exclude);
     }
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: count=%d", count);
@@ -7478,7 +7841,7 @@ get_notifications(cupsd_client_t *con)    /* I - Client connection */
   if (!ids)
   {
     send_ipp_status(con, IPP_BAD_REQUEST,
-                    _("Missing notify-subscription-ids attribute"));
+                    _("Missing notify-subscription-ids attribute."));
     return;
   }
 
@@ -7494,8 +7857,7 @@ get_notifications(cupsd_client_t *con)    /* I - Client connection */
       * Bad subscription ID...
       */
 
-      send_ipp_status(con, IPP_NOT_FOUND,
-                      _("notify-subscription-id %d no good"),
+      send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
                      ids->values[i].integer);
       return;
     }
@@ -7561,7 +7923,7 @@ get_notifications(cupsd_client_t *con)    /* I - Client connection */
     * If we don't have any new events, nothing to do here...
     */
 
-    if (min_seq > (sub->first_event_id + sub->num_events))
+    if (min_seq > (sub->first_event_id + cupsArrayCount(sub->events)))
       continue;
 
    /*
@@ -7573,12 +7935,13 @@ get_notifications(cupsd_client_t *con)  /* I - Client connection */
     else
       j = min_seq - sub->first_event_id;
 
-    for (; j < sub->num_events; j ++)
+    for (; j < cupsArrayCount(sub->events); j ++)
     {
       ippAddSeparator(con->response);
 
-      copy_attrs(con->response, sub->events[j]->attrs, NULL,
-                IPP_TAG_EVENT_NOTIFICATION, 0);
+      copy_attrs(con->response,
+                 ((cupsd_event_t *)cupsArrayIndex(sub->events, j))->attrs, NULL,
+                IPP_TAG_EVENT_NOTIFICATION, 0, NULL);
     }
   }
 }
@@ -7930,7 +8293,7 @@ get_printer_attrs(cupsd_client_t  *con,   /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -7986,7 +8349,7 @@ get_printer_supported(
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -8146,7 +8509,8 @@ get_printers(cupsd_client_t *con, /* I - Client connection */
       * access...
       */
 
-      if (printer->num_users && username && !user_allowed(printer, username))
+      if (cupsArrayCount(printer->users) && username &&
+         !user_allowed(printer, username))
         continue;
 
      /*
@@ -8183,7 +8547,9 @@ get_subscription_attrs(
 {
   http_status_t                status;         /* Policy status */
   cupsd_subscription_t *sub;           /* Subscription */
-  cups_array_t         *ra;            /* Requested attributes array */
+  cupsd_policy_t       *policy;        /* Current security policy */
+  cups_array_t         *ra,            /* Requested attributes array */
+                       *exclude;       /* Private attributes array */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
@@ -8200,8 +8566,8 @@ get_subscription_attrs(
     * Bad subscription ID...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND,
-                    _("notify-subscription-id %d no good"), sub_id);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
+                    sub_id);
     return;
   }
 
@@ -8209,14 +8575,19 @@ get_subscription_attrs(
   * Check policy...
   */
 
-  if ((status = cupsdCheckPolicy(sub->dest ? sub->dest->op_policy_ptr :
-                                             DefaultPolicyPtr,
-                                 con, sub->owner)) != HTTP_OK)
+  if (sub->dest)
+    policy = sub->dest->op_policy_ptr;
+  else
+    policy = DefaultPolicyPtr;
+
+  if ((status = cupsdCheckPolicy(policy, con, sub->owner)) != HTTP_OK)
   {
     send_http_error(con, status, sub->dest);
     return;
   }
 
+  exclude = cupsdGetPrivateAttrs(policy, con, sub->dest, sub->owner);
+
  /*
   * Copy the subscription attributes to the response using the
   * requested-attributes attribute that may be provided by the client.
@@ -8224,7 +8595,7 @@ get_subscription_attrs(
 
   ra = create_requested_array(con->request);
 
-  copy_subscription_attrs(con, sub, ra);
+  copy_subscription_attrs(con, sub, ra, exclude);
 
   cupsArrayDelete(ra);
 
@@ -8258,6 +8629,8 @@ get_subscriptions(cupsd_client_t  *con,   /* I - Client connection */
   int                  port;           /* Port portion of URI */
   cupsd_job_t          *job;           /* Job pointer */
   cupsd_printer_t      *printer;       /* Printer */
+  cupsd_policy_t       *policy;        /* Policy */
+  cups_array_t         *exclude;       /* Private attributes array */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2,
@@ -8287,8 +8660,8 @@ get_subscriptions(cupsd_client_t  *con,   /* I - Client connection */
 
     if (!job)
     {
-      send_ipp_status(con, IPP_NOT_FOUND, _("Job #%s does not exist"),
-                      resource + 6);
+      send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
+                      atoi(resource + 6));
       return;
     }
   }
@@ -8299,7 +8672,7 @@ get_subscriptions(cupsd_client_t  *con,   /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
   else if ((attr = ippFindAttribute(con->request, "notify-job-id",
@@ -8309,7 +8682,7 @@ get_subscriptions(cupsd_client_t  *con,   /* I - Client connection */
 
     if (!job)
     {
-      send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"),
+      send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."),
                       attr->values[0].integer);
       return;
     }
@@ -8321,9 +8694,12 @@ get_subscriptions(cupsd_client_t  *con,  /* I - Client connection */
   * Check policy...
   */
 
-  if ((status = cupsdCheckPolicy(printer ? printer->op_policy_ptr :
-                                           DefaultPolicyPtr,
-                                 con, NULL)) != HTTP_OK)
+  if (printer)
+    policy = printer->op_policy_ptr;
+  else
+    policy = DefaultPolicyPtr;
+
+  if ((status = cupsdCheckPolicy(policy, con, NULL)) != HTTP_OK)
   {
     send_http_error(con, status, printer);
     return;
@@ -8360,7 +8736,12 @@ get_subscriptions(cupsd_client_t  *con,  /* I - Client connection */
         (!username[0] || !strcasecmp(username, sub->owner)))
     {
       ippAddSeparator(con->response);
-      copy_subscription_attrs(con, sub, ra);
+
+      exclude = cupsdGetPrivateAttrs(sub->dest ? sub->dest->op_policy_ptr :
+                                                policy, con, sub->dest,
+                                                sub->owner);
+
+      copy_subscription_attrs(con, sub, ra, exclude);
 
       count ++;
       if (limit && count >= limit)
@@ -8432,7 +8813,7 @@ hold_job(cupsd_client_t  *con,            /* I - Client connection */
                                  IPP_TAG_INTEGER)) == NULL)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Got a printer-uri attribute but no job-id"));
+                      _("Got a printer-uri attribute but no job-id."));
       return;
     }
 
@@ -8455,7 +8836,7 @@ hold_job(cupsd_client_t  *con,            /* I - Client connection */
       */
 
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad job-uri attribute \"%s\""),
+                      _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -8473,7 +8854,7 @@ hold_job(cupsd_client_t  *con,            /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
     return;
   }
 
@@ -8541,7 +8922,7 @@ hold_new_jobs(cupsd_client_t  *con,       /* I - Connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -8562,7 +8943,6 @@ hold_new_jobs(cupsd_client_t  *con,       /* I - Connection */
   printer->holding_new_jobs = 1;
 
   cupsdSetPrinterReasons(printer, "+hold-new-jobs");
-  cupsdAddPrinterHistory(printer);
 
   if (dtype & CUPS_PRINTER_CLASS)
     cupsdLogMessage(CUPSD_LOG_INFO,
@@ -8620,7 +9000,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_BAD_REQUEST,
-                    _("job-printer-uri attribute missing"));
+                    _("job-printer-uri attribute missing."));
     return;
   }
 
@@ -8631,7 +9011,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -8664,7 +9044,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
        */
 
        send_ipp_status(con, IPP_NOT_FOUND,
-                       _("The printer or class was not found."));
+                       _("The printer or class does not exist."));
        return;
       }
 
@@ -8683,7 +9063,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
        */
 
        send_ipp_status(con, IPP_NOT_FOUND,
-                       _("Job #%d does not exist"), attr->values[0].integer);
+                       _("Job #%d does not exist."), attr->values[0].integer);
        return;
       }
       else
@@ -8709,8 +9089,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
       * Not a valid URI!
       */
 
-      send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad job-uri attribute \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -8727,8 +9106,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
       * Nope - return a "not found" error...
       */
 
-      send_ipp_status(con, IPP_NOT_FOUND,
-                      _("Job #%d does not exist"), jobid);
+      send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
       return;
     }
     else
@@ -8770,7 +9148,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
       */
 
       send_ipp_status(con, IPP_NOT_POSSIBLE,
-                      _("Job #%d is finished and cannot be altered"),
+                      _("Job #%d is finished and cannot be altered."),
                      job->id);
       return;
     }
@@ -8955,7 +9333,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
       )
     {
       send_ipp_status(con, IPP_ATTRIBUTES,
-                      _("Unsupported compression \"%s\""),
+                      _("Unsupported compression \"%s\"."),
                      attr->values[0].string.text);
       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
                   "compression", NULL, attr->values[0].string.text);
@@ -8974,7 +9352,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
 
   if (!con->filename)
   {
-    send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?"));
+    send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
     return;
   }
 
@@ -8989,7 +9367,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -9008,7 +9386,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
                type) != 2)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad document-format \"%s\""),
+                      _("Bad document-format \"%s\"."),
                      format->values[0].string.text);
       return;
     }
@@ -9024,7 +9402,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
     if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad document-format \"%s\""),
+                      _("Bad document-format \"%s\"."),
                      default_format);
       return;
     }
@@ -9089,7 +9467,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
   else if (!filetype)
   {
     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
-                    _("Unsupported document-format \"%s\""),
+                    _("Unsupported document-format \"%s\"."),
                    format ? format->values[0].string.text :
                             "application/octet-stream");
     cupsdLogMessage(CUPSD_LOG_INFO,
@@ -9382,7 +9760,7 @@ reject_jobs(cupsd_client_t  *con, /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -9409,8 +9787,6 @@ reject_jobs(cupsd_client_t  *con, /* I - Client connection */
     strlcpy(printer->state_message, attr->values[0].string.text,
             sizeof(printer->state_message));
 
-  cupsdAddPrinterHistory(printer);
-
   cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, printer, NULL,
                 "No longer accepting jobs.");
 
@@ -9465,7 +9841,7 @@ release_held_new_jobs(
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -9486,7 +9862,6 @@ release_held_new_jobs(
   printer->holding_new_jobs = 0;
 
   cupsdSetPrinterReasons(printer, "-hold-new-jobs");
-  cupsdAddPrinterHistory(printer);
 
   if (dtype & CUPS_PRINTER_CLASS)
     cupsdLogMessage(CUPSD_LOG_INFO,
@@ -9540,7 +9915,7 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
                                  IPP_TAG_INTEGER)) == NULL)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Got a printer-uri attribute but no job-id"));
+                      _("Got a printer-uri attribute but no job-id."));
       return;
     }
 
@@ -9562,8 +9937,7 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
       * Not a valid URI!
       */
 
-      send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad job-uri attribute \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -9581,7 +9955,7 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
     return;
   }
 
@@ -9595,7 +9969,7 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
     * Nope - return a "not possible" error...
     */
 
-    send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held"), jobid);
+    send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not held."), jobid);
     return;
   }
 
@@ -9674,8 +10048,8 @@ renew_subscription(
     * Bad subscription ID...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND,
-                    _("notify-subscription-id %d no good"), sub_id);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Subscription #%d does not exist."),
+                    sub_id);
     return;
   }
 
@@ -9686,7 +10060,7 @@ renew_subscription(
     */
 
     send_ipp_status(con, IPP_NOT_POSSIBLE,
-                    _("Job subscriptions cannot be renewed"));
+                    _("Job subscriptions cannot be renewed."));
     return;
   }
 
@@ -9766,7 +10140,7 @@ restart_job(cupsd_client_t  *con,        /* I - Client connection */
                                  IPP_TAG_INTEGER)) == NULL)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Got a printer-uri attribute but no job-id"));
+                      _("Got a printer-uri attribute but no job-id."));
       return;
     }
 
@@ -9788,8 +10162,7 @@ restart_job(cupsd_client_t  *con,        /* I - Client connection */
       * Not a valid URI!
       */
 
-      send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad job-uri attribute \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -9807,7 +10180,7 @@ restart_job(cupsd_client_t  *con,        /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
     return;
   }
 
@@ -9821,7 +10194,7 @@ restart_job(cupsd_client_t  *con,        /* I - Client connection */
     * Nope - return a "not possible" error...
     */
 
-    send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete"),
+    send_ipp_status(con, IPP_NOT_POSSIBLE, _("Job #%d is not complete."),
                     jobid);
     return;
   }
@@ -9839,7 +10212,7 @@ restart_job(cupsd_client_t  *con,        /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_POSSIBLE,
-                    _("Job #%d cannot be restarted - no files"), jobid);
+                    _("Job #%d cannot be restarted - no files."), jobid);
     return;
   }
 
@@ -10121,7 +10494,7 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
                                  IPP_TAG_INTEGER)) == NULL)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Got a printer-uri attribute but no job-id"));
+                      _("Got a printer-uri attribute but no job-id."));
       return;
     }
 
@@ -10143,8 +10516,7 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
       * Not a valid URI!
       */
 
-      send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad job-uri attribute \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -10162,7 +10534,7 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
     return;
   }
 
@@ -10195,7 +10567,7 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
 #endif /* HAVE_LIBZ */
       )
     {
-      send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\""),
+      send_ipp_status(con, IPP_ATTRIBUTES, _("Unsupported compression \"%s\"."),
                      attr->values[0].string.text);
       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
                   "compression", NULL, attr->values[0].string.text);
@@ -10225,7 +10597,7 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
         attr->values[0].boolean)
       goto last_document;
 
-    send_ipp_status(con, IPP_BAD_REQUEST, _("No file!?"));
+    send_ipp_status(con, IPP_BAD_REQUEST, _("No file in print request."));
     return;
   }
 
@@ -10243,7 +10615,7 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
     if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
                super, type) != 2)
     {
-      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
                      format->values[0].string.text);
       return;
     }
@@ -10259,8 +10631,7 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
     if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Could not scan type \"%s\""),
-                     default_format);
+                      _("Bad document-format-default \"%s\"."), default_format);
       return;
     }
   }
@@ -10293,8 +10664,9 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
     if (!filetype)
       filetype = mimeType(MimeDatabase, super, type);
 
-    cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
-               filetype->super, filetype->type);
+    if (filetype)
+      cupsdLogJob(job, CUPSD_LOG_DEBUG, "Request file type is %s/%s.",
+                 filetype->super, filetype->type);
   }
   else
     filetype = mimeType(MimeDatabase, super, type);
@@ -10323,7 +10695,7 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
   else if (!filetype)
   {
     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
-                    _("Unsupported format \'%s/%s\'"), super, type);
+                    _("Unsupported document-format \"%s/%s\"."), super, type);
     cupsdLogMessage(CUPSD_LOG_INFO,
                     "Hint: Do you have the raw file printing rules enabled?");
 
@@ -10340,7 +10712,7 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
              filetype->type);
 
     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
-                    _("Unsupported format \'%s\'"), mimetype);
+                    _("Unsupported document-format \"%s\"."), mimetype);
 
     ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
                  "document-format", NULL, mimetype);
@@ -10619,7 +10991,7 @@ set_default(cupsd_client_t  *con,       /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -10710,7 +11082,7 @@ set_job_attrs(cupsd_client_t  *con,     /* I - Client connection */
                                  IPP_TAG_INTEGER)) == NULL)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Got a printer-uri attribute but no job-id"));
+                      _("Got a printer-uri attribute but no job-id."));
       return;
     }
 
@@ -10732,8 +11104,7 @@ set_job_attrs(cupsd_client_t  *con,     /* I - Client connection */
       * Not a valid URI!
       */
 
-      send_ipp_status(con, IPP_BAD_REQUEST,
-                      _("Bad job-uri attribute \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad job-uri \"%s\"."),
                       uri->values[0].string.text);
       return;
     }
@@ -10751,7 +11122,7 @@ set_job_attrs(cupsd_client_t  *con,     /* I - Client connection */
     * Nope - return a "not found" error...
     */
 
-    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist"), jobid);
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist."), jobid);
     return;
   }
 
@@ -10766,7 +11137,7 @@ set_job_attrs(cupsd_client_t  *con,     /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_POSSIBLE,
-                    _("Job #%d is finished and cannot be altered"), jobid);
+                    _("Job #%d is finished and cannot be altered."), jobid);
     return;
   }
 
@@ -10841,7 +11212,7 @@ set_job_attrs(cupsd_client_t  *con,     /* I - Client connection */
 
       if (attr->value_tag != IPP_TAG_INTEGER)
       {
-       send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value"));
+       send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-priority value."));
 
        if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
           attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
@@ -10871,7 +11242,7 @@ set_job_attrs(cupsd_client_t  *con,     /* I - Client connection */
 
       if (attr->value_tag != IPP_TAG_ENUM)
       {
-       send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value"));
+       send_ipp_status(con, IPP_REQUEST_VALUE, _("Bad job-state value."));
 
        if ((attr2 = copy_attribute(con->response, attr, 0)) != NULL)
           attr2->group_tag = IPP_TAG_UNSUPPORTED_GROUP;
@@ -11075,7 +11446,7 @@ set_printer_attrs(cupsd_client_t  *con, /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -11182,7 +11553,7 @@ set_printer_defaults(
     }
     else if (!strcmp(attr->name, "requesting-user-name-allowed"))
     {
-      cupsdFreePrinterUsers(printer);
+      cupsdFreeStrings(&(printer->users));
 
       printer->deny_users = 0;
 
@@ -11191,12 +11562,12 @@ set_printer_defaults(
           strcmp(attr->values[0].string.text, "all")))
       {
        for (i = 0; i < attr->num_values; i ++)
-         cupsdAddPrinterUser(printer, attr->values[i].string.text);
+         cupsdAddString(&(printer->users), attr->values[i].string.text);
       }
     }
     else if (!strcmp(attr->name, "requesting-user-name-denied"))
     {
-      cupsdFreePrinterUsers(printer);
+      cupsdFreeStrings(&(printer->users));
 
       printer->deny_users = 1;
 
@@ -11205,7 +11576,7 @@ set_printer_defaults(
           strcmp(attr->values[0].string.text, "none")))
       {
        for (i = 0; i < attr->num_values; i ++)
-         cupsdAddPrinterUser(printer, attr->values[i].string.text);
+         cupsdAddString(&(printer->users), attr->values[i].string.text);
       }
     }
     else if (!strcmp(attr->name, "job-quota-period"))
@@ -11385,6 +11756,7 @@ static void
 start_printer(cupsd_client_t  *con,    /* I - Client connection */
               ipp_attribute_t *uri)    /* I - Printer URI */
 {
+  int                  i;              /* Temporary variable */
   http_status_t                status;         /* Policy status */
   cups_ptype_t         dtype;          /* Destination type (printer/class) */
   cupsd_printer_t      *printer;       /* Printer data */
@@ -11404,7 +11776,7 @@ start_printer(cupsd_client_t  *con,     /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -11435,6 +11807,21 @@ start_printer(cupsd_client_t  *con,    /* I - Client connection */
 
   cupsdCheckJobs();
 
+ /*
+  * Check quotas...
+  */
+
+  if ((i = check_quotas(con, printer)) < 0)
+  {
+    send_ipp_status(con, IPP_NOT_POSSIBLE, _("Quota limit reached."));
+    return;
+  }
+  else if (i == 0)
+  {
+    send_ipp_status(con, IPP_NOT_AUTHORIZED, _("Not allowed to print."));
+    return;
+  }
+
  /*
   * Everything was ok, so return OK status...
   */
@@ -11471,7 +11858,7 @@ stop_printer(cupsd_client_t  *con,      /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }
 
@@ -11618,13 +12005,13 @@ static int                            /* O - 0 if not allowed, 1 if allowed */
 user_allowed(cupsd_printer_t *p,       /* I - Printer or class */
              const char      *username)        /* I - Username */
 {
-  int          i;                      /* Looping var */
   struct passwd        *pw;                    /* User password data */
   char         baseuser[256],          /* Base username */
-               *baseptr;               /* Pointer to "@" in base username */
+               *baseptr,               /* Pointer to "@" in base username */
+               *name;                  /* Current user name */
 
 
-  if (p->num_users == 0)
+  if (cupsArrayCount(p->users) == 0)
     return (1);
 
   if (!strcmp(username, "root"))
@@ -11647,31 +12034,33 @@ user_allowed(cupsd_printer_t *p,      /* I - Printer or class */
   pw = getpwnam(username);
   endpwent();
 
-  for (i = 0; i < p->num_users; i ++)
+  for (name = (char *)cupsArrayFirst(p->users);
+       name;
+       name = (char *)cupsArrayNext(p->users))
   {
-    if (p->users[i][0] == '@')
+    if (name[0] == '@')
     {
      /*
       * Check group membership...
       */
 
-      if (cupsdCheckGroup(username, pw, p->users[i] + 1))
+      if (cupsdCheckGroup(username, pw, name + 1))
         break;
     }
-    else if (p->users[i][0] == '#')
+    else if (name[0] == '#')
     {
      /*
       * Check UUID...
       */
 
-      if (cupsdCheckGroup(username, pw, p->users[i]))
+      if (cupsdCheckGroup(username, pw, name))
         break;
     }
-    else if (!strcasecmp(username, p->users[i]))
+    else if (!strcasecmp(username, name))
       break;
   }
 
-  return ((i < p->num_users) != p->deny_users);
+  return ((name != NULL) != p->deny_users);
 }
 
 
@@ -11713,7 +12102,7 @@ validate_job(cupsd_client_t  *con,      /* I - Client connection */
       )
     {
       send_ipp_status(con, IPP_ATTRIBUTES,
-                      _("Unsupported compression \"%s\""),
+                      _("Unsupported compression \"%s\"."),
                      attr->values[0].string.text);
       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_KEYWORD,
                   "compression", NULL, attr->values[0].string.text);
@@ -11731,7 +12120,7 @@ validate_job(cupsd_client_t  *con,      /* I - Client connection */
     if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]",
                super, type) != 2)
     {
-      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\""),
+      send_ipp_status(con, IPP_BAD_REQUEST, _("Bad document-format \"%s\"."),
                      format->values[0].string.text);
       return;
     }
@@ -11742,7 +12131,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 \"%s\"."),
                      format->values[0].string.text);
       ippAddString(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_MIMETYPE,
                    "document-format", NULL, format->values[0].string.text);
@@ -11761,7 +12150,7 @@ validate_job(cupsd_client_t  *con,      /* I - Client connection */
     */
 
     send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
+                    _("The printer or class does not exist."));
     return;
   }