]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/ipp.c
Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / ipp.c
index 28115133d99ac08cc042b29c9a36b46c7b56158a..186d14f3de0731acc57bcb330034c532fd4bf260 100644 (file)
@@ -1,9 +1,12 @@
 /*
- * "$Id: ipp.c 5736 2006-07-13 19:59:36Z mike $"
+ * "$Id: ipp.c 6383 2007-03-21 20:01:20Z mike $"
  *
  *   IPP routines for the Common UNIX Printing System (CUPS) scheduler.
  *
- *   Copyright 1997-2006 by Easy Software Products, all rights reserved.
+ *   Copyright 1997-2007 by Easy Software Products, all rights reserved.
+ *
+ *   This file contains Kerberos support code, copyright 2006 by
+ *   Jelmer Vernooij.
  *
  *   These coded instructions, statements, and computer programs are the
  *   property of Easy Software Products and are protected by Federal
@@ -71,7 +74,6 @@
  *   get_username()              - Get the username associated with a request.
  *   hold_job()                  - Hold a print job.
  *   move_job()                  - Move a job to a new destination.
- *   ppd_add_default()           - Add a PPD default choice.
  *   ppd_parse_line()            - Parse a PPD default line.
  *   print_job()                 - Print a file to a printer or class.
  *   read_ps_line()              - Read a line from a PS file...
@@ -80,6 +82,7 @@
  *   release_job()               - Release a held print job.
  *   restart_job()               - Restart an old print job.
  *   save_auth_info()            - Save authentication information for a job.
+ *   save_krb5_creds()           - Save Kerberos credentials for a job.
  *   send_document()             - Send a file to a printer or class.
  *   send_http_error()           - Send a HTTP error back to the IPP client.
  *   send_ipp_status()           - Send a status back to the IPP client.
 
 #include "cupsd.h"
 
+#ifdef HAVE_KRB5_H
+#  include <krb5.h>
+#endif /* HAVE_KRB5_H */
+
 #ifdef HAVE_LIBPAPER
 #  include <paper.h>
 #endif /* HAVE_LIBPAPER */
 
-
-/*
- * PPD default choice structure...
- */
-
-typedef struct
-{
-  char option[PPD_MAX_NAME];           /* Main keyword (option name) */
-  char choice[PPD_MAX_NAME];           /* Option keyword (choice name) */
-} ppd_default_t;
+#ifdef __APPLE__
+#  ifdef HAVE_MEMBERSHIP_H
+#    include <membership.h>
+#  endif /* HAVE_MEMBERSHIP_H */
+#  ifdef HAVE_MEMBERSHIPPRIV_H
+#    include <membershipPriv.h>
+#  else
+extern int mbr_user_name_to_uuid(const char* name, uuid_t uu);
+extern int mbr_group_name_to_uuid(const char* name, uuid_t uu);
+extern int mbr_check_membership_by_id(uuid_t user, gid_t group, int* ismember);
+#  endif /* HAVE_MEMBERSHIPPRIV_H */
+#endif /* __APPLE__ */
 
 
 /*
@@ -126,8 +135,7 @@ static void accept_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    add_class(cupsd_client_t *con, ipp_attribute_t *uri);
 static int     add_file(cupsd_client_t *con, cupsd_job_t *job,
                         mime_type_t *filetype, int compression);
-static cupsd_job_t *add_job(cupsd_client_t *con, ipp_attribute_t *uri,
-                           cupsd_printer_t **dprinter,
+static cupsd_job_t *add_job(cupsd_client_t *con, cupsd_printer_t *printer,
                            mime_type_t *filetype);
 static void    add_job_state_reasons(cupsd_client_t *con, cupsd_job_t *job);
 static void    add_job_subscriptions(cupsd_client_t *con, cupsd_job_t *job);
@@ -178,8 +186,6 @@ static void get_subscriptions(cupsd_client_t *con, ipp_attribute_t *uri);
 static const char *get_username(cupsd_client_t *con);
 static void    hold_job(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    move_job(cupsd_client_t *con, ipp_attribute_t *uri);
-static int     ppd_add_default(const char *option, const char *choice,
-                               int num_defaults, ppd_default_t **defaults);
 static int     ppd_parse_line(const char *line, char *option, int olen,
                               char *choice, int clen);
 static void    print_job(cupsd_client_t *con, ipp_attribute_t *uri);
@@ -188,9 +194,14 @@ static void        reject_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    release_job(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    renew_subscription(cupsd_client_t *con, int sub_id);
 static void    restart_job(cupsd_client_t *con, ipp_attribute_t *uri);
-static void    save_auth_info(cupsd_client_t *con, cupsd_job_t *job);
+static void    save_auth_info(cupsd_client_t *con, cupsd_job_t *job,
+                              ipp_attribute_t *auth_info);
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
+static void    save_krb5_creds(cupsd_client_t *con, cupsd_job_t *job);
+#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
 static void    send_document(cupsd_client_t *con, ipp_attribute_t *uri);
-static void    send_http_error(cupsd_client_t *con, http_status_t status);
+static void    send_http_error(cupsd_client_t *con, http_status_t status,
+                               cupsd_printer_t *printer);
 static void    send_ipp_status(cupsd_client_t *con, ipp_status_t status,
                                const char *message, ...)
 #    ifdef __GNUC__
@@ -247,7 +258,7 @@ cupsdProcessIPPRequest(
  /*
   * Then validate the request header and required attributes...
   */
-  
+
   if (con->request->request.any.version[0] != 1)
   {
    /*
@@ -264,7 +275,7 @@ cupsdProcessIPPRequest(
                     _("Bad request version number %d.%d!"),
                    con->request->request.any.version[0],
                    con->request->request.any.version[1]);
-  }  
+  }
   else if (!con->request->attrs)
   {
     cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
@@ -398,7 +409,7 @@ cupsdProcessIPPRequest(
        cupsdLogMessage(CUPSD_LOG_DEBUG, "Request attributes follow...");
 
        for (attr = con->request->attrs; attr; attr = attr->next)
-         cupsdLogMessage(CUPSD_LOG_DEBUG, 
+         cupsdLogMessage(CUPSD_LOG_DEBUG,
                          "attr \"%s\": group_tag = %x, value_tag = %x",
                          attr->name ? attr->name : "(null)", attr->group_tag,
                          attr->value_tag);
@@ -625,12 +636,12 @@ cupsdProcessIPPRequest(
                     con->http.fd, con->response->request.status.status_code,
                    ippErrorString(con->response->request.status.status_code));
 
-    if (cupsdSendHeader(con, HTTP_OK, "application/ipp"))
+    if (cupsdSendHeader(con, HTTP_OK, "application/ipp", AUTH_NONE))
     {
 #ifdef CUPSD_USE_CHUNKING
      /*
       * Because older versions of CUPS (1.1.17 and older) and some IPP
-      * clients do not implement chunking properly, we should not use
+      * clients do not implement chunking properly, we cannot use
       * chunking by default.  This may become the default in future
       * CUPS releases, or we might add a configuration directive for
       * it.
@@ -638,30 +649,35 @@ cupsdProcessIPPRequest(
 
       if (con->http.version == HTTP_1_1)
       {
-       con->http.data_encoding = HTTP_ENCODE_CHUNKED;
+       if (httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n") < 0)
+         return (0);
 
-       httpPrintf(HTTP(con), "Transfer-Encoding: chunked\r\n\r\n");
+       if (cupsdFlushHeader(con) < 0)
+         return (0);
+
+       con->http.data_encoding = HTTP_ENCODE_CHUNKED;
       }
       else
 #endif /* CUPSD_USE_CHUNKING */
       {
-       con->http.data_encoding  = HTTP_ENCODE_LENGTH;
-       con->http.data_remaining = ippLength(con->response);
+        size_t length;                 /* Length of response */
 
-        if (con->http.data_remaining < INT_MAX)
-         con->http._data_remaining = con->http.data_remaining;
-       else
-         con->http._data_remaining = INT_MAX;
 
-       httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
-                  CUPS_LLCAST con->http.data_remaining);
-      }
+       length = ippLength(con->response);
+
+       if (httpPrintf(HTTP(con), "Content-Length: " CUPS_LLFMT "\r\n\r\n",
+                      CUPS_LLCAST length) < 0)
+         return (0);
 
-      cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                      "cupsdProcessIPPRequest: Adding fd %d to OutputSet...",
-                     con->http.fd);
+       if (cupsdFlushHeader(con) < 0)
+         return (0);
+
+       con->http.data_encoding  = HTTP_ENCODE_LENGTH;
+       con->http.data_remaining = length;
+      }
 
-      FD_SET(con->http.fd, OutputSet);
+      cupsdAddSelect(con->http.fd, (cupsd_selfunc_t)cupsdReadClient,
+                     (cupsd_selfunc_t)cupsdWriteClient, con);
 
      /*
       * Tell the caller the response header was sent successfully...
@@ -700,12 +716,6 @@ accept_jobs(cupsd_client_t  *con,  /* I - Client connection */
 {
   http_status_t        status;                 /* Policy status */
   cups_ptype_t dtype;                  /* Destination type (printer or class) */
-  char         method[HTTP_MAX_URI],   /* Method 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 */
-  const char   *name;                  /* Printer name */
   cupsd_printer_t *printer;            /* Printer data */
 
 
@@ -716,11 +726,7 @@ accept_jobs(cupsd_client_t  *con,  /* I - Client connection */
   * Is the destination valid?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -737,7 +743,7 @@ accept_jobs(cupsd_client_t  *con,   /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, printer);
     return;
   }
 
@@ -751,12 +757,19 @@ accept_jobs(cupsd_client_t  *con, /* I - Client connection */
   cupsdAddPrinterHistory(printer);
 
   if (dtype & CUPS_PRINTER_CLASS)
+  {
     cupsdSaveAllClasses();
+
+    cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" now accepting jobs (\"%s\").",
+                    printer->name, get_username(con));
+  }
   else
+  {
     cupsdSaveAllPrinters();
 
-  cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" now accepting jobs (\"%s\").", name,
-                  get_username(con));
+    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" now accepting jobs (\"%s\").",
+                    printer->name, get_username(con));
+  }
 
  /*
   * Everything was ok, so return OK status...
@@ -784,7 +797,6 @@ add_class(cupsd_client_t  *con,             /* I - Client connection */
   cupsd_printer_t *pclass,             /* Class */
                *member;                /* Member printer/class */
   cups_ptype_t dtype;                  /* Destination type */
-  const char   *dest;                  /* Printer or class name */
   ipp_attribute_t *attr;               /* Printer attribute */
   int          modify;                 /* Non-zero if we just modified */
   char         newname[IPP_MAX_NAME];  /* New class name */
@@ -837,7 +849,7 @@ add_class(cupsd_client_t  *con,             /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -939,7 +951,7 @@ add_class(cupsd_client_t  *con,             /* I - Client connection */
                                IPP_TAG_BOOLEAN)) != NULL)
   {
     if (pclass->shared && !attr->values[0].boolean)
-      cupsdSendBrowseDelete(pclass);
+      cupsdDeregisterPrinter(pclass, 1);
 
     cupsdLogMessage(CUPSD_LOG_INFO,
                     "Setting %s printer-is-shared to %d (was %d.)",
@@ -1003,11 +1015,7 @@ add_class(cupsd_client_t  *con,          /* I - Client connection */
       * Search for the printer or class URI...
       */
 
-      httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[i].string.text, method,
-                      sizeof(method), username, sizeof(username), host,
-                     sizeof(host), &port, resource, sizeof(resource));
-
-      if ((dest = cupsdValidateDest(host, resource, &dtype, &member)) == NULL)
+      if (!cupsdValidateDest(attr->values[i].string.text, &dtype, &member))
       {
        /*
        * Bad URI...
@@ -1120,7 +1128,7 @@ add_file(cupsd_client_t *con,             /* I - Connection to client */
 
   if (!compressions || !filetypes)
   {
-    cupsdCancelJob(job, 1);
+    cupsdCancelJob(job, 1, IPP_JOB_ABORTED);
 
     send_ipp_status(con, IPP_INTERNAL_ERROR,
                     _("Unable to allocate memory for file types!"));
@@ -1144,54 +1152,25 @@ add_file(cupsd_client_t *con,           /* I - Connection to client */
 
 static cupsd_job_t *                   /* O - Job object */
 add_job(cupsd_client_t  *con,          /* I - Client connection */
-        ipp_attribute_t *uri,          /* I - printer-uri */
-       cupsd_printer_t **dprinter,     /* I - Destination printer */
+       cupsd_printer_t *printer,       /* I - Destination printer */
        mime_type_t     *filetype)      /* I - First print file type, if any */
 {
   http_status_t        status;                 /* Policy status */
-  ipp_attribute_t *attr;               /* Current attribute */
-  const char   *dest;                  /* Destination */
-  cups_ptype_t dtype;                  /* Destination type (printer or class) */
+  ipp_attribute_t *attr,               /* Current attribute */
+               *auth_info;             /* auth-info attribute */
   const char   *val;                   /* Default option value */
   int          priority;               /* Job priority */
   char         *title;                 /* Job name/title */
   cupsd_job_t  *job;                   /* Current job */
-  char         job_uri[HTTP_MAX_URI],  /* Job URI */
-               method[HTTP_MAX_URI],   /* Method 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 */
-  cupsd_printer_t *printer;            /* Printer data */
+  char         job_uri[HTTP_MAX_URI];  /* Job URI */
   int          kbytes;                 /* Size of print file */
   int          i;                      /* Looping var */
   int          lowerpagerange;         /* Page range bound */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %s)", con,
-                  con->http.fd, uri->values[0].string.text);
-
- /*
-  * Is the destination valid?
-  */
-
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
-  {
-   /*
-    * Bad URI...
-    */
-
-    send_ipp_status(con, IPP_NOT_FOUND,
-                    _("The printer or class was not found."));
-    return (NULL);
-  }
-
-  if (dprinter)
-    *dprinter = printer;
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job(%p[%d], %p(%s), %p(%s/%s))",
+                  con, con->http.fd, printer, printer->name,
+                 filetype, filetype->super, filetype->type);
 
  /*
   * Check remote printing to non-shared printer...
@@ -1210,16 +1189,31 @@ add_job(cupsd_client_t  *con,           /* I - Client connection */
   * Check policy...
   */
 
+  auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
+
   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, printer);
+    return (NULL);
+  }
+  else if ((printer->type & CUPS_PRINTER_AUTHENTICATED) &&
+           !con->username[0] && !auth_info)
+  {
+    send_http_error(con, HTTP_UNAUTHORIZED, printer);
     return (NULL);
   }
-  else if ((printer->type & CUPS_PRINTER_AUTHENTICATED) && !con->username[0])
+#ifdef HAVE_SSL
+  else if (auth_info && !con->http.tls &&
+           !httpAddrLocalhost(con->http.hostaddr))
   {
-    send_http_error(con, HTTP_UNAUTHORIZED);
+   /*
+    * Require encryption of auth-info over non-local connections...
+    */
+
+    send_http_error(con, HTTP_UPGRADE_REQUIRED, printer);
     return (NULL);
   }
+#endif /* HAVE_SSL */
 
  /*
   * See if the printer is accepting jobs...
@@ -1229,13 +1223,13 @@ add_job(cupsd_client_t  *con,           /* I - Client connection */
   {
     send_ipp_status(con, IPP_NOT_ACCEPTING,
                     _("Destination \"%s\" is not accepting jobs."),
-                    dest);
+                    printer->name);
     return (NULL);
   }
 
  /*
   * Validate job template attributes; for now just document-format,
-  * copies, and page-ranges...
+  * copies, number-up, and page-ranges...
   */
 
   if (filetype && printer->filetypes &&
@@ -1270,12 +1264,30 @@ add_job(cupsd_client_t  *con,           /* I - Client connection */
     }
   }
 
+  if ((attr = ippFindAttribute(con->request, "number-up",
+                               IPP_TAG_INTEGER)) != NULL)
+  {
+    if (attr->values[0].integer != 1 &&
+        attr->values[0].integer != 2 &&
+        attr->values[0].integer != 4 &&
+        attr->values[0].integer != 6 &&
+        attr->values[0].integer != 9 &&
+        attr->values[0].integer != 16)
+    {
+      send_ipp_status(con, IPP_ATTRIBUTES, _("Bad number-up value %d."),
+                      attr->values[0].integer);
+      ippAddInteger(con->response, IPP_TAG_UNSUPPORTED_GROUP, IPP_TAG_INTEGER,
+                   "number-up", attr->values[0].integer);
+      return (NULL);
+    }
+  }
+
   if ((attr = ippFindAttribute(con->request, "page-ranges",
                                IPP_TAG_RANGE)) != NULL)
   {
     for (i = 0, lowerpagerange = 1; i < attr->num_values; i ++)
     {
-      if (attr->values[i].range.lower < lowerpagerange || 
+      if (attr->values[i].range.lower < lowerpagerange ||
          attr->values[i].range.lower > attr->values[i].range.upper)
       {
        send_ipp_status(con, IPP_BAD_REQUEST,
@@ -1296,7 +1308,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
   if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
     cupsdCleanJobs();
 
-  if (cupsArrayCount(Jobs) >= MaxJobs && MaxJobs)
+  if (MaxJobs && cupsArrayCount(Jobs) >= MaxJobs)
   {
     send_ipp_status(con, IPP_NOT_POSSIBLE,
                     _("Too many active jobs."));
@@ -1338,13 +1350,15 @@ 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\"!"), dest);
+                    _("Unable to add job for destination \"%s\"!"),
+                   printer->name);
     return (NULL);
   }
 
-  job->dtype   = dtype;
+  job->dtype   = printer->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_IMPLICIT |
+                                  CUPS_PRINTER_REMOTE);
   job->attrs   = con->request;
-  con->request = NULL;
+  con->request = ippNewRequest(job->attrs->request.op.operation_id);
 
   add_job_uuid(con, job);
   apply_printer_defaults(printer, job);
@@ -1357,8 +1371,6 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
 
     if (attr)
       cupsdSetString(&attr->values[0].string.text, con->username);
-
-    save_auth_info(con, job);
   }
   else if (attr)
   {
@@ -1381,6 +1393,28 @@ add_job(cupsd_client_t  *con,            /* I - Client connection */
     attr->name = _cupsStrAlloc("job-originating-user-name");
   }
 
+  if (con->username[0] || auth_info)
+  {
+    save_auth_info(con, job, auth_info);
+
+   /*
+    * Remove the auth-info attribute from the attribute data...
+    */
+
+    if (auth_info)
+    {
+      if (job->attrs->prev)
+        job->attrs->prev->next = auth_info->next;
+      else
+        job->attrs->attrs = auth_info->next;
+
+      if (job->attrs->last == auth_info)
+        job->attrs->last = job->attrs->prev;
+
+      _ippFreeAttr(auth_info);
+    }
+  }
+
   if ((attr = ippFindAttribute(job->attrs, "job-originating-host-name",
                                IPP_TAG_ZERO)) != NULL)
   {
@@ -1447,7 +1481,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
     * the connection...
     */
 
-    ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME, 
+    ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_NAME,
                 "job-originating-host-name", NULL, con->http.hostname);
   }
 
@@ -1467,7 +1501,7 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
   ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", job->id);
   job->state = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_ENUM,
                              "job-state", IPP_JOB_STOPPED);
-  job->state_value = job->state->values[0].integer;
+  job->state_value = (ipp_jstate_t)job->state->values[0].integer;
   job->sheets = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER,
                               "job-media-sheets-completed", 0);
   ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri", NULL,
@@ -1728,7 +1762,7 @@ add_job_state_reasons(
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "add_job_state_reasons(%p[%d], %d)",
                   con, con->http.fd, job ? job->id : 0);
 
-  switch (job ? job->state_value : IPP_JOB_CANCELLED)
+  switch (job ? job->state_value : IPP_JOB_CANCELED)
   {
     case IPP_JOB_PENDING :
        dest = cupsdFindDest(job->dest);
@@ -1763,7 +1797,7 @@ add_job_state_reasons(
                     "job-state-reasons", NULL, "job-stopped");
         break;
 
-    case IPP_JOB_CANCELLED :
+    case IPP_JOB_CANCELED :
         ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_KEYWORD,
                     "job-state-reasons", NULL, "job-canceled-by-user");
         break;
@@ -1830,7 +1864,7 @@ add_job_subscriptions(
 
     while (attr && attr->group_tag != IPP_TAG_ZERO)
     {
-      if (!strcmp(attr->name, "notify-recipient") &&
+      if (!strcmp(attr->name, "notify-recipient-uri") &&
           attr->value_tag == IPP_TAG_URI)
         recipient = attr->values[0].string.text;
       else if (!strcmp(attr->name, "notify-pull-method") &&
@@ -1961,7 +1995,6 @@ add_job_uuid(cupsd_client_t *con, /* I - Client connection */
              cupsd_job_t    *job)      /* I - Job */
 {
   char                 uuid[1024];     /* job-uuid string */
-  ipp_attribute_t      *attr;          /* job-uuid attribute */
   _cups_md5_state_t    md5state;       /* MD5 state */
   unsigned char                md5sum[16];     /* MD5 digest/sum */
 
@@ -1970,7 +2003,7 @@ add_job_uuid(cupsd_client_t *con, /* I - Client connection */
   * First see if the job already has a job-uuid attribute; if so, return...
   */
 
-  if ((attr = ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI)) != NULL)
+  if (ippFindAttribute(job->attrs, "job-uuid", IPP_TAG_URI))
     return;
 
  /*
@@ -2074,7 +2107,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -2274,7 +2307,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
                                IPP_TAG_BOOLEAN)) != NULL)
   {
     if (printer->shared && !attr->values[0].boolean)
-      cupsdSendBrowseDelete(printer);
+      cupsdDeregisterPrinter(printer, 1);
 
     cupsdLogMessage(CUPSD_LOG_INFO,
                     "Setting %s printer-is-shared to %d (was %d.)",
@@ -2574,7 +2607,7 @@ apply_printer_defaults(
   int          i,                      /* Looping var */
                num_options;            /* Number of default options */
   cups_option_t        *options,               /* Default options */
-               *option;                /* Current option */    
+               *option;                /* Current option */
 
 
  /*
@@ -2608,7 +2641,8 @@ static void
 authenticate_job(cupsd_client_t  *con, /* I - Client connection */
                 ipp_attribute_t *uri)  /* I - Job URI */
 {
-  ipp_attribute_t      *attr;          /* Job-id attribute */
+  ipp_attribute_t      *attr,          /* job-id attribute */
+                       *auth_info;     /* auth-info attribute */
   int                  jobid;          /* Job ID */
   cupsd_job_t          *job;           /* Current job */
   char                 method[HTTP_MAX_URI],
@@ -2660,7 +2694,7 @@ authenticate_job(cupsd_client_t  *con,    /* I - Client connection */
     httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
                     sizeof(method), username, sizeof(username), host,
                    sizeof(host), &port, resource, sizeof(resource));
+
     if (strncmp(resource, "/jobs/", 6))
     {
      /*
@@ -2710,7 +2744,9 @@ authenticate_job(cupsd_client_t  *con,    /* I - Client connection */
   * See if we have already authenticated...
   */
 
-  if (!con->username[0])
+  auth_info = ippFindAttribute(con->request, "auth-info", IPP_TAG_TEXT);
+
+  if (!con->username[0] && !auth_info)
   {
     send_ipp_status(con, IPP_NOT_AUTHORIZED,
                     _("No authentication information provided!"));
@@ -2723,7 +2759,7 @@ authenticate_job(cupsd_client_t  *con,    /* I - Client connection */
 
   if (!validate_user(job, con, job->username, username, sizeof(username)))
   {
-    send_http_error(con, HTTP_UNAUTHORIZED);
+    send_http_error(con, HTTP_UNAUTHORIZED, NULL);
     return;
   }
 
@@ -2731,7 +2767,7 @@ authenticate_job(cupsd_client_t  *con,    /* I - Client connection */
   * Save the authentication information for this job...
   */
 
-  save_auth_info(con, job);
+  save_auth_info(con, job, auth_info);
 
  /*
   * Reset the job-hold-until value to "no-hold"...
@@ -2767,11 +2803,10 @@ cancel_all_jobs(cupsd_client_t  *con,   /* I - Client connection */
                ipp_attribute_t *uri)   /* I - Job or Printer URI */
 {
   http_status_t        status;                 /* Policy status */
-  const char   *dest;                  /* Destination */
   cups_ptype_t dtype;                  /* Destination type */
-  char         method[HTTP_MAX_URI],   /* Method portion of URI */
+  char         scheme[HTTP_MAX_URI],   /* Scheme portion of URI */
                userpass[HTTP_MAX_URI], /* Username portion of URI */
-               host[HTTP_MAX_URI],     /* Host portion of URI */
+               hostname[HTTP_MAX_URI], /* Host portion of URI */
                resource[HTTP_MAX_URI]; /* Resource portion of URI */
   int          port;                   /* Port portion of URI */
   ipp_attribute_t *attr;               /* Attribute in request */
@@ -2830,16 +2865,17 @@ cancel_all_jobs(cupsd_client_t  *con,   /* I - Client connection */
   * And if the destination is valid...
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), userpass, sizeof(userpass), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI?
     */
 
+    httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text,
+                    scheme, sizeof(scheme), userpass, sizeof(userpass),
+                   hostname, sizeof(hostname), &port,
+                   resource, sizeof(resource));
+
     if ((!strncmp(resource, "/printers/", 10) && resource[10]) ||
         (!strncmp(resource, "/classes/", 9) && resource[9]))
     {
@@ -2847,13 +2883,6 @@ cancel_all_jobs(cupsd_client_t  *con,    /* I - Client connection */
                       _("The printer or class was not found."));
       return;
     }
-    else if (strcmp(resource, "/printers/"))
-    {
-      send_ipp_status(con, IPP_NOT_FOUND,
-                      _("The printer-uri \"%s\" is not valid."),
-                     uri->values[0].string.text);
-      return;
-    }
 
    /*
     * Check policy...
@@ -2861,7 +2890,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
 
     if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
     {
-      send_http_error(con, status);
+      send_http_error(con, status, NULL);
       return;
     }
 
@@ -2872,7 +2901,7 @@ cancel_all_jobs(cupsd_client_t  *con,     /* I - Client connection */
     cupsdCancelJobs(NULL, username, purge);
 
     cupsdLogMessage(CUPSD_LOG_INFO, "All jobs were %s by \"%s\".",
-                    purge ? "purged" : "cancelled", get_username(con));
+                    purge ? "purged" : "canceled", get_username(con));
   }
   else
   {
@@ -2880,9 +2909,10 @@ cancel_all_jobs(cupsd_client_t  *con,    /* I - Client connection */
     * Check policy...
     */
 
-    if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
+    if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
+                                   NULL)) != HTTP_OK)
     {
-      send_http_error(con, status);
+      send_http_error(con, status, printer);
       return;
     }
 
@@ -2890,10 +2920,11 @@ cancel_all_jobs(cupsd_client_t  *con,   /* I - Client connection */
     * Cancel all of the jobs on the named printer...
     */
 
-    cupsdCancelJobs(dest, username, purge);
+    cupsdCancelJobs(printer->name, username, purge);
 
     cupsdLogMessage(CUPSD_LOG_INFO, "All jobs on \"%s\" were %s by \"%s\".",
-                    dest, purge ? "purged" : "cancelled", get_username(con));
+                    printer->name, purge ? "purged" : "canceled",
+                   get_username(con));
   }
 
   con->response->request.status.status_code = IPP_OK;
@@ -2910,13 +2941,12 @@ cancel_job(cupsd_client_t  *con,        /* I - Client connection */
 {
   ipp_attribute_t *attr;               /* Current attribute */
   int          jobid;                  /* Job ID */
-  char         method[HTTP_MAX_URI],   /* Method portion of URI */
+  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 */
   cupsd_job_t  *job;                   /* Job information */
-  const char   *dest;                  /* Destination */
   cups_ptype_t dtype;                  /* Destination type (printer or class) */
   cupsd_printer_t *printer;            /* Printer data */
 
@@ -2948,11 +2978,7 @@ cancel_job(cupsd_client_t  *con, /* I - Client connection */
       * Find the current job on the specified printer...
       */
 
-      httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                      sizeof(method), username, sizeof(username), host,
-                     sizeof(host), &port, resource, sizeof(resource));
-
-      if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+      if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
       {
        /*
        * Bad URI...
@@ -2974,12 +3000,12 @@ cancel_job(cupsd_client_t  *con,        /* I - Client connection */
        /*
         * No, see if there are any pending jobs...
        */
-        
+
         for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
             job;
             job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
          if (job->state_value <= IPP_JOB_PROCESSING &&
-             !strcasecmp(job->dest, dest))
+             !strcasecmp(job->dest, printer->name))
            break;
 
        if (job)
@@ -2987,7 +3013,7 @@ cancel_job(cupsd_client_t  *con,  /* I - Client connection */
        else
        {
          send_ipp_status(con, IPP_NOT_POSSIBLE, _("No active jobs on %s!"),
-                         dest);
+                         printer->name);
          return;
        }
       }
@@ -2999,10 +3025,10 @@ cancel_job(cupsd_client_t  *con,        /* I - Client connection */
     * Got a job URI; parse it to get the job ID...
     */
 
-    httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                    sizeof(method), username, sizeof(username), host,
+    httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
+                    sizeof(scheme), username, sizeof(username), host,
                    sizeof(host), &port, resource, sizeof(resource));
+
     if (strncmp(resource, "/jobs/", 6))
     {
      /*
@@ -3038,22 +3064,22 @@ cancel_job(cupsd_client_t  *con,        /* I - Client connection */
 
   if (!validate_user(job, con, job->username, username, sizeof(username)))
   {
-    send_http_error(con, HTTP_UNAUTHORIZED);
+    send_http_error(con, HTTP_UNAUTHORIZED, NULL);
     return;
   }
 
  /*
-  * See if the job is already completed, cancelled, or aborted; if so,
+  * See if the job is already completed, canceled, or aborted; if so,
   * we can't cancel...
   */
 
-  if (job->state_value >= IPP_JOB_CANCELLED)
+  if (job->state_value >= IPP_JOB_CANCELED)
   {
     switch (job->state_value)
     {
-      case IPP_JOB_CANCELLED :
+      case IPP_JOB_CANCELED :
          send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("Job #%d is already cancelled - can\'t cancel."),
+                         _("Job #%d is already canceled - can\'t cancel."),
                          jobid);
           break;
 
@@ -3077,13 +3103,10 @@ cancel_job(cupsd_client_t  *con,        /* I - Client connection */
   * Cancel the job and return...
   */
 
-  cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job,
-                "Job cancelled by \"%s\".", username);
-
-  cupsdCancelJob(job, 0);
+  cupsdCancelJob(job, 0, IPP_JOB_CANCELED);
   cupsdCheckJobs();
 
-  cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was cancelled by \"%s\".", jobid,
+  cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was canceled by \"%s\".", jobid,
                   username);
 
   con->response->request.status.status_code = IPP_OK;
@@ -3130,7 +3153,7 @@ cancel_subscription(
                                              DefaultPolicyPtr,
                                  con, sub->owner)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, sub->dest);
     return;
   }
 
@@ -3155,7 +3178,24 @@ check_quotas(cupsd_client_t  *con,       /* I - Client connection */
   int          i;                      /* Looping var */
   char         username[33];           /* Username */
   cupsd_quota_t        *q;                     /* Quota data */
+#ifdef HAVE_MBR_UID_TO_UUID
+ /*
+  * Use Apple membership APIs which require that all names represent
+  * valid user account or group records accessible by the server.
+  */
+
+  uuid_t       usr_uuid;               /* UUID for job requesting user  */
+  uuid_t       usr2_uuid;              /* UUID for ACL user name entry  */
+  uuid_t       grp_uuid;               /* UUID for ACL group name entry */
+  int          mbr_err;                /* Error from membership function */
+  int          is_member;              /* Is this user a member? */
+#else
+ /*
+  * Use standard POSIX APIs for checking users and groups...
+  */
+
   struct passwd        *pw;                    /* User password data */
+#endif /* HAVE_MBR_UID_TO_UUID */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "check_quotas(%p[%d], %p[%s])",
@@ -3215,8 +3255,34 @@ check_quotas(cupsd_client_t  *con,       /* I - Client connection */
 
   if (p->num_users)
   {
+#ifdef HAVE_MBR_UID_TO_UUID
+   /*
+    * Get UUID for job requesting user...
+    */
+
+    if (mbr_user_name_to_uuid((char *)username, usr_uuid))
+    {
+     /*
+      * Unknown user...
+      */
+
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                     "check_quotas: UUID lookup failed for user \"%s\"",
+                     username);
+      cupsdLogMessage(CUPSD_LOG_INFO,
+                     "Denying user \"%s\" access to printer \"%s\" "
+                     "(unknown user)...",
+                     username, p->name);
+      return (0);
+    }
+#else
+   /*
+    * Get UID and GID of requesting user...
+    */
+
     pw = getpwnam(username);
     endpwent();
+#endif /* HAVE_MBR_UID_TO_UUID */
 
     for (i = 0; i < p->num_users; i ++)
       if (p->users[i][0] == '@')
@@ -3225,11 +3291,86 @@ check_quotas(cupsd_client_t  *con,      /* I - Client connection */
         * Check group membership...
        */
 
+#ifdef HAVE_MBR_UID_TO_UUID
+       if ((mbr_err = mbr_group_name_to_uuid((char *)p->users[i] + 1,
+                                             grp_uuid)) != 0)
+       {
+        /*
+         * Invalid ACL entries are ignored for matching; just record a
+         * warning in the log...
+         */
+
+         cupsdLogMessage(CUPSD_LOG_DEBUG,
+                         "check_quotas: UUID lookup failed for ACL entry "
+                         "\"%s\" (err=%d)", p->users[i], mbr_err);
+         cupsdLogMessage(CUPSD_LOG_WARN,
+                         "Access control entry \"%s\" not a valid group name; "
+                         "entry ignored", p->users[i]);
+       }
+       else
+       {
+         if ((mbr_err = mbr_check_membership(usr_uuid, grp_uuid,
+                                             &is_member)) != 0)
+         {
+          /*
+           * At this point, there should be no errors, but check anyways...
+           */
+
+           cupsdLogMessage(CUPSD_LOG_DEBUG,
+                           "check_quotas: group \"%s\" membership check "
+                           "failed (err=%d)", p->users[i] + 1, mbr_err);
+            is_member = 0;
+         }
+
+         /*
+         * Stop if we found a match...
+         */
+
+         if (is_member)
+           break;
+       }
+#else
         if (cupsdCheckGroup(username, pw, p->users[i] + 1))
          break;
+#endif /* HAVE_MBR_UID_TO_UUID */
+      }
+#ifdef HAVE_MBR_UID_TO_UUID
+      else
+      {
+        if ((mbr_err = mbr_user_name_to_uuid((char *)p->users[i],
+                                            usr2_uuid)) != 0)
+       {
+        /*
+         * Invalid ACL entries are ignored for matching; just record a
+         * warning in the log...
+         */
+
+          cupsdLogMessage(CUPSD_LOG_DEBUG,
+                         "check_quotas: UUID lookup failed for ACL entry "
+                         "\"%s\" (err=%d)", p->users[i], mbr_err);
+          cupsdLogMessage(CUPSD_LOG_WARN,
+                         "Access control entry \"%s\" not a valid user name; "
+                         "entry ignored", p->users[i]);
+       }
+       else
+       {
+         if ((mbr_err = mbr_check_membership(usr_uuid, usr2_uuid,
+                                             &is_member)) != 0)
+          {
+           cupsdLogMessage(CUPSD_LOG_DEBUG,
+                           "check_quotas: User \"%s\" identity check failed "
+                           "(err=%d)", p->users[i], mbr_err);
+           is_member = 0;
+         }
+
+         if (is_member)
+           break;
+       }
       }
+#else
       else if (!strcasecmp(username, p->users[i]))
        break;
+#endif /* HAVE_MBR_UID_TO_UUID */
 
     if ((i < p->num_users) == p->deny_users)
     {
@@ -3244,6 +3385,66 @@ check_quotas(cupsd_client_t  *con,       /* I - Client connection */
   * Check quotas...
   */
 
+#ifdef __APPLE__
+  if (AppleQuotas)
+  {
+   /*
+    * TODO: Define these special page count values as constants!
+    */
+
+    if (q->page_count == -4) /* special case: unlimited user */
+    {
+      cupsdLogMessage(CUPSD_LOG_INFO,
+                      "User \"%s\" request approved for printer %s (%s): " 
+                     "unlimited quota.",
+                     username, p->name, p->info);
+      q->page_count = 0; /* allow user to print */
+      return (1);
+    }
+    else if (q->page_count == -3) /* quota exceeded */
+    {
+      cupsdLogMessage(CUPSD_LOG_INFO,
+                      "User \"%s\" request denied for printer %s (%s): "
+                     "quota limit exceeded.",
+                     username, p->name, p->info);
+      q->page_count = 2; /* force quota exceeded failure */
+      return (0);
+    }
+    else if (q->page_count == -2) /* quota disabled for user */
+    {
+      cupsdLogMessage(CUPSD_LOG_INFO,
+                      "User \"%s\" request denied for printer %s (%s): "
+                     "printing disabled for user.",
+                     username, p->name, p->info);
+      q->page_count = 2; /* force quota exceeded failure */
+      return (0);
+    }
+    else if (q->page_count == -1) /* quota access error */
+    {
+      cupsdLogMessage(CUPSD_LOG_INFO,
+                      "User \"%s\" request denied for printer %s (%s): "
+                     "unable to determine quota limit.",
+                     username, p->name, p->info);
+      q->page_count = 2; /* force quota exceeded failure */
+      return (0);
+    }
+    else if (q->page_count < 0) /* user not found or other error */
+    {
+      cupsdLogMessage(CUPSD_LOG_INFO,
+                      "User \"%s\" request denied for printer %s (%s): "
+                     "user disabled / missing quota.",
+                     username, p->name, p->info);
+      q->page_count = 2; /* force quota exceeded failure */
+      return (0);
+    }
+    else /* page within user limits */
+    {
+      q->page_count = 0; /* allow user to print */
+      return (1);
+    }
+  }
+  else
+#endif /* __APPLE__ */
   if (p->k_limit || p->page_limit)
   {
     if ((q = cupsdUpdateQuota(p, username, 0, 0)) == NULL)
@@ -3462,8 +3663,8 @@ copy_attrs(ipp_t        *to,              /* I - Destination request */
     * Filter attributes as needed...
     */
 
-    if (group != IPP_TAG_ZERO && fromattr->group_tag != group &&
-        fromattr->group_tag != IPP_TAG_ZERO && !fromattr->name)
+    if ((group != IPP_TAG_ZERO && fromattr->group_tag != group &&
+         fromattr->group_tag != IPP_TAG_ZERO) || !fromattr->name)
       continue;
 
     if (!ra || cupsArrayFind(ra, fromattr->name))
@@ -3805,7 +4006,7 @@ copy_model(cupsd_client_t *con,           /* I - Client connection */
            const char     *from,       /* I - Source file */
            const char     *to)         /* I - Destination file */
 {
-  fd_set       *input;                 /* select() input set */
+  fd_set       input;                  /* select() input set */
   struct timeval timeout;              /* select() timeout */
   int          maxfd;                  /* Maximum file descriptor for select() */
   char         tempfile[1024];         /* Temporary PPD file */
@@ -3816,15 +4017,15 @@ copy_model(cupsd_client_t *con,         /* I - Client connection */
                *envp[MAX_ENV];         /* Environment */
   cups_file_t  *src,                   /* Source file */
                *dst;                   /* Destination file */
+  ppd_file_t   *ppd;                   /* PPD file */
   int          bytes,                  /* Bytes from pipe */
                total;                  /* Total bytes from pipe */
-  char         buffer[2048],           /* Copy buffer */
-               *ptr;                   /* Pointer into buffer */
+  char         buffer[2048];           /* Copy buffer */
   int          i;                      /* Looping var */
   char         option[PPD_MAX_NAME],   /* Option name */
                choice[PPD_MAX_NAME];   /* Choice name */
   int          num_defaults;           /* Number of default options */
-  ppd_default_t        *defaults;              /* Default options */
+  cups_option_t        *defaults;              /* Default options */
   char         cups_protocol[PPD_MAX_LINE];
                                        /* cupsProtocol attribute */
   int          have_letter,            /* Have Letter size */
@@ -3858,24 +4059,12 @@ copy_model(cupsd_client_t *con,         /* I - Client connection */
 
   cupsdOpenPipe(temppipe);
 
-  if ((input = calloc(1, SetSize)) == NULL)
-  {
-    close(tempfd);
-    unlink(tempfile);
-
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "copy_model: Unable to allocate %d bytes for select()...",
-                    SetSize);
-    return (-1);
-  }
-
   cupsdLogMessage(CUPSD_LOG_DEBUG,
                   "copy_model: Running \"cups-driverd cat %s\"...", from);
 
   if (!cupsdStartProcess(buffer, argv, envp, -1, temppipe[1], CGIPipes[1],
-                         -1, 0, &temppid))
+                         -1, -1, 0, &temppid))
   {
-    free(input);
     close(tempfd);
     unlink(tempfile);
     return (-1);
@@ -3902,13 +4091,14 @@ copy_model(cupsd_client_t *con,         /* I - Client connection */
 
     bytes = 0;
 
-    FD_SET(temppipe[0], input);
-    FD_SET(CGIPipes[0], input);
+    FD_ZERO(&input);
+    FD_SET(temppipe[0], &input);
+    FD_SET(CGIPipes[0], &input);
 
     timeout.tv_sec  = 30;
     timeout.tv_usec = 0;
 
-    if ((i = select(maxfd, input, NULL, NULL, &timeout)) < 0)
+    if ((i = select(maxfd, &input, NULL, NULL, &timeout)) < 0)
     {
       if (errno == EINTR)
         continue;
@@ -3924,7 +4114,7 @@ copy_model(cupsd_client_t *con,           /* I - Client connection */
       break;
     }
 
-    if (FD_ISSET(temppipe[0], input))
+    if (FD_ISSET(temppipe[0], &input))
     {
      /*
       * Read the PPD file from the pipe, and write it to the PPD file.
@@ -3941,15 +4131,13 @@ copy_model(cupsd_client_t *con,         /* I - Client connection */
        break;
     }
 
-    if (FD_ISSET(CGIPipes[0], input))
+    if (FD_ISSET(CGIPipes[0], &input))
       cupsdUpdateCGI();
   }
 
   close(temppipe[0]);
   close(tempfd);
 
-  free(input);
-
   if (!total)
   {
    /*
@@ -3965,41 +4153,14 @@ copy_model(cupsd_client_t *con,         /* I - Client connection */
   * Read the source file and see what page sizes are supported...
   */
 
-  if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
+  if ((ppd = ppdOpenFile(tempfile)) == NULL)
   {
     unlink(tempfile);
     return (-1);
   }
 
-  have_letter = 0;
-  have_a4     = 0;
-
-  while (cupsFileGets(src, buffer, sizeof(buffer)))
-    if (!strncmp(buffer, "*PageSize ", 10))
-    {
-     /*
-      * Strip UI text and command data from the end of the line...
-      */
-
-      if ((ptr = strchr(buffer + 10, '/')) != NULL)
-        *ptr = '\0';
-      if ((ptr = strchr(buffer + 10, ':')) != NULL)
-        *ptr = '\0';
-
-      for (ptr = buffer + 10; isspace(*ptr); ptr ++);
-
-     /*
-      * Look for Letter and A4 page sizes...
-      */
-
-      if (!strcmp(ptr, "Letter"))
-       have_letter = 1;
-
-      if (!strcmp(ptr, "A4"))
-       have_a4 = 1;
-    }
-
-  cupsFileRewind(src);
+  have_letter = ppdPageSize(ppd, "Letter") != NULL;
+  have_a4     = ppdPageSize(ppd, "A4") != NULL;
 
  /*
   * Open the destination (if possible) and set the default options...
@@ -4024,8 +4185,21 @@ copy_model(cupsd_client_t *con,          /* I - Client connection */
 
         if (!ppd_parse_line(buffer, option, sizeof(option),
                            choice, sizeof(choice)))
-          num_defaults = ppd_add_default(option, choice, num_defaults,
+        {
+         ppd_option_t  *ppdo;          /* PPD option */
+
+
+         /*
+         * Only add the default if the default hasn't already been
+         * set and the choice exists in the new PPD...
+         */
+
+         if (!cupsGetOption(option, num_defaults, defaults) &&
+             (ppdo = ppdFindOption(ppd, option)) != NULL &&
+             ppdFindChoice(ppdo, choice))
+            num_defaults = cupsAddOption(option, choice, num_defaults,
                                         &defaults);
+        }
       }
       else if (!strncmp(buffer, "*cupsProtocol:", 14))
         strlcpy(cups_protocol, buffer, sizeof(cups_protocol));
@@ -4045,14 +4219,14 @@ copy_model(cupsd_client_t *con,         /* I - Client connection */
     if ((!strcmp(system_paper, "Letter") && have_letter) ||
         (!strcmp(system_paper, "A4") && have_a4))
     {
-      num_defaults = ppd_add_default("PageSize", system_paper, 
-                                    num_defaults, &defaults);
-      num_defaults = ppd_add_default("PageRegion", system_paper, 
-                                    num_defaults, &defaults);
-      num_defaults = ppd_add_default("PaperDimension", system_paper, 
-                                    num_defaults, &defaults);
-      num_defaults = ppd_add_default("ImageableArea", system_paper, 
-                                    num_defaults, &defaults);
+      num_defaults = cupsAddOption("PageSize", system_paper,
+                                  num_defaults, &defaults);
+      num_defaults = cupsAddOption("PageRegion", system_paper,
+                                  num_defaults, &defaults);
+      num_defaults = cupsAddOption("PaperDimension", system_paper,
+                                  num_defaults, &defaults);
+      num_defaults = cupsAddOption("ImageableArea", system_paper,
+                                  num_defaults, &defaults);
     }
   }
 #endif /* HAVE_LIBPAPER */
@@ -4071,6 +4245,7 @@ copy_model(cupsd_client_t *con,           /* I - Client connection */
         !strcasecmp(DefaultLanguage, "C") ||
         !strcasecmp(DefaultLanguage, "POSIX") ||
        !strcasecmp(DefaultLanguage, "en") ||
+       !strncasecmp(DefaultLanguage, "en.", 3) ||
        !strncasecmp(DefaultLanguage, "en_US", 5) ||
        !strncasecmp(DefaultLanguage, "en_CA", 5) ||
        !strncasecmp(DefaultLanguage, "fr_CA", 5))
@@ -4081,14 +4256,14 @@ copy_model(cupsd_client_t *con,         /* I - Client connection */
 
       if (have_letter)
       {
-       num_defaults = ppd_add_default("PageSize", "Letter", num_defaults,
-                                       &defaults);
-       num_defaults = ppd_add_default("PageRegion", "Letter", num_defaults,
-                                       &defaults);
-       num_defaults = ppd_add_default("PaperDimension", "Letter", num_defaults,
-                                       &defaults);
-       num_defaults = ppd_add_default("ImageableArea", "Letter", num_defaults,
-                                       &defaults);
+       num_defaults = cupsAddOption("PageSize", "Letter", num_defaults,
+                                     &defaults);
+       num_defaults = cupsAddOption("PageRegion", "Letter", num_defaults,
+                                     &defaults);
+       num_defaults = cupsAddOption("PaperDimension", "Letter", num_defaults,
+                                     &defaults);
+       num_defaults = cupsAddOption("ImageableArea", "Letter", num_defaults,
+                                     &defaults);
       }
     }
     else if (have_a4)
@@ -4097,26 +4272,37 @@ copy_model(cupsd_client_t *con,         /* I - Client connection */
       * The rest default to "a4" size...
       */
 
-      num_defaults = ppd_add_default("PageSize", "A4", num_defaults,
-                                     &defaults);
-      num_defaults = ppd_add_default("PageRegion", "A4", num_defaults,
-                                     &defaults);
-      num_defaults = ppd_add_default("PaperDimension", "A4", num_defaults,
-                                     &defaults);
-      num_defaults = ppd_add_default("ImageableArea", "A4", num_defaults,
-                                     &defaults);
+      num_defaults = cupsAddOption("PageSize", "A4", num_defaults,
+                                   &defaults);
+      num_defaults = cupsAddOption("PageRegion", "A4", num_defaults,
+                                   &defaults);
+      num_defaults = cupsAddOption("PaperDimension", "A4", num_defaults,
+                                   &defaults);
+      num_defaults = cupsAddOption("ImageableArea", "A4", num_defaults,
+                                   &defaults);
     }
   }
 
+  ppdClose(ppd);
+
+ /*
+  * Open the source file for a copy...
+  */
+
+  if ((src = cupsFileOpen(tempfile, "rb")) == NULL)
+  {
+    cupsFreeOptions(num_defaults, defaults);
+    unlink(tempfile);
+    return (-1);
+  }
+
  /*
   * Open the destination file for a copy...
   */
 
   if ((dst = cupsFileOpen(to, "wb")) == NULL)
   {
-    if (num_defaults > 0)
-      free(defaults);
-
+    cupsFreeOptions(num_defaults, defaults);
     cupsFileClose(src);
     unlink(tempfile);
     return (-1);
@@ -4137,17 +4323,17 @@ copy_model(cupsd_client_t *con,         /* I - Client connection */
       if (!ppd_parse_line(buffer, option, sizeof(option),
                          choice, sizeof(choice)))
       {
-        for (i = 0; i < num_defaults; i ++)
-         if (!strcmp(option, defaults[i].option))
-         {
-          /*
-           * Substitute the previous choice...
-           */
+        const char     *val;           /* Default option value */
 
-           snprintf(buffer, sizeof(buffer), "*Default%s: %s", option,
-                    defaults[i].choice);
-           break;
-         }
+
+        if ((val = cupsGetOption(option, num_defaults, defaults)) != NULL)
+       {
+        /*
+         * Substitute the previous choice...
+         */
+
+         snprintf(buffer, sizeof(buffer), "*Default%s: %s", option, val);
+       }
       }
     }
 
@@ -4157,8 +4343,7 @@ copy_model(cupsd_client_t *con,           /* I - Client connection */
   if (cups_protocol[0])
     cupsFilePrintf(dst, "%s\n", cups_protocol);
 
-  if (num_defaults > 0)
-    free(defaults);
+  cupsFreeOptions(num_defaults, defaults);
 
  /*
   * Close both files and return...
@@ -4275,7 +4460,7 @@ copy_printer_attrs(
   if (!ra || cupsArrayFind(ra, "printer-state-change-time"))
     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"))
   {
@@ -4382,7 +4567,7 @@ copy_subscription_attrs(
       */
 
       ippAddString(con->response, IPP_TAG_SUBSCRIPTION,
-                   IPP_TAG_KEYWORD | IPP_TAG_COPY,
+                   (ipp_tag_t)(IPP_TAG_KEYWORD | IPP_TAG_COPY),
                    "notify-events", NULL, name);
     }
     else
@@ -4396,7 +4581,7 @@ copy_subscription_attrs(
           count ++;
 
       attr = ippAddStrings(con->response, IPP_TAG_SUBSCRIPTION,
-                           IPP_TAG_KEYWORD | IPP_TAG_COPY,
+                           (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)
@@ -4460,23 +4645,39 @@ static void
 create_job(cupsd_client_t  *con,       /* I - Client connection */
           ipp_attribute_t *uri)        /* I - Printer URI */
 {
-  cupsd_job_t  *job;                   /* New job */
+  cupsd_printer_t      *printer;       /* Printer */
+  cupsd_job_t          *job;           /* New job */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "create_job(%p[%d], %s)", con,
                   con->http.fd, uri->values[0].string.text);
 
+ /*
+  * Is the destination valid?
+  */
+
+  if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
+  {
+   /*
+    * Bad URI...
+    */
+
+    send_ipp_status(con, IPP_NOT_FOUND,
+                    _("The printer or class was not found."));
+    return;
+  }
+
  /*
   * Create the job object...
   */
 
-  if ((job = add_job(con, uri, NULL, NULL)) == NULL)
+  if ((job = add_job(con, printer, NULL)) == NULL)
     return;
 
  /*
   * Save and log the job...
   */
-   
+
   cupsdSaveJob(job);
 
   cupsdLogMessage(CUPSD_LOG_INFO, "Job %d created on \"%s\" by \"%s\".",
@@ -4644,6 +4845,16 @@ create_requested_array(ipp_t *request)   /* I - IPP request */
       cupsArrayAdd(ra, "uri-authentication-supported");
       cupsArrayAdd(ra, "uri-security-supported");
     }
+    else if (!strcmp(value, "printer-defaults"))
+    {
+      char     *name;                  /* Option name */
+
+
+      for (name = (char *)cupsArrayFirst(CommonDefaults);
+           name;
+          name = (char *)cupsArrayNext(CommonDefaults))
+        cupsArrayAdd(ra, name);
+    }
     else if (!strcmp(value, "subscription-template"))
     {
       cupsArrayAdd(ra, "notify-attributes");
@@ -4676,7 +4887,6 @@ create_subscription(
   http_status_t        status;                 /* Policy status */
   int                  i;              /* Looping var */
   ipp_attribute_t      *attr;          /* Current attribute */
-  const char           *dest;          /* Destination */
   cups_ptype_t         dtype;          /* Destination type (printer or class) */
   char                 scheme[HTTP_MAX_URI],
                                        /* Scheme portion of URI */
@@ -4698,6 +4908,8 @@ create_subscription(
   int                  interval,       /* notify-time-interval */
                        lease;          /* notify-lease-duration */
   unsigned             mask;           /* notify-events */
+  ipp_attribute_t      *notify_events,/* notify-events(-default) */
+                       *notify_lease;  /* notify-lease-duration(-default) */
 
 
 #ifdef DEBUG
@@ -4725,23 +4937,20 @@ create_subscription(
 
   if (!strcmp(resource, "/"))
   {
-    dest    = NULL;
     dtype   = (cups_ptype_t)0;
     printer = NULL;
   }
   else if (!strncmp(resource, "/printers", 9) && strlen(resource) <= 10)
   {
-    dest    = NULL;
     dtype   = (cups_ptype_t)0;
     printer = NULL;
   }
   else if (!strncmp(resource, "/classes", 8) && strlen(resource) <= 9)
   {
-    dest    = NULL;
     dtype   = CUPS_PRINTER_CLASS;
     printer = NULL;
   }
-  else if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -4758,15 +4967,16 @@ create_subscription(
 
   if (printer)
   {
-    if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
+    if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
+                                   NULL)) != HTTP_OK)
     {
-      send_http_error(con, status);
+      send_http_error(con, status, printer);
       return;
     }
   }
   else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -4808,9 +5018,26 @@ create_subscription(
     jobid      = 0;
     mask       = CUPSD_EVENT_NONE;
 
+    if (printer)
+    {
+      notify_events = ippFindAttribute(printer->attrs, "notify-events-default",
+                                       IPP_TAG_KEYWORD);
+      notify_lease  = ippFindAttribute(printer->attrs,
+                                       "notify-lease-duration-default",
+                                       IPP_TAG_INTEGER);
+
+      if (notify_lease)
+        lease = notify_lease->values[0].integer;
+    }
+    else
+    {
+      notify_events = NULL;
+      notify_lease  = NULL;
+    }
+
     while (attr && attr->group_tag != IPP_TAG_ZERO)
     {
-      if (!strcmp(attr->name, "notify-recipient") &&
+      if (!strcmp(attr->name, "notify-recipient-uri") &&
           attr->value_tag == IPP_TAG_URI)
       {
        /*
@@ -4829,7 +5056,7 @@ create_subscription(
                            resource, sizeof(resource)) < HTTP_URI_OK)
         {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("Bad notify-recipient URI \"%s\"!"), recipient);
+                         _("Bad notify-recipient-uri URI \"%s\"!"), recipient);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_URI_SCHEME);
          return;
@@ -4840,7 +5067,7 @@ create_subscription(
         if (access(notifier, X_OK))
        {
           send_ipp_status(con, IPP_NOT_POSSIBLE,
-                         _("notify-recipient URI \"%s\" uses unknown scheme!"),
+                         _("notify-recipient-uri URI \"%s\" uses unknown scheme!"),
                          recipient);
          ippAddInteger(con->response, IPP_TAG_SUBSCRIPTION, IPP_TAG_ENUM,
                        "notify-status-code", IPP_URI_SCHEME);
@@ -4896,10 +5123,7 @@ create_subscription(
       }
       else if (!strcmp(attr->name, "notify-events") &&
                attr->value_tag == IPP_TAG_KEYWORD)
-      {
-        for (i = 0; i < attr->num_values; i ++)
-         mask |= cupsdEventValue(attr->values[i].string.text);
-      }
+        notify_events = attr;
       else if (!strcmp(attr->name, "notify-lease-duration") &&
                attr->value_tag == IPP_TAG_INTEGER)
         lease = attr->values[0].integer;
@@ -4913,6 +5137,12 @@ create_subscription(
       attr = attr->next;
     }
 
+    if (notify_events)
+    {
+      for (i = 0; i < notify_events->num_values; i ++)
+       mask |= cupsdEventValue(notify_events->values[i].string.text);
+    }
+
     if (recipient)
       cupsdLogMessage(CUPSD_LOG_DEBUG, "recipient=\"%s\"", recipient);
     if (pullmethod)
@@ -5007,13 +5237,7 @@ delete_printer(cupsd_client_t  *con,     /* I - Client connection */
                ipp_attribute_t *uri)   /* I - URI of printer or class */
 {
   http_status_t        status;                 /* Policy status */
-  const char   *dest;                  /* Destination */
   cups_ptype_t dtype;                  /* Destination type (printer or class) */
-  char         method[HTTP_MAX_URI],   /* Method 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 */
   cupsd_printer_t *printer;            /* Printer/class */
   char         filename[1024];         /* Script/PPD filename */
 
@@ -5025,11 +5249,7 @@ delete_printer(cupsd_client_t  *con,     /* I - Client connection */
   * Do we have a valid URI?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -5046,7 +5266,7 @@ delete_printer(cupsd_client_t  *con,      /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -5054,7 +5274,7 @@ delete_printer(cupsd_client_t  *con,      /* I - Client connection */
   * Remove old jobs...
   */
 
-  cupsdCancelJobs(dest, NULL, 1);
+  cupsdCancelJobs(printer->name, NULL, 1);
 
  /*
   * Remove old subscriptions and send a "deleted printer" event...
@@ -5063,32 +5283,34 @@ delete_printer(cupsd_client_t  *con,    /* I - Client connection */
   cupsdAddEvent(CUPSD_EVENT_PRINTER_DELETED, printer, NULL,
                 "%s \"%s\" deleted by \"%s\".",
                (dtype & CUPS_PRINTER_CLASS) ? "Class" : "Printer",
-               dest, get_username(con));
+               printer->name, get_username(con));
 
   cupsdExpireSubscriptions(printer, NULL);
+
  /*
   * Remove any old PPD or script files...
   */
 
-  snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot, dest);
+  snprintf(filename, sizeof(filename), "%s/interfaces/%s", ServerRoot,
+           printer->name);
   unlink(filename);
 
-  snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot, dest);
+  snprintf(filename, sizeof(filename), "%s/ppd/%s.ppd", ServerRoot,
+           printer->name);
   unlink(filename);
 
   if (dtype & CUPS_PRINTER_CLASS)
   {
-    cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".", dest,
-                    get_username(con));
+    cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" deleted by \"%s\".",
+                    printer->name, get_username(con));
 
     cupsdDeletePrinter(printer, 0);
     cupsdSaveAllClasses();
   }
   else
   {
-    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".", dest,
-                    get_username(con));
+    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" deleted by \"%s\".",
+                    printer->name, get_username(con));
 
     cupsdDeletePrinter(printer, 0);
     cupsdSaveAllPrinters();
@@ -5123,7 +5345,7 @@ get_default(cupsd_client_t *con)  /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -5166,7 +5388,7 @@ get_devices(cupsd_client_t *con)  /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -5300,7 +5522,7 @@ get_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -5331,7 +5553,7 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
   const char   *dest;                  /* Destination */
   cups_ptype_t dtype;                  /* Destination type (printer or class) */
   cups_ptype_t dmask;                  /* Destination type mask */
-  char         method[HTTP_MAX_URI],   /* Method portion of URI */
+  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 */
@@ -5353,8 +5575,8 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
   * Is the destination valid?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
+  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
+                  sizeof(scheme), username, sizeof(username), host,
                  sizeof(host), &port, resource, sizeof(resource));
 
   if (!strcmp(resource, "/") ||
@@ -5379,7 +5601,8 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
     dmask   = CUPS_PRINTER_CLASS;
     printer = NULL;
   }
-  else if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  else if ((dest = cupsdValidateDest(uri->values[0].string.text, &dtype,
+                                     &printer)) == NULL)
   {
    /*
     * Bad URI...
@@ -5390,7 +5613,10 @@ get_jobs(cupsd_client_t  *con,           /* I - Client connection */
     return;
   }
   else
+  {
+    dtype &= CUPS_PRINTER_CLASS;
     dmask = CUPS_PRINTER_CLASS;
+  }
 
  /*
   * Check policy...
@@ -5398,15 +5624,16 @@ get_jobs(cupsd_client_t  *con,          /* I - Client connection */
 
   if (printer)
   {
-    if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
+    if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
+                                   NULL)) != HTTP_OK)
     {
-      send_http_error(con, status);
+      send_http_error(con, status, printer);
       return;
     }
   }
   else if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -5481,9 +5708,6 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
     if ((job->dtype & dmask) != dtype &&
         (!job->printer || (job->printer->type & dmask) != dtype))
       continue;
-    if (username[0] && strcasecmp(username, job->username))
-      continue;
-
     if (completed && job->state_value <= IPP_JOB_STOPPED)
       continue;
 
@@ -5495,6 +5719,9 @@ get_jobs(cupsd_client_t  *con,            /* I - Client connection */
     if (!job->attrs)
       continue;
 
+    if (username[0] && strcasecmp(username, job->username))
+      continue;
+
     if (count > 0)
       ippAddSeparator(con->response);
 
@@ -5527,7 +5754,7 @@ get_notifications(cupsd_client_t *con)    /* I - Client connection */
   int                  interval;       /* Poll interval */
 
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_subscription_attrs(con=%p[%d])",
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_notifications(con=%p[%d])",
                   con, con->http.fd);
 
  /*
@@ -5572,7 +5799,7 @@ get_notifications(cupsd_client_t *con)    /* I - Client connection */
                                                DefaultPolicyPtr,
                                    con, sub->owner)) != HTTP_OK)
     {
-      send_http_error(con, status);
+      send_http_error(con, status, sub->dest);
       return;
     }
 
@@ -5674,7 +5901,7 @@ get_ppds(cupsd_client_t *con)             /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -5734,17 +5961,7 @@ get_printer_attrs(cupsd_client_t  *con,  /* I - Client connection */
                  ipp_attribute_t *uri) /* I - Printer URI */
 {
   http_status_t                status;         /* Policy status */
-  const char           *dest;          /* Destination */
   cups_ptype_t         dtype;          /* Destination type (printer or class) */
-  char                 method[HTTP_MAX_URI],
-                                       /* Method 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 */
   cupsd_printer_t      *printer;       /* Printer/class */
   cups_array_t         *ra;            /* Requested attributes array */
 
@@ -5756,11 +5973,7 @@ get_printer_attrs(cupsd_client_t  *con,  /* I - Client connection */
   * Is the destination valid?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if ((dest = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -5777,7 +5990,7 @@ get_printer_attrs(cupsd_client_t  *con,   /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, printer);
     return;
   }
 
@@ -5825,7 +6038,7 @@ get_printers(cupsd_client_t *con, /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -5989,7 +6202,7 @@ get_subscription_attrs(
                                              DefaultPolicyPtr,
                                  con, sub->owner)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, sub->dest);
     return;
   }
 
@@ -6023,8 +6236,8 @@ get_subscriptions(cupsd_client_t  *con,   /* I - Client connection */
   cups_array_t         *ra;            /* Requested attributes array */
   ipp_attribute_t      *attr;          /* Attribute */
   cups_ptype_t         dtype;          /* Destination type (printer or class) */
-  char                 method[HTTP_MAX_URI],
-                                       /* Method portion of URI */
+  char                 scheme[HTTP_MAX_URI],
+                                       /* Scheme portion of URI */
                        username[HTTP_MAX_URI],
                                        /* Username portion of URI */
                        host[HTTP_MAX_URI],
@@ -6044,8 +6257,8 @@ get_subscriptions(cupsd_client_t  *con,   /* I - Client connection */
   * Is the destination valid?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
+  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
+                  sizeof(scheme), username, sizeof(username), host,
                  sizeof(host), &port, resource, sizeof(resource));
 
   if (!strcmp(resource, "/") ||
@@ -6068,7 +6281,7 @@ get_subscriptions(cupsd_client_t  *con,   /* I - Client connection */
       return;
     }
   }
-  else if (!cupsdValidateDest(host, resource, &dtype, &printer))
+  else if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -6101,7 +6314,7 @@ get_subscriptions(cupsd_client_t  *con,   /* I - Client connection */
                                            DefaultPolicyPtr,
                                  con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, printer);
     return;
   }
 
@@ -6259,7 +6472,7 @@ hold_job(cupsd_client_t  *con,            /* I - Client connection */
 
   if (!validate_user(job, con, job->username, username, sizeof(username)))
   {
-    send_http_error(con, HTTP_UNAUTHORIZED);
+    send_http_error(con, HTTP_UNAUTHORIZED, NULL);
     return;
   }
 
@@ -6269,6 +6482,9 @@ hold_job(cupsd_client_t  *con,            /* I - Client connection */
 
   cupsdHoldJob(job);
 
+  cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
+                "Job held by user.");
+
   if ((newattr = ippFindAttribute(con->request, "job-hold-until",
                                   IPP_TAG_KEYWORD)) == NULL)
     newattr = ippFindAttribute(con->request, "job-hold-until", IPP_TAG_NAME);
@@ -6302,6 +6518,9 @@ hold_job(cupsd_client_t  *con,            /* I - Client connection */
     */
 
     cupsdSetJobHoldUntil(job, attr->values[0].string.text);
+
+    cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
+                  "Job job-hold-until value changed by user.");
   }
 
   cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was held by \"%s\".", jobid,
@@ -6323,11 +6542,10 @@ move_job(cupsd_client_t  *con,          /* I - Client connection */
   ipp_attribute_t *attr;               /* Current attribute */
   int          jobid;                  /* Job ID */
   cupsd_job_t  *job;                   /* Current job */
-  const char   *src,                   /* Source printer/class */
-               *dest;                  /* Destination */
+  const char   *src;                   /* Source printer/class */
   cups_ptype_t stype,                  /* Source type (printer or class) */
                dtype;                  /* Destination type (printer or class) */
-  char         method[HTTP_MAX_URI],   /* Method portion of URI */
+  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 */
@@ -6354,12 +6572,8 @@ move_job(cupsd_client_t  *con,           /* I - Client connection */
                     _("job-printer-uri attribute missing!"));
     return;
   }
-    
-  httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
 
-  if ((dest = cupsdValidateDest(host, resource, &dtype, &dprinter)) == NULL)
+  if (!cupsdValidateDest(attr->values[0].string.text, &dtype, &dprinter))
   {
    /*
     * Bad URI...
@@ -6374,9 +6588,10 @@ move_job(cupsd_client_t  *con,           /* I - Client connection */
   * Check policy...
   */
 
-  if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con, NULL)) != HTTP_OK)
+  if ((status = cupsdCheckPolicy(dprinter->op_policy_ptr, con,
+                                 NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, dprinter);
     return;
   }
 
@@ -6384,8 +6599,8 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
   * See if we have a job URI or a printer URI...
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
+  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, scheme,
+                  sizeof(scheme), username, sizeof(username), host,
                  sizeof(host), &port, resource, sizeof(resource));
 
   if (!strcmp(uri->name, "printer-uri"))
@@ -6401,7 +6616,8 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
       * Move all jobs...
       */
 
-      if ((src = cupsdValidateDest(host, resource, &stype, &sprinter)) == NULL)
+      if ((src = cupsdValidateDest(uri->values[0].string.text, &stype,
+                                   &sprinter)) == NULL)
       {
        /*
        * Bad URI...
@@ -6514,7 +6730,7 @@ move_job(cupsd_client_t  *con,            /* I - Client connection */
 
     if (!validate_user(job, con, job->username, username, sizeof(username)))
     {
-      send_http_error(con, HTTP_UNAUTHORIZED);
+      send_http_error(con, HTTP_UNAUTHORIZED, NULL);
       return;
     }
 
@@ -6572,56 +6788,6 @@ move_job(cupsd_client_t  *con,           /* I - Client connection */
 }
 
 
-/*
- * 'ppd_add_default()' - Add a PPD default choice.
- */
-
-static int                             /* O  - Number of defaults */
-ppd_add_default(
-    const char    *option,             /* I  - Option name */
-    const char    *choice,             /* I  - Choice name */
-    int           num_defaults,                /* I  - Number of defaults */
-    ppd_default_t **defaults)          /* IO - Defaults */
-{
-  int          i;                      /* Looping var */
-  ppd_default_t        *temp;                  /* Temporary defaults array */
-
-
- /*
-  * First check if the option already has a default value; the PPD spec
-  * says that the first one is used...
-  */
-
-  for (i = 0, temp = *defaults; i < num_defaults; i ++)
-    if (!strcmp(option, temp[i].option))
-      return (num_defaults);
-
- /*
-  * Now add the option...
-  */
-
-  if (num_defaults == 0)
-    temp = malloc(sizeof(ppd_default_t));
-  else
-    temp = realloc(*defaults, (num_defaults + 1) * sizeof(ppd_default_t));
-
-  if (!temp)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "ppd_add_default: Unable to add default value for \"%s\" - %s",
-               option, strerror(errno));
-    return (num_defaults);
-  }
-
-  *defaults = temp;
-  temp      += num_defaults;
-
-  strlcpy(temp->option, option, sizeof(temp->option));
-  strlcpy(temp->choice, choice, sizeof(temp->choice));
-
-  return (num_defaults + 1);
-}
-
-
 /*
  * 'ppd_parse_line()' - Parse a PPD default line.
  */
@@ -6699,6 +6865,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
 {
   ipp_attribute_t *attr;               /* Current attribute */
   ipp_attribute_t *format;             /* Document-format attribute */
+  const char   *default_format;        /* document-format-default value */
   cupsd_job_t  *job;                   /* New job */
   char         filename[1024];         /* Job filename */
   mime_type_t  *filetype;              /* Type of file */
@@ -6755,6 +6922,21 @@ print_job(cupsd_client_t  *con,          /* I - Client connection */
     return;
   }
 
+ /*
+  * Is the destination valid?
+  */
+
+  if (!cupsdValidateDest(uri->values[0].string.text, NULL, &printer))
+  {
+   /*
+    * Bad URI...
+    */
+
+    send_ipp_status(con, IPP_NOT_FOUND,
+                    _("The printer or class was not found."));
+    return;
+  }
+
  /*
   * Is it a format we support?
   */
@@ -6766,7 +6948,8 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
     * Grab format from client...
     */
 
-    if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super, type) != 2)
+    if (sscanf(format->values[0].string.text, "%15[^/]/%31[^;]", super,
+               type) != 2)
     {
       send_ipp_status(con, IPP_BAD_REQUEST,
                       _("Could not scan type \"%s\"!"),
@@ -6774,10 +6957,26 @@ print_job(cupsd_client_t  *con,         /* I - Client connection */
       return;
     }
   }
+  else if ((default_format = cupsGetOption("document-format",
+                                           printer->num_options,
+                                          printer->options)) != NULL)
+  {
+   /*
+    * Use default document format...
+    */
+
+    if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
+    {
+      send_ipp_status(con, IPP_BAD_REQUEST,
+                      _("Could not scan type \"%s\"!"),
+                     default_format);
+      return;
+    }
+  }
   else
   {
    /*
-    * No document format attribute?  Auto-type it!
+    * Auto-type it!
     */
 
     strcpy(super, "application");
@@ -6800,32 +6999,35 @@ print_job(cupsd_client_t  *con,         /* I - Client connection */
                             doc_name ? doc_name->values[0].string.text : NULL,
                            &compression);
 
-    if (filetype)
-    {
-     /*
-      * Replace the document-format attribute value with the auto-typed one.
-      */
+    if (!filetype)
+      filetype = mimeType(MimeDatabase, super, type);
+  }
+  else
+    filetype = mimeType(MimeDatabase, super, type);
 
-      snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
-               filetype->type);
+  if (filetype &&
+      (!format ||
+       (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
+  {
+   /*
+    * Replace the document-format attribute value with the auto-typed or
+    * default one.
+    */
 
-      if (format)
-      {
-         _cupsStrFree(format->values[0].string.text);
+    snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
+             filetype->type);
 
-       format->values[0].string.text = _cupsStrAlloc(mimetype);
-      }
-      else
-        ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
-                    "document-format", NULL, mimetype);
+    if (format)
+    {
+      _cupsStrFree(format->values[0].string.text);
+
+      format->values[0].string.text = _cupsStrAlloc(mimetype);
     }
     else
-      filetype = mimeType(MimeDatabase, super, type);
+      ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
+                  "document-format", NULL, mimetype);
   }
-  else
-    filetype = mimeType(MimeDatabase, super, type);
-
-  if (!filetype)
+  else if (!filetype)
   {
     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
                     _("Unsupported format \'%s/%s\'!"), super, type);
@@ -6854,7 +7056,7 @@ print_job(cupsd_client_t  *con,           /* I - Client connection */
   * Create the job object...
   */
 
-  if ((job = add_job(con, uri, &printer, filetype)) == NULL)
+  if ((job = add_job(con, printer, filetype)) == NULL)
     return;
 
  /*
@@ -7119,12 +7321,6 @@ reject_jobs(cupsd_client_t  *con,        /* I - Client connection */
 {
   http_status_t        status;                 /* Policy status */
   cups_ptype_t dtype;                  /* Destination type (printer or class) */
-  char         method[HTTP_MAX_URI],   /* Method 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 */
-  const char   *name;                  /* Printer name */
   cupsd_printer_t *printer;            /* Printer data */
   ipp_attribute_t *attr;               /* printer-state-message text */
 
@@ -7136,11 +7332,7 @@ reject_jobs(cupsd_client_t  *con,        /* I - Client connection */
   * Is the destination valid?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -7157,7 +7349,7 @@ reject_jobs(cupsd_client_t  *con, /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, printer);
     return;
   }
 
@@ -7181,14 +7373,14 @@ reject_jobs(cupsd_client_t  *con,       /* I - Client connection */
     cupsdSaveAllClasses();
 
     cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" rejecting jobs (\"%s\").",
-                    name, get_username(con));
+                    printer->name, get_username(con));
   }
   else
   {
     cupsdSaveAllPrinters();
 
     cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" rejecting jobs (\"%s\").",
-                    name, get_username(con));
+                    printer->name, get_username(con));
   }
 
  /*
@@ -7299,7 +7491,7 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
 
   if (!validate_user(job, con, job->username, username, sizeof(username)))
   {
-    send_http_error(con, HTTP_UNAUTHORIZED);
+    send_http_error(con, HTTP_UNAUTHORIZED, NULL);
     return;
   }
 
@@ -7317,6 +7509,9 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
 
     attr->value_tag = IPP_TAG_KEYWORD;
     attr->values[0].string.text = _cupsStrAlloc("no-hold");
+
+    cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
+                  "Job job-hold-until value changed by user.");
   }
 
  /*
@@ -7325,6 +7520,9 @@ release_job(cupsd_client_t  *con, /* I - Client connection */
 
   cupsdReleaseJob(job);
 
+  cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
+                "Job released by user.");
+
   cupsdLogMessage(CUPSD_LOG_INFO, "Job %d was released by \"%s\".", jobid,
                   username);
 
@@ -7384,7 +7582,7 @@ renew_subscription(
                                              DefaultPolicyPtr,
                                  con, sub->owner)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, sub->dest);
     return;
   }
 
@@ -7515,7 +7713,7 @@ restart_job(cupsd_client_t  *con, /* I - Client connection */
 
   cupsdLoadJob(job);
 
-  if (!job->attrs ||job->num_files == 0)
+  if (!job->attrs || job->num_files == 0)
   {
    /*
     * Nope - return a "not possible" error...
@@ -7532,7 +7730,7 @@ restart_job(cupsd_client_t  *con, /* I - Client connection */
 
   if (!validate_user(job, con, job->username, username, sizeof(username)))
   {
-    send_http_error(con, HTTP_UNAUTHORIZED);
+    send_http_error(con, HTTP_UNAUTHORIZED, NULL);
     return;
   }
 
@@ -7554,22 +7752,24 @@ restart_job(cupsd_client_t  *con,       /* I - Client connection */
  */
 
 static void
-save_auth_info(cupsd_client_t *con,    /* I - Client connection */
-               cupsd_job_t    *job)    /* I - Job */
+save_auth_info(
+    cupsd_client_t  *con,              /* I - Client connection */
+    cupsd_job_t     *job,              /* I - Job */
+    ipp_attribute_t *auth_info)                /* I - auth-info attribute, if any */
 {
   int          i;                      /* Looping var */
   char         filename[1024];         /* Job authentication filename */
   cups_file_t  *fp;                    /* Job authentication file */
-  char         line[1024];             /* Line for file */
+  char         line[2048];             /* Line for file */
 
 
  /*
   * This function saves the in-memory authentication information for
   * a job so that it can be used to authenticate with a remote host.
   * The information is stored in a file that is readable only by the
-  * root user.  The username and password are Base-64 encoded, each
-  * on a separate line, followed by random number (up to 1024) of
-  * newlines to limit the amount of information that is exposed.
+  * root user.  The fields are Base-64 encoded, each on a separate line,
+  * followed by random number (up to 1024) of newlines to limit the
+  * amount of information that is exposed.
   *
   * Because of the potential for exposing of authentication information,
   * this functionality is only enabled when running cupsd as root.
@@ -7605,19 +7805,35 @@ save_auth_info(cupsd_client_t *con,     /* I - Client connection */
   fchown(cupsFileNumber(fp), 0, 0);
   fchmod(cupsFileNumber(fp), 0400);
 
- /*
-  * Write the authenticated username...
-  */
+  if (auth_info)
+  {
+   /*
+    * Write 1 to 4 auth values...
+    */
 
-  httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
-  cupsFilePrintf(fp, "%s\n", line);
+    for (i = 0; i < auth_info->num_values; i ++)
+    {
+      httpEncode64_2(line, sizeof(line), auth_info->values[i].string.text,
+                     strlen(auth_info->values[i].string.text));
+      cupsFilePrintf(fp, "%s\n", line);
+    }
+  }
+  else
+  {
+   /*
+    * Write the authenticated username...
+    */
 
- /*
-  * Write the authenticated password...
-  */
+    httpEncode64_2(line, sizeof(line), con->username, strlen(con->username));
+    cupsFilePrintf(fp, "%s\n", line);
 
-  httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
-  cupsFilePrintf(fp, "%s\n", line);
+   /*
+    * Write the authenticated password...
+    */
+
+    httpEncode64_2(line, sizeof(line), con->password, strlen(con->password));
+    cupsFilePrintf(fp, "%s\n", line);
+  }
 
  /*
   * Write a random number of newlines to the end of the file...
@@ -7631,9 +7847,66 @@ save_auth_info(cupsd_client_t *con,      /* I - Client connection */
   */
 
   cupsFileClose(fp);
+
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
+  save_krb5_creds(con, job);
+#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
 }
 
 
+#if defined(HAVE_GSSAPI) && defined(HAVE_KRB5_H)
+/*
+ * 'save_krb5_creds()' - Save Kerberos credentials for the job.
+ */
+
+static void
+save_krb5_creds(cupsd_client_t *con,   /* I - Client connection */
+                cupsd_job_t    *job)   /* I - Job */
+{
+  krb5_context krb_context;            /* Kerberos context */
+  krb5_ccache  ccache;                 /* Credentials cache */
+  OM_uint32    major_status,           /* Major status code */
+               minor_status;           /* Minor status code */
+
+
+ /*
+  * Setup a cached context for the job filters to use...
+  */
+
+  if (krb5_init_context(&krb_context))
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize Kerberos context");
+    return;
+  }
+
+#  ifdef HAVE_HEIMDAL
+  if (krb5_cc_gen_new(krb_context, &krb5_fcc_ops, &ccache))
+#  else
+  if (krb5_cc_gen_new(krb_context, &ccache))
+#  endif /* HAVE_HEIMDAL */
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create new credentials");
+    return;
+  }
+
+  major_status = gss_krb5_copy_ccache(&minor_status, con->gss_delegated_cred,
+                                     ccache);
+
+  if (GSS_ERROR(major_status))
+  {
+    cupsdLogGSSMessage(CUPSD_LOG_ERROR, major_status, minor_status,
+                       "Unable to import client credentials cache");
+    krb5_cc_destroy(krb_context, ccache);
+    return;
+  }
+
+  cupsdSetStringf(&(job->ccname), "KRB5CCNAME=FILE:%s",
+                  krb5_cc_get_name(krb_context, ccache));
+  krb5_cc_close(krb_context, ccache);
+}
+#endif /* HAVE_GSSAPI && HAVE_KRB5_H */
+
+
 /*
  * 'send_document()' - Send a file to a printer or class.
  */
@@ -7644,6 +7917,7 @@ send_document(cupsd_client_t  *con,       /* I - Client connection */
 {
   ipp_attribute_t      *attr;          /* Current attribute */
   ipp_attribute_t      *format;        /* Document-format attribute */
+  const char           *default_format;/* document-format-default value */
   int                  jobid;          /* Job ID number */
   cupsd_job_t          *job;           /* Current job */
   char                 job_uri[HTTP_MAX_URI],
@@ -7741,7 +8015,7 @@ send_document(cupsd_client_t  *con,       /* I - Client connection */
 
   if (!validate_user(job, con, job->username, username, sizeof(username)))
   {
-    send_http_error(con, HTTP_UNAUTHORIZED);
+    send_http_error(con, HTTP_UNAUTHORIZED, NULL);
     return;
   }
 
@@ -7802,6 +8076,22 @@ send_document(cupsd_client_t  *con,      /* I - Client connection */
       return;
     }
   }
+  else if ((default_format = cupsGetOption("document-format",
+                                           printer->num_options,
+                                          printer->options)) != NULL)
+  {
+   /*
+    * Use default document format...
+    */
+
+    if (sscanf(default_format, "%15[^/]/%31[^;]", super, type) != 2)
+    {
+      send_ipp_status(con, IPP_BAD_REQUEST,
+                      _("Could not scan type \"%s\"!"),
+                     default_format);
+      return;
+    }
+  }
   else
   {
    /*
@@ -7828,31 +8118,35 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
                             doc_name ? doc_name->values[0].string.text : NULL,
                            &compression);
 
-    if (filetype)
-    {
-     /*
-      * Replace the document-format attribute value with the auto-typed one.
-      */
-
-      snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
-               filetype->type);
-
-      if (format)
-      {
-       _cupsStrFree(format->values[0].string.text);
-       format->values[0].string.text = _cupsStrAlloc(mimetype);
-      }
-      else
-        ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
-                    "document-format", NULL, mimetype);
-    }
-    else
+    if (!filetype)
       filetype = mimeType(MimeDatabase, super, type);
   }
   else
     filetype = mimeType(MimeDatabase, super, type);
 
-  if (!filetype)
+  if (filetype &&
+      (!format ||
+       (!strcmp(super, "application") && !strcmp(type, "octet-stream"))))
+  {
+   /*
+    * Replace the document-format attribute value with the auto-typed or
+    * default one.
+    */
+
+    snprintf(mimetype, sizeof(mimetype), "%s/%s", filetype->super,
+             filetype->type);
+
+    if (format)
+    {
+      _cupsStrFree(format->values[0].string.text);
+
+      format->values[0].string.text = _cupsStrAlloc(mimetype);
+    }
+    else
+      ippAddString(con->request, IPP_TAG_JOB, IPP_TAG_MIMETYPE,
+                  "document-format", NULL, mimetype);
+  }
+  else if (!filetype)
   {
     send_ipp_status(con, IPP_DOCUMENT_FORMAT,
                     _("Unsupported format \'%s/%s\'!"), super, type);
@@ -8004,7 +8298,7 @@ send_document(cupsd_client_t  *con,       /* I - Client connection */
   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-id", jobid);
 
   ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_ENUM, "job-state",
-                job ? job->state_value : IPP_JOB_CANCELLED);
+                job ? job->state_value : IPP_JOB_CANCELED);
   add_job_state_reasons(con, job);
 
   con->response->request.status.status_code = IPP_OK;
@@ -8016,14 +8310,21 @@ send_document(cupsd_client_t  *con,     /* I - Client connection */
  */
 
 static void
-send_http_error(cupsd_client_t *con,   /* I - Client connection */
-                http_status_t  status) /* I - HTTP status code */
+send_http_error(
+    cupsd_client_t  *con,              /* I - Client connection */
+    http_status_t   status,            /* I - HTTP status code */
+    cupsd_printer_t *printer)          /* I - Printer, if any */
 {
   cupsdLogMessage(CUPSD_LOG_ERROR, "%s: %s",
                   ippOpString(con->request->request.op.operation_id),
                  httpStatus(status));
 
-  cupsdSendError(con, status);
+  if (status == HTTP_UNAUTHORIZED &&
+      printer && printer->num_auth_info_required > 0 &&
+      !strcmp(printer->auth_info_required[0], "negotiate"))
+    cupsdSendError(con, status, AUTH_NEGOTIATE);
+  else
+    cupsdSendError(con, status, AUTH_NONE);
 
   ippDelete(con->response);
   con->response = NULL;
@@ -8053,14 +8354,12 @@ send_ipp_status(cupsd_client_t *con,    /* I - Client connection */
               _cupsLangString(con->language, message), ap);
     va_end(ap);
 
-    cupsdLogMessage(status >= IPP_BAD_REQUEST ? CUPSD_LOG_ERROR : CUPSD_LOG_INFO,
-                    "%s %s: %s",
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s: %s",
                    ippOpString(con->request->request.op.operation_id),
                    ippErrorString(status), formatted);
   }
   else
-    cupsdLogMessage(status >= IPP_BAD_REQUEST ? CUPSD_LOG_ERROR : CUPSD_LOG_INFO,
-                    "%s %s",
+    cupsdLogMessage(CUPSD_LOG_DEBUG, "%s %s",
                    ippOpString(con->request->request.op.operation_id),
                    ippErrorString(status));
 
@@ -8092,16 +8391,6 @@ set_default(cupsd_client_t  *con,        /* I - Client connection */
 {
   http_status_t                status;         /* Policy status */
   cups_ptype_t         dtype;          /* Destination type (printer or class) */
-  char                 method[HTTP_MAX_URI],
-                                       /* Method 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 */
-  const char           *name;          /* Printer name */
   cupsd_printer_t      *printer;       /* Printer */
 
 
@@ -8112,11 +8401,7 @@ set_default(cupsd_client_t  *con,        /* I - Client connection */
   * Is the destination valid?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -8133,7 +8418,7 @@ set_default(cupsd_client_t  *con, /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, NULL);
     return;
   }
 
@@ -8149,8 +8434,8 @@ set_default(cupsd_client_t  *con, /* I - Client connection */
   cupsdWritePrintcap();
 
   cupsdLogMessage(CUPSD_LOG_INFO,
-                  "Default destination set to \"%s\" by \"%s\".", name,
-                  get_username(con));
+                  "Default destination set to \"%s\" by \"%s\".",
+                 printer->name, get_username(con));
 
  /*
   * Everything was ok, so return OK status...
@@ -8181,6 +8466,7 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
                        resource[HTTP_MAX_URI];
                                        /* Resource portion of URI */
   int                  port;           /* Port portion of URI */
+  int                  event;          /* Events? */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "set_job_attrs(%p[%d], %s)", con,
@@ -8272,7 +8558,7 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
 
   if (!validate_user(job, con, job->username, username, sizeof(username)))
   {
-    send_http_error(con, HTTP_UNAUTHORIZED);
+    send_http_error(con, HTTP_UNAUTHORIZED, NULL);
     return;
   }
 
@@ -8282,6 +8568,8 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
 
   cupsdLoadJob(job);
 
+  event = 0;
+
   for (attr = con->request->attrs; attr; attr = attr->next)
   {
     if (attr->group_tag != IPP_TAG_JOB || !attr->name)
@@ -8345,7 +8633,10 @@ set_job_attrs(cupsd_client_t  *con,      /* I - Client connection */
        return;
       }
       else if (con->response->request.status.status_code == IPP_OK)
+      {
         cupsdSetJobPriority(job, attr->values[0].integer);
+        event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
+      }
     }
     else if (!strcmp(attr->name, "job-state"))
     {
@@ -8375,7 +8666,9 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
               else if (con->response->request.status.status_code == IPP_OK)
              {
                job->state->values[0].integer = attr->values[0].integer;
-               job->state_value              = attr->values[0].integer;
+               job->state_value              = (ipp_jstate_t)attr->values[0].integer;
+
+                event |= CUPSD_EVENT_JOB_STATE;
              }
              break;
 
@@ -8389,7 +8682,7 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
              }
              break;
 
-         case IPP_JOB_CANCELLED :
+         case IPP_JOB_CANCELED :
          case IPP_JOB_ABORTED :
          case IPP_JOB_COMPLETED :
              if (job->state_value > IPP_JOB_PROCESSING)
@@ -8399,16 +8692,7 @@ set_job_attrs(cupsd_client_t  *con,      /* I - Client connection */
                return;
              }
               else if (con->response->request.status.status_code == IPP_OK)
-             {
-                cupsdCancelJob(job, 0);
-
-               if (JobHistory)
-               {
-                  job->state->values[0].integer = attr->values[0].integer;
-                  job->state_value              = attr->values[0].integer;
-                 cupsdSaveJob(job);
-               }
-             }
+                cupsdCancelJob(job, 0, (ipp_jstate_t)attr->values[0].integer);
              break;
        }
       }
@@ -8450,6 +8734,8 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
          cupsdReleaseJob(job);
        else
          cupsdHoldJob(job);
+
+        event |= CUPSD_EVENT_JOB_CONFIG_CHANGED | CUPSD_EVENT_JOB_STATE;
       }
     }
     else if (attr->value_tag == IPP_TAG_DELETEATTR)
@@ -8470,6 +8756,8 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
          job->attrs->last = job->attrs->prev;
 
         _ippFreeAttr(attr2);
+
+        event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
       }
     }
     else
@@ -8479,6 +8767,8 @@ set_job_attrs(cupsd_client_t  *con,       /* I - Client connection */
       */
 
       copy_attribute(job->attrs, attr, 0);
+
+      event |= CUPSD_EVENT_JOB_CONFIG_CHANGED;
     }
   }
 
@@ -8488,6 +8778,19 @@ set_job_attrs(cupsd_client_t  *con,      /* I - Client connection */
 
   cupsdSaveJob(job);
 
+ /*
+  * Send events as needed...
+  */
+
+  if (event & CUPSD_EVENT_JOB_STATE)
+    cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job,
+                  job->state_value == IPP_JOB_HELD ?
+                     "Job held by user." : "Job restarted by user.");
+
+  if (event & CUPSD_EVENT_JOB_CONFIG_CHANGED)
+    cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, job->printer, job,
+                  "Job options changed by user.");
+
  /*
   * Start jobs if possible...
   */
@@ -8652,10 +8955,6 @@ set_printer_defaults(
                       attr->values[0].string.text);
       cupsdSetString(&printer->error_policy, attr->values[0].string.text);
     }
-    else if (!strcmp(attr->name, "document-format-default") ||
-             !strcmp(attr->name, "notify-lease-duration-default") ||
-             !strcmp(attr->name, "notify-events-default"))
-      continue;
 
    /*
     * Skip any other non-default attributes...
@@ -8756,16 +9055,6 @@ start_printer(cupsd_client_t  *con,      /* I - Client connection */
 {
   http_status_t                status;         /* Policy status */
   cups_ptype_t         dtype;          /* Destination type (printer or class) */
-  char                 method[HTTP_MAX_URI],
-                                       /* Method 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 */
-  const char           *name;          /* Printer name */
   cupsd_printer_t      *printer;       /* Printer data */
 
 
@@ -8776,11 +9065,7 @@ start_printer(cupsd_client_t  *con,      /* I - Client connection */
   * Is the destination valid?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -8797,7 +9082,7 @@ start_printer(cupsd_client_t  *con,       /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, printer);
     return;
   }
 
@@ -8810,11 +9095,11 @@ start_printer(cupsd_client_t  *con,     /* I - Client connection */
   cupsdStartPrinter(printer, 1);
 
   if (dtype & CUPS_PRINTER_CLASS)
-    cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".", name,
-                    get_username(con));
+    cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" started by \"%s\".",
+                    printer->name, get_username(con));
   else
-    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".", name,
-                    get_username(con));
+    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" started by \"%s\".",
+                    printer->name, get_username(con));
 
   cupsdCheckJobs();
 
@@ -8836,16 +9121,6 @@ stop_printer(cupsd_client_t  *con,       /* I - Client connection */
 {
   http_status_t                status;         /* Policy status */
   cups_ptype_t         dtype;          /* Destination type (printer or class) */
-  char                 method[HTTP_MAX_URI],
-                                       /* Method 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 */
-  const char           *name;          /* Printer name */
   cupsd_printer_t      *printer;       /* Printer data */
   ipp_attribute_t      *attr;          /* printer-state-message attribute */
 
@@ -8857,11 +9132,7 @@ stop_printer(cupsd_client_t  *con,       /* I - Client connection */
   * Is the destination valid?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if ((name = cupsdValidateDest(host, resource, &dtype, &printer)) == NULL)
+  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -8878,7 +9149,7 @@ stop_printer(cupsd_client_t  *con,        /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, printer);
     return;
   }
 
@@ -8898,11 +9169,11 @@ stop_printer(cupsd_client_t  *con,      /* I - Client connection */
   cupsdStopPrinter(printer, 1);
 
   if (dtype & CUPS_PRINTER_CLASS)
-    cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".", name,
-                    get_username(con));
+    cupsdLogMessage(CUPSD_LOG_INFO, "Class \"%s\" stopped by \"%s\".",
+                    printer->name, get_username(con));
   else
-    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".", name,
-                    get_username(con));
+    cupsdLogMessage(CUPSD_LOG_INFO, "Printer \"%s\" stopped by \"%s\".",
+                    printer->name, get_username(con));
 
  /*
   * Everything was ok, so return OK status...
@@ -9028,15 +9299,6 @@ validate_job(cupsd_client_t  *con,       /* I - Client connection */
   ipp_attribute_t      *attr;          /* Current attribute */
   ipp_attribute_t      *format;        /* Document-format attribute */
   cups_ptype_t         dtype;          /* Destination type (printer or class) */
-  char                 method[HTTP_MAX_URI],
-                                       /* Method 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 */
   char                 super[MIME_MAX_SUPER],
                                        /* Supertype of file */
                        type[MIME_MAX_TYPE];
@@ -9096,11 +9358,7 @@ validate_job(cupsd_client_t  *con,       /* I - Client connection */
   * Is the destination valid?
   */
 
-  httpSeparateURI(HTTP_URI_CODING_ALL, uri->values[0].string.text, method,
-                  sizeof(method), username, sizeof(username), host,
-                 sizeof(host), &port, resource, sizeof(resource));
-
-  if (cupsdValidateDest(host, resource, &dtype, &printer) == NULL)
+  if (!cupsdValidateDest(uri->values[0].string.text, &dtype, &printer))
   {
    /*
     * Bad URI...
@@ -9117,7 +9375,7 @@ validate_job(cupsd_client_t  *con,        /* I - Client connection */
 
   if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con, NULL)) != HTTP_OK)
   {
-    send_http_error(con, status);
+    send_http_error(con, status, printer);
     return;
   }
 
@@ -9144,7 +9402,7 @@ validate_name(const char *name)   /* I - Name to check */
   */
 
   for (ptr = name; *ptr; ptr ++)
-    if ((*ptr >= 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
+    if ((*ptr > 0 && *ptr <= ' ') || *ptr == 127 || *ptr == '/' || *ptr == '#')
       return (0);
 
  /*
@@ -9200,5 +9458,5 @@ validate_user(cupsd_job_t    *job,        /* I - Job */
 
 
 /*
- * End of "$Id: ipp.c 5736 2006-07-13 19:59:36Z mike $".
+ * End of "$Id: ipp.c 6383 2007-03-21 20:01:20Z mike $".
  */