]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/ipp.c
Import CUPS trunk (1.4svn) r7116.
[thirdparty/cups.git] / scheduler / ipp.c
index d8899dc8b3fc474abd2b6c1f56ce5efc70807862..690a087327ba707760910e4a56c8965595d47dee 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * "$Id: ipp.c 6739 2007-07-26 19:30:03Z mike $"
+ * "$Id: ipp.c 7014 2007-10-10 21:57:43Z mike $"
  *
  *   IPP routines for the Common UNIX Printing System (CUPS) scheduler.
  *
@@ -54,6 +54,7 @@
  *   get_default()               - Get the default destination.
  *   get_devices()               - Get the list of available devices on the
  *                                 local system.
+ *   get_document()              - Get a copy of a job file.
  *   get_job_attrs()             - Get job attributes.
  *   get_jobs()                  - Get a list of jobs for the specified printer.
  *   get_notifications()         - Get events for a subscription.
 
 #include "cupsd.h"
 
-#ifdef HAVE_KRB5_H
-#  include <krb5.h>
-#endif /* HAVE_KRB5_H */
-
 #ifdef HAVE_LIBPAPER
 #  include <paper.h>
 #endif /* HAVE_LIBPAPER */
@@ -169,6 +166,7 @@ static void create_subscription(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    delete_printer(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    get_default(cupsd_client_t *con);
 static void    get_devices(cupsd_client_t *con);
+static void    get_document(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    get_jobs(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    get_job_attrs(cupsd_client_t *con, ipp_attribute_t *uri);
 static void    get_notifications(cupsd_client_t *con);
@@ -364,13 +362,31 @@ cupsdProcessIPPRequest(
        ippAddString(con->response, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
                      "attributes-natural-language", NULL, DefaultLanguage);
 
-      if (!charset || !language ||
-         (!uri &&
-          con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
-          con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
-          con->request->request.op.operation_id != CUPS_GET_CLASSES &&
-          con->request->request.op.operation_id != CUPS_GET_DEVICES &&
-          con->request->request.op.operation_id != CUPS_GET_PPDS))
+      if (charset &&
+          strcasecmp(charset->values[0].string.text, "us-ascii") &&
+          strcasecmp(charset->values[0].string.text, "utf-8"))
+      {
+       /*
+        * Bad character set...
+       */
+
+        cupsdLogMessage(CUPSD_LOG_ERROR, "Unsupported character set \"%s\"!",
+                       charset->values[0].string.text);
+       cupsdAddEvent(CUPSD_EVENT_SERVER_AUDIT, NULL, NULL,
+                     "%04X %s Unsupported attributes-charset value \"%s\"",
+                     IPP_CHARSET, con->http.hostname,
+                     charset->values[0].string.text);
+       send_ipp_status(con, IPP_BAD_REQUEST,
+                       _("Unsupported character set \"%s\"!"),
+                       charset->values[0].string.text);
+      }
+      else if (!charset || !language ||
+              (!uri &&
+               con->request->request.op.operation_id != CUPS_GET_DEFAULT &&
+               con->request->request.op.operation_id != CUPS_GET_PRINTERS &&
+               con->request->request.op.operation_id != CUPS_GET_CLASSES &&
+               con->request->request.op.operation_id != CUPS_GET_DEVICES &&
+               con->request->request.op.operation_id != CUPS_GET_PPDS))
       {
        /*
        * Return an error, since attributes-charset,
@@ -575,6 +591,10 @@ cupsdProcessIPPRequest(
               get_devices(con);
               break;
 
+          case CUPS_GET_DOCUMENT :
+             get_document(con, uri);
+             break;
+
          case CUPS_GET_PPD :
               get_ppd(con, uri);
               break;
@@ -905,16 +925,6 @@ add_class(cupsd_client_t  *con,            /* I - Client connection */
     return;
   }
 
- /*
-  * Check policy...
-  */
-
-  if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
-  {
-    send_http_error(con, status, NULL);
-    return;
-  }
-
  /*
   * See if the class already exists; if not, create a new class...
   */
@@ -939,18 +949,31 @@ add_class(cupsd_client_t  *con,           /* I - Client connection */
     }
 
    /*
-    * No, add the pclass...
+    * No, check the default policy and then add the class...
     */
 
+    if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+    {
+      send_http_error(con, status, NULL);
+      return;
+    }
+
     pclass = cupsdAddClass(resource + 9);
     modify = 0;
   }
   else if (pclass->type & CUPS_PRINTER_IMPLICIT)
   {
    /*
-    * Rename the implicit class to "AnyClass" or remove it...
+    * Check the default policy, then rename the implicit class to "AnyClass"
+    * or remove it...
     */
 
+    if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+    {
+      send_http_error(con, status, NULL);
+      return;
+    }
+
     if (ImplicitAnyClasses)
     {
       snprintf(newname, sizeof(newname), "Any%s", resource + 9);
@@ -969,9 +992,15 @@ add_class(cupsd_client_t  *con,            /* I - Client connection */
   else if (pclass->type & CUPS_PRINTER_DISCOVERED)
   {
    /*
-    * Rename the remote class to "Class"...
+    * Check the default policy, then rename the remote class to "Class"...
     */
 
+    if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+    {
+      send_http_error(con, status, NULL);
+      return;
+    }
+
     snprintf(newname, sizeof(newname), "%s@%s", resource + 9, pclass->hostname);
     cupsdRenamePrinter(pclass, newname);
 
@@ -982,6 +1011,12 @@ add_class(cupsd_client_t  *con,           /* I - Client connection */
     pclass = cupsdAddClass(resource + 9);
     modify = 0;
   }
+  else if ((status = cupsdCheckPolicy(pclass->op_policy_ptr, con,
+                                      NULL)) != HTTP_OK)
+  {
+    send_http_error(con, status, pclass);
+    return;
+  }
   else
     modify = 1;
 
@@ -1239,7 +1274,8 @@ add_job(cupsd_client_t  *con,             /* I - Client connection */
 
   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);
+                 filetype, filetype ? filetype->super : "none",
+                 filetype ? filetype->type : "none");
 
  /*
   * Check remote printing to non-shared printer...
@@ -2177,16 +2213,6 @@ add_printer(cupsd_client_t  *con,        /* I - Client connection */
     return;
   }
 
- /*
-  * Check policy...
-  */
-
-  if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
-  {
-    send_http_error(con, status, NULL);
-    return;
-  }
-
  /*
   * See if the printer already exists; if not, create a new printer...
   */
@@ -2211,18 +2237,31 @@ add_printer(cupsd_client_t  *con,       /* I - Client connection */
     }
 
    /*
-    * No, add the printer...
+    * No, check the default policy then add the printer...
     */
 
+    if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+    {
+      send_http_error(con, status, NULL);
+      return;
+    }
+
     printer = cupsdAddPrinter(resource + 10);
     modify  = 0;
   }
   else if (printer->type & CUPS_PRINTER_IMPLICIT)
   {
    /*
-    * Rename the implicit printer to "AnyPrinter" or delete it...
+    * Check the default policy, then rename the implicit printer to
+    * "AnyPrinter" or delete it...
     */
 
+    if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+    {
+      send_http_error(con, status, NULL);
+      return;
+    }
+
     if (ImplicitAnyClasses)
     {
       snprintf(newname, sizeof(newname), "Any%s", resource + 10);
@@ -2241,9 +2280,16 @@ add_printer(cupsd_client_t  *con,        /* I - Client connection */
   else if (printer->type & CUPS_PRINTER_DISCOVERED)
   {
    /*
-    * Rename the remote printer to "Printer@server"...
+    * Check the default policy, then rename the remote printer to
+    * "Printer@server"...
     */
 
+    if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+    {
+      send_http_error(con, status, NULL);
+      return;
+    }
+
     snprintf(newname, sizeof(newname), "%s@%s", resource + 10,
              printer->hostname);
     cupsdRenamePrinter(printer, newname);
@@ -2255,6 +2301,12 @@ add_printer(cupsd_client_t  *con,        /* I - Client connection */
     printer = cupsdAddPrinter(resource + 10);
     modify  = 0;
   }
+  else if ((status = cupsdCheckPolicy(printer->op_policy_ptr, con,
+                                      NULL)) != HTTP_OK)
+  {
+    send_http_error(con, status, printer);
+    return;
+  }
   else
     modify = 1;
 
@@ -2281,11 +2333,26 @@ add_printer(cupsd_client_t  *con,       /* I - Client connection */
     * Do we have a valid device URI?
     */
 
+    http_uri_status_t uri_status;      /* URI separation status */
+
+
     need_restart_job = 1;
 
-    httpSeparateURI(HTTP_URI_CODING_ALL, attr->values[0].string.text, scheme,
-                    sizeof(scheme), username, sizeof(username), host,
-                   sizeof(host), &port, resource, sizeof(resource));
+    uri_status = httpSeparateURI(HTTP_URI_CODING_ALL,
+                                attr->values[0].string.text,
+                                scheme, sizeof(scheme),
+                                username, sizeof(username),
+                                host, sizeof(host), &port,
+                                resource, sizeof(resource));
+
+    if (uri_status < HTTP_URI_OK)
+    {
+      send_ipp_status(con, IPP_NOT_POSSIBLE, _("Bad device-uri \"%s\"!"),
+                     attr->values[0].string.text);
+      cupsdLogMessage(CUPSD_LOG_DEBUG,
+                      "add_printer: httpSeparateURI returned %d", uri_status);
+      return;
+    }
 
     if (!strcmp(scheme, "file"))
     {
@@ -2349,7 +2416,7 @@ add_printer(cupsd_client_t  *con, /* I - Client connection */
     need_restart_job = 1;
 
     supported = ippFindAttribute(printer->attrs, "port-monitor-supported",
-                                 IPP_TAG_KEYWORD);
+                                 IPP_TAG_NAME);
     for (i = 0; i < supported->num_values; i ++)
       if (!strcmp(supported->values[i].string.text,
                   attr->values[0].string.text))
@@ -2877,8 +2944,22 @@ authenticate_job(cupsd_client_t  *con,   /* I - Client connection */
 
   if (!con->username[0] && !auth_info)
   {
-    send_ipp_status(con, IPP_NOT_AUTHORIZED,
-                    _("No authentication information provided!"));
+    cupsd_printer_t    *printer;       /* Job destination */
+
+
+   /*
+    * No auth data.  If we need to authenticate via Kerberos, send a
+    * HTTP auth challenge, otherwise just return an IPP error...
+    */
+
+    printer = cupsdFindDest(job->dest);
+
+    if (printer && printer->num_auth_info_required > 0 &&
+        !strcmp(printer->auth_info_required[0], "negotiate"))
+      send_http_error(con, HTTP_UNAUTHORIZED, printer);
+    else
+      send_ipp_status(con, IPP_NOT_AUTHORIZED,
+                     _("No authentication information provided!"));
     return;
   }
 
@@ -2888,7 +2969,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, NULL);
+    send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
     return;
   }
 
@@ -3078,6 +3159,7 @@ cancel_job(cupsd_client_t  *con,  /* I - Client connection */
   cupsd_job_t  *job;                   /* Job information */
   cups_ptype_t dtype;                  /* Destination type (printer/class) */
   cupsd_printer_t *printer;            /* Printer data */
+  int          purge;                  /* Purge the job? */
 
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cancel_job(%p[%d], %s)", con,
@@ -3185,6 +3267,16 @@ cancel_job(cupsd_client_t  *con, /* I - Client connection */
     jobid = atoi(resource + 6);
   }
 
+ /*
+  * Look for the "purge-job" attribute...
+  */
+
+  if ((attr = ippFindAttribute(con->request, "purge-job",
+                               IPP_TAG_BOOLEAN)) != NULL)
+    purge = attr->values[0].boolean;
+  else
+    purge = 0;
+
  /*
   * See if the job exists...
   */
@@ -3205,7 +3297,7 @@ 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, NULL);
+    send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
     return;
   }
 
@@ -3214,7 +3306,7 @@ cancel_job(cupsd_client_t  *con,  /* I - Client connection */
   * we can't cancel...
   */
 
-  if (job->state_value >= IPP_JOB_CANCELED)
+  if (job->state_value >= IPP_JOB_CANCELED && !purge)
   {
     switch (job->state_value)
     {
@@ -3244,11 +3336,15 @@ cancel_job(cupsd_client_t  *con,        /* I - Client connection */
   * Cancel the job and return...
   */
 
-  cupsdCancelJob(job, 0, IPP_JOB_CANCELED);
+  cupsdCancelJob(job, purge, IPP_JOB_CANCELED);
   cupsdCheckJobs();
 
-  cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
-                  username);
+  if (purge)
+    cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Purged by \"%s\".", jobid,
+                    username);
+  else
+    cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Canceled by \"%s\".", jobid,
+                    username);
 
   con->response->request.status.status_code = IPP_OK;
 }
@@ -4521,6 +4617,10 @@ copy_job_attrs(cupsd_client_t *con,      /* I - Client connection */
                    con->servername, con->serverport, "/jobs/%d",
                   job->id);
 
+  if (!ra || cupsArrayFind(ra, "document-count"))
+    ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER,
+                 "document-count", job->num_files);
+
   if (!ra || cupsArrayFind(ra, "job-more-info"))
     ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_URI,
                 "job-more-info", NULL, job_uri);
@@ -5593,6 +5693,150 @@ get_devices(cupsd_client_t *con)        /* I - Client connection */
 }
 
 
+/*
+ * 'get_document()' - Get a copy of a job file.
+ */
+
+static void
+get_document(cupsd_client_t  *con,     /* I - Client connection */
+             ipp_attribute_t *uri)     /* I - Job URI */
+{
+  http_status_t        status;                 /* Policy status */
+  ipp_attribute_t *attr;               /* Current attribute */
+  int          jobid;                  /* Job ID */
+  int          docnum;                 /* Document number */
+  cupsd_job_t  *job;                   /* Current job */
+  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         filename[1024],         /* Filename for document */
+               format[1024];           /* Format for document */
+
+
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_document(%p[%d], %s)", con,
+                  con->http.fd, uri->values[0].string.text);
+
+ /*
+  * See if we have a job URI or a printer URI...
+  */
+
+  if (!strcmp(uri->name, "printer-uri"))
+  {
+   /*
+    * Got a printer URI; see if we also have a job-id attribute...
+    */
+
+    if ((attr = ippFindAttribute(con->request, "job-id",
+                                 IPP_TAG_INTEGER)) == NULL)
+    {
+      send_ipp_status(con, IPP_BAD_REQUEST,
+                      _("Got a printer-uri attribute but no job-id!"));
+      return;
+    }
+
+    jobid = attr->values[0].integer;
+  }
+  else
+  {
+   /*
+    * 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,
+                   sizeof(host), &port, resource, sizeof(resource));
+
+    if (strncmp(resource, "/jobs/", 6))
+    {
+     /*
+      * Not a valid URI!
+      */
+
+      send_ipp_status(con, IPP_BAD_REQUEST,
+                      _("Bad job-uri attribute \"%s\"!"),
+                      uri->values[0].string.text);
+      return;
+    }
+
+    jobid = atoi(resource + 6);
+  }
+
+ /*
+  * See if the job exists...
+  */
+
+  if ((job = cupsdFindJob(jobid)) == NULL)
+  {
+   /*
+    * Nope - return a "not found" error...
+    */
+
+    send_ipp_status(con, IPP_NOT_FOUND, _("Job #%d does not exist!"), jobid);
+    return;
+  }
+
+ /*
+  * Check policy...
+  */
+
+  if ((status = cupsdCheckPolicy(DefaultPolicyPtr, con, NULL)) != HTTP_OK)
+  {
+    send_http_error(con, status, NULL);
+    return;
+  }
+
+ /*
+  * Get the document number...
+  */
+
+  if ((attr = ippFindAttribute(con->request, "document-number",
+                               IPP_TAG_INTEGER)) == NULL)
+  {
+    send_ipp_status(con, IPP_BAD_REQUEST,
+                    _("Missing document-number attribute!"));
+    return;
+  }
+
+  if ((docnum = attr->values[0].integer) < 1 || docnum > job->num_files ||
+      attr->num_values > 1)
+  {
+    send_ipp_status(con, IPP_NOT_FOUND, _("Document %d not found in job %d."),
+                    docnum, jobid);
+    return;
+  }
+
+  snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot, jobid,
+           docnum);
+  if ((con->file = open(filename, O_RDONLY)) == -1)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to open document %d in job %d - %s", docnum, jobid,
+                   strerror(errno));
+    send_ipp_status(con, IPP_NOT_FOUND,
+                    _("Unable to open document %d in job %d!"), docnum, jobid);
+    return;
+  }
+
+  fcntl(con->file, F_SETFD, fcntl(con->file, F_GETFD) | FD_CLOEXEC);
+
+  cupsdLoadJob(job);
+
+  snprintf(format, sizeof(format), "%s/%s", job->filetypes[docnum - 1]->super,
+           job->filetypes[docnum - 1]->type);
+
+  ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_MIMETYPE, "document-format",
+               NULL, format);
+  ippAddInteger(con->response, IPP_TAG_JOB, IPP_TAG_INTEGER, "document-number",
+                docnum);
+  if ((attr = ippFindAttribute(job->attrs, "document-name",
+                               IPP_TAG_NAME)) != NULL)
+    ippAddString(con->response, IPP_TAG_JOB, IPP_TAG_NAME, "document-name",
+                 NULL, attr->values[0].string.text);
+}
+
+
 /*
  * 'get_job_attrs()' - Get job attributes.
  */
@@ -5861,6 +6105,12 @@ get_jobs(cupsd_client_t  *con,           /* I - Client connection */
 
     cupsdLogMessage(CUPSD_LOG_DEBUG2, "get_jobs: job->id = %d", job->id);
 
+    if (!job->dest || !job->username)
+      cupsdLoadJob(job);
+
+    if (!job->dest || !job->username)
+      continue;
+
     if ((dest && strcmp(job->dest, dest)) &&
         (!job->printer || !dest || strcmp(job->printer->name, dest)))
       continue;
@@ -6113,7 +6363,7 @@ get_ppd(cupsd_client_t  *con,             /* I - Client connection */
 
     if ((status = cupsdCheckPolicy(dest->op_policy_ptr, con, NULL)) != HTTP_OK)
     {
-      send_http_error(con, status, NULL);
+      send_http_error(con, status, dest);
       return;
     }
 
@@ -6850,7 +7100,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, NULL);
+    send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
     return;
   }
 
@@ -7108,7 +7358,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, NULL);
+      send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
       return;
     }
 
@@ -7854,7 +8104,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, NULL);
+    send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
     return;
   }
 
@@ -8096,7 +8346,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, NULL);
+    send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
     return;
   }
 
@@ -8255,72 +8505,105 @@ 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 */
-
+#  if !defined(HAVE_KRB5_CC_NEW_UNIQUE) && !defined(HAVE_HEIMDAL)
+  cupsdLogMessage(CUPSD_LOG_INFO,
+                  "Sorry, your version of Kerberos does not support delegated "
+                 "credentials!");
+  return;
 
-#  ifdef __APPLE__
-   /*
-    * If the weak-linked GSSAPI/Kerberos library is not present, don't try
-    * to use it...
-    */
+#  else
+  krb5_error_code      error;          /* Kerberos error code */
+  OM_uint32            major_status,   /* Major status code */
+                       minor_status;   /* Minor status code */
+  krb5_principal       principal;      /* Kerberos principal */
 
-    if (krb5_init_context == NULL)
-    {
-      cupsdLogMessage(CUPSD_LOG_DEBUG,
-                     "save_krb5_creds: GSSAPI/Kerberos framework is not "
-                     "present");
-      return;
-    }
-#  endif /* __APPLE__ */
 
+#   ifdef __APPLE__
  /*
-  * Setup a cached context for the job filters to use...
+  * If the weak-linked GSSAPI/Kerberos library is not present, don't try
+  * to use it...
   */
 
-  if (krb5_init_context(&krb_context))
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to initialize Kerberos context");
+  if (krb5_init_context == NULL)
     return;
-  }
+#    endif /* __APPLE__ */
 
  /*
   * We MUST create a file-based cache because memory-based caches are
   * only valid for the current process/address space.
-  */
+  *
+  * Due to various bugs/features in different versions of Kerberos, we
+  * need either the krb5_cc_new_unique() function or Heimdal's version
+  * of krb5_cc_gen_new() to create a new FILE: credential cache that
+  * can be passed to the backend.  These functions create a temporary
+  * file (typically in /tmp) containing the cached credentials, which
+  * are removed when we have successfully printed a job.
+  */
+
+#    ifdef HAVE_KRB5_CC_NEW_UNIQUE
+  if ((error = krb5_cc_new_unique(KerberosContext, "FILE", NULL,
+                                  &(job->ccache))) != 0)
+#    else /* HAVE_HEIMDAL */
+  if ((error = krb5_cc_gen_new(KerberosContext, &krb5_fcc_ops,
+                               &(job->ccache))) != 0)
+#    endif /* HAVE_KRB5_CC_NEW_UNIQUE */
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to create new credentials cache (%d/%s)",
+                    error, strerror(errno));
+    job->ccache = NULL;
+    return;
+  }
 
-#  ifdef HAVE_KRB5_CC_RESOLVE
-  if (krb5_cc_resolve(krb_context, "FILE:", &ccache))
-#  elif defined(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 */
+  if ((error = krb5_parse_name(KerberosContext, con->username, &principal)) != 0)
+  {
+    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to parse kerberos username (%d/%s)",
+                    error, strerror(errno));
+    krb5_cc_destroy(KerberosContext, job->ccache);
+    job->ccache = NULL;
+    return;
+  }
+
+  if ((error = krb5_cc_initialize(KerberosContext, job->ccache, principal)))
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to create new credentials");
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                    "Unable to initialize credentials cache (%d/%s)", error,
+                   strerror(errno));
+    krb5_cc_destroy(KerberosContext, job->ccache);
+    krb5_free_principal(KerberosContext, principal);
+    job->ccache = NULL;
     return;
   }
 
+  krb5_free_principal(KerberosContext, principal);
+
+ /*
+  * Copy the user's credentials to the new cache file...
+  */
+
   major_status = gss_krb5_copy_ccache(&minor_status, con->gss_delegated_cred,
-                                     ccache);
+                                     job->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);
+    krb5_cc_destroy(KerberosContext, job->ccache);
+    job->ccache = NULL;
     return;
   }
 
+ /*
+  * Add the KRB5CCNAME environment variable to the job so that the
+  * backend can use the credentials when printing.
+  */
+
   cupsdSetStringf(&(job->ccname), "KRB5CCNAME=FILE:%s",
-                  krb5_cc_get_name(krb_context, ccache));
+                  krb5_cc_get_name(KerberosContext, job->ccache));
 
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "[Job %d] save_krb5_creds: %s", job->id,
                   job->ccname);
-
-  krb5_cc_close(krb_context, ccache);
+#  endif /* HAVE_KRB5_CC_NEW_UNIQUE || HAVE_HEIMDAL */
 }
 #endif /* HAVE_GSSAPI && HAVE_KRB5_H */
 
@@ -8433,7 +8716,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, NULL);
+    send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
     return;
   }
 
@@ -8725,6 +9008,23 @@ send_http_error(
       printer && printer->num_auth_info_required > 0 &&
       !strcmp(printer->auth_info_required[0], "negotiate"))
     cupsdSendError(con, status, AUTH_NEGOTIATE);
+  else if (printer)
+  {
+    char       resource[HTTP_MAX_URI]; /* Resource portion of URI */
+    cupsd_location_t *auth;            /* Pointer to authentication element */
+
+
+    if (printer->type & CUPS_PRINTER_CLASS)
+      snprintf(resource, sizeof(resource), "/classes/%s", printer->name);
+    else
+      snprintf(resource, sizeof(resource), "/printers/%s", printer->name);
+
+    if ((auth = cupsdFindBest(resource, HTTP_POST)) == NULL ||
+        auth->type == AUTH_NONE)
+      auth = cupsdFindPolicyOp(printer->op_policy_ptr, IPP_PRINT_JOB);
+
+    cupsdSendError(con, status, auth ? auth->type : AUTH_NONE);
+  }
   else
     cupsdSendError(con, status, AUTH_NONE);
 
@@ -8952,7 +9252,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, NULL);
+    send_http_error(con, HTTP_UNAUTHORIZED, cupsdFindDest(job->dest));
     return;
   }
 
@@ -9884,5 +10184,5 @@ validate_user(cupsd_job_t    *job,       /* I - Job */
 
 
 /*
- * End of "$Id: ipp.c 6739 2007-07-26 19:30:03Z mike $".
+ * End of "$Id: ipp.c 7014 2007-10-10 21:57:43Z mike $".
  */