]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/job.c
Fix source file header text duplication text duplication.
[thirdparty/cups.git] / scheduler / job.c
index 2af8785ce1063e6e7f2bfbb1a0540e880a07fe5b..1b12fe5a152826774544c93db180c94678e0b82b 100644 (file)
@@ -1,16 +1,14 @@
 /*
- * "$Id$"
- *
  * Job management routines for the CUPS scheduler.
  *
- * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2007-2016 by Apple Inc.
  * Copyright 1997-2007 by Easy Software Products, all rights reserved.
  *
  * These coded instructions, statements, and computer programs are the
  * property of Apple Inc. and are protected by Federal copyright
  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
  * which should have been included with this file.  If this file is
- * file is missing or damaged, see the license at "http://www.cups.org/".
+ * missing or damaged, see the license at "http://www.cups.org/".
  */
 
 /*
@@ -119,6 +117,7 @@ static mime_filter_t        gziptoany_filter =
  */
 
 static int     compare_active_jobs(void *first, void *second, void *data);
+static int     compare_completed_jobs(void *first, void *second, void *data);
 static int     compare_jobs(void *first, void *second, void *data);
 static void    dump_job_history(cupsd_job_t *job);
 static void    finalize_job(cupsd_job_t *job, int set_job_state);
@@ -212,8 +211,6 @@ cupsdCancelJobs(const char *dest,   /* I - Destination to cancel */
                         "Job canceled by user.");
     }
   }
-
-  cupsdCheckJobs();
 }
 
 
@@ -230,14 +227,12 @@ cupsdCheckJobs(void)
                        *pclass;        /* Printer class destination */
   ipp_attribute_t      *attr;          /* Job attribute */
   time_t               curtime;        /* Current time */
+  const char           *reasons;       /* job-state-reasons value */
 
 
   curtime = time(NULL);
 
-  cupsdLogMessage(CUPSD_LOG_DEBUG2,
-                  "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d, "
-                  "curtime=%ld", cupsArrayCount(ActiveJobs), Sleeping,
-                  NeedReload, (long)curtime);
+  cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdCheckJobs: %d active jobs, sleeping=%d, ac-power=%d, reload=%d, curtime=%ld", cupsArrayCount(ActiveJobs), Sleeping, ACPower, NeedReload, (long)curtime);
 
   for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
        job;
@@ -326,12 +321,31 @@ cupsdCheckJobs(void)
        ((FilterLevel + job->pending_cost) < FilterLimit || FilterLevel == 0))
       cupsdContinueJob(job);
 
+   /*
+    * Skip jobs that where held-on-create
+    */
+
+    reasons = ippGetString(job->reasons, 0, NULL);
+    if (reasons && !strcmp(reasons, "job-held-on-create"))
+    {
+     /*
+      * Check whether the printer is still holding new jobs...
+      */
+
+      printer = cupsdFindDest(job->dest);
+
+      if (printer->holding_new_jobs)
+        continue;
+
+      ippSetString(job->attrs, &job->reasons, 0, "none");
+    }
+
    /*
     * Start pending jobs if the destination is available...
     */
 
     if (job->state_value == IPP_JOB_PENDING && !NeedReload &&
-        !Sleeping && !DoingShutdown && !job->printer)
+        (!Sleeping || ACPower) && !DoingShutdown && !job->printer)
     {
       printer = cupsdFindDest(job->dest);
       pclass  = NULL;
@@ -363,7 +377,7 @@ cupsdCheckJobs(void)
                         "Job aborted because the destination printer/class "
                         "has gone away.");
       }
-      else if (printer && !printer->holding_new_jobs)
+      else if (printer)
       {
        /*
         * See if the printer is available or remote and not printing a job;
@@ -373,16 +387,14 @@ cupsdCheckJobs(void)
         if (pclass)
        {
         /*
-         * Add/update a job-actual-printer-uri attribute for this job
+         * Add/update a job-printer-uri-actual attribute for this job
          * so that we know which printer actually printed the job...
          */
 
-          if ((attr = ippFindAttribute(job->attrs, "job-actual-printer-uri",
-                                      IPP_TAG_URI)) != NULL)
-            cupsdSetString(&attr->values[0].string.text, printer->uri);
+          if ((attr = ippFindAttribute(job->attrs, "job-printer-uri-actual", IPP_TAG_URI)) != NULL)
+            ippSetString(job->attrs, &attr, 0, printer->uri);
          else
-           ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI,
-                        "job-actual-printer-uri", NULL, printer->uri);
+           ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI, "job-printer-uri-actual", NULL, printer->uri);
 
           job->dirty = 1;
           cupsdMarkDirty(CUPSD_DIRTY_JOBS);
@@ -394,7 +406,9 @@ cupsdCheckJobs(void)
          * Start the job...
          */
 
+         cupsArraySave(ActiveJobs);
          start_job(job, printer);
+         cupsArrayRestore(ActiveJobs);
        }
       }
     }
@@ -444,6 +458,8 @@ cupsdCleanJobs(void)
         cupsdLogJob(job, CUPSD_LOG_DEBUG, "Removing document files.");
         remove_job_files(job);
 
+        cupsdMarkDirty(CUPSD_DIRTY_JOBS);
+
         if (job->history_time < JobHistoryUpdate || !JobHistoryUpdate)
          JobHistoryUpdate = job->history_time;
       }
@@ -491,7 +507,7 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
                                        /* Pipes used between filters */
   int                  envc;           /* Number of environment variables */
   struct stat          fileinfo;       /* Job file information */
-  int                  argc;           /* Number of arguments */
+  int                  argc = 0;       /* Number of arguments */
   char                 **argv = NULL,  /* Filter command-line arguments */
                        filename[1024], /* Job filename */
                        command[1024],  /* Full path to command */
@@ -563,12 +579,37 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
     * Local jobs get filtered...
     */
 
+    mime_type_t        *dst = job->printer->filetype;
+                                       /* Destination file type */
+
     snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
              job->id, job->current_file + 1);
     if (stat(filename, &fileinfo))
       fileinfo.st_size = 0;
 
-    filters = mimeFilter2(MimeDatabase, job->filetypes[job->current_file], (size_t)fileinfo.st_size, job->printer->filetype, &(job->cost));
+    if (job->retry_as_raster)
+    {
+     /*
+      * Need to figure out whether the printer supports image/pwg-raster or
+      * image/urf, and use the corresponding type...
+      */
+
+      char     type[MIME_MAX_TYPE];    /* MIME media type for printer */
+
+      snprintf(type, sizeof(type), "%s/image/urf", job->printer->name);
+      if ((dst = mimeType(MimeDatabase, "printer", type)) == NULL)
+      {
+       snprintf(type, sizeof(type), "%s/image/pwg-raster", job->printer->name);
+       dst = mimeType(MimeDatabase, "printer", type);
+      }
+
+      if (dst)
+        cupsdLogJob(job, CUPSD_LOG_DEBUG, "Retrying job as \"%s\".", strchr(dst->type, '/') + 1);
+      else
+        cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to retry job using a supported raster format.");
+    }
+
+    filters = mimeFilter2(MimeDatabase, job->filetypes[job->current_file], (size_t)fileinfo.st_size, dst, &(job->cost));
 
     if (!filters)
     {
@@ -707,8 +748,8 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
   * Add decompression/raw filter as needed...
   */
 
-  if (job->compressions[job->current_file] &&
-      (!job->printer->remote || job->num_files == 1))
+  if ((job->compressions[job->current_file] && (!job->printer->remote || job->num_files == 1)) ||
+      (!job->printer->remote && job->printer->raw && job->num_files > 1))
   {
    /*
     * Add gziptoany filter to the front of the list...
@@ -1208,7 +1249,7 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
       else if (stat(command, &backinfo))
        backroot = 0;
       else
-        backroot = !(backinfo.st_mode & (S_IRWXG | S_IRWXO));
+        backroot = !(backinfo.st_mode & (S_IWGRP | S_IRWXO));
 
       argv[0] = job->printer->sanitized_device_uri;
 
@@ -1430,6 +1471,30 @@ cupsdFindJob(int id)                     /* I - Job ID */
 }
 
 
+/*
+ * 'cupsdGetCompletedJobs()'- Generate a completed jobs list.
+ */
+
+cups_array_t *                         /* O - Array of jobs */
+cupsdGetCompletedJobs(
+    cupsd_printer_t *p)                        /* I - Printer */
+{
+  cups_array_t *list;                  /* Array of jobs */
+  cupsd_job_t  *job;                   /* Current job */
+
+
+  list = cupsArrayNew(compare_completed_jobs, NULL);
+
+  for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
+       job;
+       job = (cupsd_job_t *)cupsArrayNext(Jobs))
+    if ((!p || !_cups_strcasecmp(p->name, job->dest)) && job->state_value >= IPP_JOB_STOPPED && job->completed_time)
+      cupsArrayAdd(list, job);
+
+  return (list);
+}
+
+
 /*
  * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
  *                               or held jobs in a printer or class.
@@ -1484,9 +1549,10 @@ void
 cupsdLoadAllJobs(void)
 {
   char         filename[1024];         /* Full filename of job.cache file */
-  struct stat  fileinfo,               /* Information on job.cache file */
-               dirinfo;                /* Information on RequestRoot dir */
-
+  struct stat  fileinfo;               /* Information on job.cache file */
+  cups_dir_t   *dir;                   /* RequestRoot dir */
+  cups_dentry_t        *dent;                  /* Entry in RequestRoot */
+  int          load_cache = 1;         /* Load the job.cache file? */
 
 
  /*
@@ -1510,36 +1576,65 @@ cupsdLoadAllJobs(void)
 
   if (stat(filename, &fileinfo))
   {
-    fileinfo.st_mtime = 0;
+   /*
+    * No job.cache file...
+    */
+
+    load_cache = 0;
 
     if (errno != ENOENT)
       cupsdLogMessage(CUPSD_LOG_ERROR,
                       "Unable to get file information for \"%s\" - %s",
                      filename, strerror(errno));
   }
+  else if ((dir = cupsDirOpen(RequestRoot)) == NULL)
+  {
+   /*
+    * No spool directory...
+    */
 
-  if (stat(RequestRoot, &dirinfo))
+    load_cache = 0;
+  }
+  else
   {
-    dirinfo.st_mtime = 0;
+    while ((dent = cupsDirRead(dir)) != NULL)
+    {
+      if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c' && dent->fileinfo.st_mtime > fileinfo.st_mtime)
+      {
+       /*
+        * Job history file is newer than job.cache file...
+       */
 
-    if (errno != ENOENT)
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to get directory information for \"%s\" - %s",
-                     RequestRoot, strerror(errno));
+        load_cache = 0;
+       break;
+      }
+    }
+
+    cupsDirClose(dir);
   }
 
  /*
   * Load the most recent source for job data...
   */
 
-  if (dirinfo.st_mtime > fileinfo.st_mtime)
+  if (load_cache)
   {
+   /*
+    * Load the job.cache file...
+    */
+
+    load_job_cache(filename);
+  }
+  else
+  {
+   /*
+    * Load the job history files...
+    */
+
     load_request_root();
 
     load_next_job_id(filename);
   }
-  else
-    load_job_cache(filename);
 
  /*
   * Clean out old jobs as needed...
@@ -1625,10 +1720,13 @@ cupsdLoadJob(cupsd_job_t *job)          /* I - Job */
   job->file_time    = 0;
   job->history_time = 0;
 
-  if (job->state_value >= IPP_JOB_CANCELED &&
-      (attr = ippFindAttribute(job->attrs, "time-at-completed",
-                              IPP_TAG_INTEGER)) != NULL)
+  if ((attr = ippFindAttribute(job->attrs, "time-at-creation", IPP_TAG_INTEGER)) != NULL)
+    job->creation_time = attr->values[0].integer;
+
+  if (job->state_value >= IPP_JOB_CANCELED && (attr = ippFindAttribute(job->attrs, "time-at-completed", IPP_TAG_INTEGER)) != NULL)
   {
+    job->completed_time = attr->values[0].integer;
+
     if (JobHistory < INT_MAX)
       job->history_time = attr->values[0].integer + JobHistory;
     else
@@ -1743,9 +1841,12 @@ cupsdLoadJob(cupsd_job_t *job)           /* I - Job */
       ippSetString(job->attrs, &job->reasons, 0, "none");
   }
 
-  job->sheets     = ippFindAttribute(job->attrs, "job-media-sheets-completed",
-                                     IPP_TAG_INTEGER);
-  job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
+  job->impressions = ippFindAttribute(job->attrs, "job-impressions-completed", IPP_TAG_INTEGER);
+  job->sheets      = ippFindAttribute(job->attrs, "job-media-sheets-completed", IPP_TAG_INTEGER);
+  job->job_sheets  = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
+
+  if (!job->impressions)
+    job->impressions = ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-impressions-completed", 0);
 
   if (!job->priority)
   {
@@ -1774,6 +1875,12 @@ cupsdLoadJob(cupsd_job_t *job)           /* I - Job */
     cupsdSetString(&job->username, attr->values[0].string.text);
   }
 
+  if (!job->name)
+  {
+    if ((attr = ippFindAttribute(job->attrs, "job-name", IPP_TAG_NAME)) != NULL)
+      cupsdSetString(&job->name, attr->values[0].string.text);
+  }
+
  /*
   * Set the job hold-until time and state...
   */
@@ -1798,6 +1905,9 @@ cupsdLoadJob(cupsd_job_t *job)            /* I - Job */
     job->state_value              = IPP_JOB_PENDING;
   }
 
+  if ((attr = ippFindAttribute(job->attrs, "job-k-octets", IPP_TAG_INTEGER)) != NULL)
+    job->koctets = attr->values[0].integer;
+
   if (!job->num_files)
   {
    /*
@@ -2007,7 +2117,7 @@ cupsdMoveJob(cupsd_job_t     *job,        /* I - Job */
 
   if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
                                IPP_TAG_URI)) != NULL)
-    cupsdSetString(&(attr->values[0].string.text), p->uri);
+    ippSetString(job->attrs, &attr, 0, p->uri);
 
   cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
                 "Job #%d moved from %s to %s.", job->id, olddest,
@@ -2101,13 +2211,29 @@ cupsdSaveAllJobs(void)
        job;
        job = (cupsd_job_t *)cupsArrayNext(Jobs))
   {
+    if (job->printer && job->printer->temporary)
+    {
+     /*
+      * Don't save jobs on temporary printers...
+      */
+
+      continue;
+    }
+
     cupsFilePrintf(fp, "<Job %d>\n", job->id);
     cupsFilePrintf(fp, "State %d\n", job->state_value);
+    cupsFilePrintf(fp, "Created %ld\n", (long)job->creation_time);
+    if (job->completed_time)
+      cupsFilePrintf(fp, "Completed %ld\n", (long)job->completed_time);
     cupsFilePrintf(fp, "Priority %d\n", job->priority);
-    cupsFilePrintf(fp, "HoldUntil %d\n", (int)job->hold_until);
+    if (job->hold_until)
+      cupsFilePrintf(fp, "HoldUntil %ld\n", (long)job->hold_until);
     cupsFilePrintf(fp, "Username %s\n", job->username);
+    if (job->name)
+      cupsFilePutConf(fp, "Name", job->name);
     cupsFilePrintf(fp, "Destination %s\n", job->dest);
     cupsFilePrintf(fp, "DestType %d\n", job->dtype);
+    cupsFilePrintf(fp, "KOctets %d\n", job->koctets);
     cupsFilePrintf(fp, "NumFiles %d\n", job->num_files);
     for (i = 0; i < job->num_files; i ++)
       cupsFilePrintf(fp, "File %d %s/%s %d\n", i + 1, job->filetypes[i]->super,
@@ -2133,6 +2259,16 @@ cupsdSaveJob(cupsd_job_t *job)           /* I - Job */
   cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob(job=%p(%d)): job->attrs=%p",
                   job, job->id, job->attrs);
 
+  if (job->printer && job->printer->temporary)
+  {
+   /*
+    * Don't save jobs on temporary printers...
+    */
+
+    job->dirty = 0;
+    return;
+  }
+
   snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
 
   if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm & 0600)) == NULL)
@@ -2197,7 +2333,7 @@ cupsdSetJobHoldUntil(cupsd_job_t *job,    /* I - Job */
       attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
 
     if (attr)
-      cupsdSetString(&(attr->values[0].string.text), when);
+      ippSetString(job->attrs, &attr, 0, when);
     else
       attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
                           "job-hold-until", NULL, when);
@@ -2213,9 +2349,13 @@ cupsdSetJobHoldUntil(cupsd_job_t *job,   /* I - Job */
       cupsdMarkDirty(CUPSD_DIRTY_JOBS);
     }
 
-    ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
   }
 
+  if (strcmp(when, "no-hold"))
+    ippSetString(job->attrs, &job->reasons, 0, "job-hold-until-specified");
+  else
+    ippSetString(job->attrs, &job->reasons, 0, "none");
+
  /*
   * Update the hold time...
   */
@@ -2451,8 +2591,8 @@ cupsdSetJobState(
 
        if (attr)
        {
-         attr->value_tag = IPP_TAG_KEYWORD;
-         cupsdSetString(&(attr->values[0].string.text), "no-hold");
+         ippSetValueTag(job->attrs, &attr, IPP_TAG_KEYWORD);
+         ippSetString(job->attrs, &attr, 0, "no-hold");
        }
 
     default :
@@ -2684,7 +2824,8 @@ cupsdUnloadCompletedJobs(void)
       if (job->dirty)
         cupsdSaveJob(job);
 
-      unload_job(job);
+      if (!job->dirty)
+        unload_job(job);
     }
 }
 
@@ -2767,6 +2908,28 @@ compare_active_jobs(void *first, /* I - First job */
 }
 
 
+/*
+ * 'compare_completed_jobs()' - Compare the job IDs and completion times of two jobs.
+ */
+
+static int                             /* O - Difference */
+compare_completed_jobs(void *first,    /* I - First job */
+                       void *second,   /* I - Second job */
+                      void *data)      /* I - App data (not used) */
+{
+  int  diff;                           /* Difference */
+
+
+  (void)data;
+
+  if ((diff = ((cupsd_job_t *)second)->completed_time -
+              ((cupsd_job_t *)first)->completed_time) != 0)
+    return (diff);
+  else
+    return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
+}
+
+
 /*
  * 'compare_jobs()' - Compare the job IDs of two jobs.
  */
@@ -3043,7 +3206,7 @@ finalize_job(cupsd_job_t *job,            /* I - Job */
 
    /*
     * Convert the status to an exit code.  Due to the way the W* macros are
-    * implemented on MacOS X (bug?), we have to store the exit status in a
+    * implemented on macOS (bug?), we have to store the exit status in a
     * variable first and then convert...
     */
 
@@ -3381,6 +3544,13 @@ finalize_job(cupsd_job_t *job,           /* I - Job */
 
   cupsArrayRemove(PrintingJobs, job);
 
+ /*
+  * Clear informational messages...
+  */
+
+  if (job->status_level > CUPSD_LOG_ERROR)
+    job->printer->state_message[0] = '\0';
+
  /*
   * Apply any PPD updates...
   */
@@ -3402,13 +3572,6 @@ finalize_job(cupsd_job_t *job,           /* I - Job */
 
   job->printer->job = NULL;
   job->printer      = NULL;
-
- /*
-  * Try printing another job...
-  */
-
-  if (printer_state != IPP_PRINTER_STOPPED)
-    cupsdCheckJobs();
 }
 
 
@@ -3970,14 +4133,13 @@ load_job_cache(const char *filename)    /* I - job.cache filename */
     {
       if (job)
       {
-        cupsdLogMessage(CUPSD_LOG_ERROR, "Missing </Job> directive on line %d.",
-                       linenum);
+        cupsdLogMessage(CUPSD_LOG_ERROR, "Missing </Job> directive on line %d of %s.", linenum, filename);
         continue;
       }
 
       if (!value)
       {
-        cupsdLogMessage(CUPSD_LOG_ERROR, "Missing job ID on line %d.", linenum);
+        cupsdLogMessage(CUPSD_LOG_ERROR, "Missing job ID on line %d of %s.", linenum, filename);
        continue;
       }
 
@@ -3985,8 +4147,7 @@ load_job_cache(const char *filename)      /* I - job.cache filename */
 
       if (jobid < 1)
       {
-        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad job ID %d on line %d.", jobid,
-                       linenum);
+        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad job ID %d on line %d of %s.", jobid, linenum, filename);
         continue;
       }
 
@@ -3998,7 +4159,15 @@ load_job_cache(const char *filename)     /* I - job.cache filename */
        {
          cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Files have gone away.",
                          jobid);
-         continue;
+
+         /*
+          * job.cache file is out-of-date compared to spool directory; load
+          * that instead...
+          */
+
+         cupsFileClose(fp);
+          load_request_root();
+          return;
        }
       }
 
@@ -4025,7 +4194,7 @@ load_job_cache(const char *filename)      /* I - job.cache filename */
     else if (!job)
     {
       cupsdLogMessage(CUPSD_LOG_ERROR,
-                     "Missing <Job #> directive on line %d.", linenum);
+                     "Missing <Job #> directive on line %d of %s.", linenum, filename);
       continue;
     }
     else if (!_cups_strcasecmp(line, "</Job>"))
@@ -4034,12 +4203,20 @@ load_job_cache(const char *filename)    /* I - job.cache filename */
 
       if (job->state_value <= IPP_JOB_STOPPED && cupsdLoadJob(job))
        cupsArrayAdd(ActiveJobs, job);
+      else if (job->state_value > IPP_JOB_STOPPED)
+      {
+        if (!job->completed_time || !job->creation_time || !job->name || !job->koctets)
+       {
+         cupsdLoadJob(job);
+         unload_job(job);
+       }
+      }
 
       job = NULL;
     }
     else if (!value)
     {
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d.", linenum);
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d of %s.", linenum, filename);
       continue;
     }
     else if (!_cups_strcasecmp(line, "State"))
@@ -4051,9 +4228,21 @@ load_job_cache(const char *filename)     /* I - job.cache filename */
       else if (job->state_value > IPP_JOB_COMPLETED)
         job->state_value = IPP_JOB_COMPLETED;
     }
+    else if (!_cups_strcasecmp(line, "Name"))
+    {
+      cupsdSetString(&(job->name), value);
+    }
+    else if (!_cups_strcasecmp(line, "Created"))
+    {
+      job->creation_time = strtol(value, NULL, 10);
+    }
+    else if (!_cups_strcasecmp(line, "Completed"))
+    {
+      job->completed_time = strtol(value, NULL, 10);
+    }
     else if (!_cups_strcasecmp(line, "HoldUntil"))
     {
-      job->hold_until = atoi(value);
+      job->hold_until = strtol(value, NULL, 10);
     }
     else if (!_cups_strcasecmp(line, "Priority"))
     {
@@ -4071,14 +4260,17 @@ load_job_cache(const char *filename)    /* I - job.cache filename */
     {
       job->dtype = (cups_ptype_t)atoi(value);
     }
+    else if (!_cups_strcasecmp(line, "KOctets"))
+    {
+      job->koctets = atoi(value);
+    }
     else if (!_cups_strcasecmp(line, "NumFiles"))
     {
       job->num_files = atoi(value);
 
       if (job->num_files < 0)
       {
-       cupsdLogMessage(CUPSD_LOG_ERROR, "Bad NumFiles value %d on line %d.",
-                       job->num_files, linenum);
+       cupsdLogMessage(CUPSD_LOG_ERROR, "Bad NumFiles value %d on line %d of %s.", job->num_files, linenum, filename);
         job->num_files = 0;
        continue;
       }
@@ -4117,14 +4309,13 @@ load_job_cache(const char *filename)    /* I - job.cache filename */
       if (sscanf(value, "%d%*[ \t]%15[^/]/%255s%d", &number, super, type,
                  &compression) != 4)
       {
-        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File on line %d.", linenum);
+        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File on line %d of %s.", linenum, filename);
        continue;
       }
 
       if (number < 1 || number > job->num_files)
       {
-        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File number %d on line %d.",
-                       number, linenum);
+        cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File number %d on line %d of %s.", number, linenum, filename);
         continue;
       }
 
@@ -4158,14 +4349,13 @@ load_job_cache(const char *filename)    /* I - job.cache filename */
       }
     }
     else
-      cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown %s directive on line %d.",
-                      line, linenum);
+      cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown %s directive on line %d of %s.", line, linenum, filename);
   }
 
   if (job)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                   "Missing </Job> directive on line %d.", linenum);
+                   "Missing </Job> directive on line %d of %s.", linenum, filename);
     cupsdDeleteJob(job, CUPSD_JOB_PURGE);
   }
 
@@ -4375,6 +4565,7 @@ static void
 set_time(cupsd_job_t *job,             /* I - Job to update */
          const char  *name)            /* I - Name of attribute */
 {
+  char                 date_name[128]; /* date-time-at-xxx */
   ipp_attribute_t      *attr;          /* Time attribute */
   time_t               curtime;        /* Current time */
 
@@ -4389,8 +4580,18 @@ set_time(cupsd_job_t *job,               /* I - Job to update */
     attr->values[0].integer = curtime;
   }
 
+  snprintf(date_name, sizeof(date_name), "date-%s", name);
+
+  if ((attr = ippFindAttribute(job->attrs, date_name, IPP_TAG_ZERO)) != NULL)
+  {
+    attr->value_tag = IPP_TAG_DATE;
+    ippSetDate(job->attrs, &attr, 0, ippTimeToDate(curtime));
+  }
+
   if (!strcmp(name, "time-at-completed"))
   {
+    job->completed_time = curtime;
+
     if (JobHistory < INT_MAX && attr)
       job->history_time = attr->values[0].integer + JobHistory;
     else
@@ -4455,7 +4656,7 @@ start_job(cupsd_job_t     *job,           /* I - Job ID */
                                             "job-printer-state-message",
                                             IPP_TAG_TEXT);
   if (job->printer_message)
-    cupsdSetString(&(job->printer_message->values[0].string.text), "");
+    ippSetString(job->attrs, &job->printer_message, 0, "");
 
   ippSetString(job->attrs, &job->reasons, 0, "job-printing");
   cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL);
@@ -4623,7 +4824,7 @@ stop_job(cupsd_job_t       *job,  /* I - Job */
   FilterLevel -= job->cost;
   job->cost   = 0;
 
-  if (action == CUPSD_JOB_DEFAULT && !job->kill_time)
+  if (action == CUPSD_JOB_DEFAULT && !job->kill_time && job->backend > 0)
     job->kill_time = time(NULL) + JobKillDelay;
   else if (action >= CUPSD_JOB_FORCE)
     job->kill_time = 0;
@@ -4673,6 +4874,7 @@ unload_job(cupsd_job_t *job)              /* I - Job */
   job->attrs           = NULL;
   job->state           = NULL;
   job->reasons         = NULL;
+  job->impressions     = NULL;
   job->sheets          = NULL;
   job->job_sheets      = NULL;
   job->printer_message = NULL;
@@ -4694,6 +4896,8 @@ update_job(cupsd_job_t *job)              /* I - Job to check */
                *ptr;                   /* Pointer update... */
   int          loglevel,               /* Log level for message */
                event = 0;              /* Events? */
+  cupsd_printer_t *printer = job->printer;
+                                       /* Printer */
   static const char * const levels[] = /* Log levels */
                {
                  "NONE",
@@ -4731,6 +4935,25 @@ update_job(cupsd_job_t *job)             /* I - Job to check */
 
       cupsdLogJob(job, CUPSD_LOG_DEBUG, "PAGE: %s", message);
 
+      if (job->impressions)
+      {
+        if (!_cups_strncasecmp(message, "total ", 6))
+       {
+        /*
+         * Got a total count of pages from a backend or filter...
+         */
+
+         copies = atoi(message + 6);
+         copies -= ippGetInteger(job->impressions, 0); /* Just track the delta */
+       }
+       else if (!sscanf(message, "%*d%d", &copies))
+         copies = 1;
+
+        ippSetInteger(job->attrs, &job->impressions, 0, ippGetInteger(job->impressions, 0) + copies);
+        job->dirty = 1;
+       cupsdMarkDirty(CUPSD_DIRTY_JOBS);
+      }
+
       if (job->sheets)
       {
         if (!_cups_strncasecmp(message, "total ", 6))
@@ -4740,12 +4963,14 @@ update_job(cupsd_job_t *job)            /* I - Job to check */
          */
 
          copies = atoi(message + 6);
-         copies -= job->sheets->values[0].integer; /* Just track the delta */
+         copies -= ippGetInteger(job->sheets, 0); /* Just track the delta */
        }
        else if (!sscanf(message, "%*d%d", &copies))
          copies = 1;
 
-        job->sheets->values[0].integer += copies;
+        ippSetInteger(job->attrs, &job->sheets, 0, ippGetInteger(job->sheets, 0) + copies);
+        job->dirty = 1;
+       cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 
        if (job->printer->page_limit)
          cupsdUpdateQuota(job->printer, job->username, copies, 0);
@@ -4754,8 +4979,7 @@ update_job(cupsd_job_t *job)              /* I - Job to check */
       cupsdLogPage(job, message);
 
       if (job->sheets)
-       cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
-                     "Printed %d page(s).", job->sheets->values[0].integer);
+       cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job, "Printed %d page(s).", ippGetInteger(job->sheets, 0));
     }
     else if (loglevel == CUPSD_LOG_JOBSTATE)
     {
@@ -4766,7 +4990,10 @@ update_job(cupsd_job_t *job)             /* I - Job to check */
 
       cupsdLogJob(job, CUPSD_LOG_DEBUG, "JOBSTATE: %s", message);
 
-      ippSetString(job->attrs, &job->reasons, 0, message);
+      if (!strcmp(message, "cups-retry-as-raster"))
+        job->retry_as_raster = 1;
+      else
+        ippSetString(job->attrs, &job->reasons, 0, message);
     }
     else if (loglevel == CUPSD_LOG_STATE)
     {
@@ -4963,7 +5190,7 @@ update_job(cupsd_job_t *job)              /* I - Job to check */
         ptr = message;
 
       if (*ptr)
-        cupsdLogJob(job, loglevel, "%s", ptr);
+        cupsdLogJob(job, loglevel == CUPSD_LOG_INFO ? CUPSD_LOG_DEBUG : loglevel, "%s", ptr);
 
       if (loglevel < CUPSD_LOG_DEBUG &&
           strcmp(job->printer->state_message, ptr))
@@ -5044,10 +5271,11 @@ update_job(cupsd_job_t *job)            /* I - Job to check */
     finalize_job(job, 1);
 
    /*
-    * Check for new jobs...
+    * Try printing another job...
     */
 
-    cupsdCheckJobs();
+    if (printer->state != IPP_PRINTER_STOPPED)
+      cupsdCheckJobs();
   }
 }
 
@@ -5092,15 +5320,14 @@ update_job_attrs(cupsd_job_t *job,      /* I - Job to update */
   if (job->state_value != IPP_JOB_PROCESSING &&
       job->status_level == CUPSD_LOG_INFO)
   {
-    cupsdSetString(&(job->printer_message->values[0].string.text), "");
+    ippSetString(job->attrs, &job->printer_message, 0, "");
 
     job->dirty = 1;
     cupsdMarkDirty(CUPSD_DIRTY_JOBS);
   }
   else if (job->printer->state_message[0] && do_message)
   {
-    cupsdSetString(&(job->printer_message->values[0].string.text),
-                  job->printer->state_message);
+    ippSetString(job->attrs, &job->printer_message, 0, job->printer->state_message);
 
     job->dirty = 1;
     cupsdMarkDirty(CUPSD_DIRTY_JOBS);
@@ -5165,8 +5392,3 @@ update_job_attrs(cupsd_job_t *job,        /* I - Job to update */
   job->dirty = 1;
   cupsdMarkDirty(CUPSD_DIRTY_JOBS);
 }
-
-
-/*
- * End of "$Id$".
- */