]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - scheduler/job.c
Merge changes from CUPS 1.6svn-r9968.
[thirdparty/cups.git] / scheduler / job.c
index 3d9c9e4e4e0ff63c31588380238f8382a0e448d7..59e06dd55843bcf5feefcde1df6c37385fa9576d 100644 (file)
 #include <grp.h>
 #include <cups/backend.h>
 #include <cups/dir.h>
+#ifdef __APPLE__
+#  include <IOKit/pwr_mgt/IOPMLib.h>
+#  ifdef HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H
+#    include <IOKit/pwr_mgt/IOPMLibPrivate.h>
+#  endif /* HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H */
+#endif /* __APPLE__ */
 
 
 /*
@@ -347,7 +353,10 @@ cupsdCheckJobs(void)
     * Start pending jobs if the destination is available...
     */
 
-    if (job->state_value == IPP_JOB_PENDING && !NeedReload && !Sleeping &&
+    if (job->state_value == IPP_JOB_PENDING && !NeedReload &&
+#ifndef kIOPMAssertionTypeDenySystemSleep
+        !Sleeping &&
+#endif /* !kIOPMAssertionTypeDenySystemSleep */
         !DoingShutdown && !job->printer)
     {
       printer = cupsdFindDest(job->dest);
@@ -471,6 +480,7 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
   int                  filterfds[2][2] = { { -1, -1 }, { -1, -1 } };
                                        /* Pipes used between filters */
   int                  envc;           /* Number of environment variables */
+  struct stat          fileinfo;       /* Job file information */
   char                 **argv = NULL,  /* Filter command-line arguments */
                        filename[1024], /* Job filename */
                        command[1024],  /* Full path to command */
@@ -479,7 +489,7 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
                                        /* Job title string */
                        copies[255],    /* # copies string */
                        *options,       /* Options string */
-                       *envp[MAX_ENV + 19],
+                       *envp[MAX_ENV + 21],
                                        /* Environment variables */
                        charset[255],   /* CHARSET env variable */
                        class_name[255],/* CLASS env variable */
@@ -496,6 +506,8 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
                        apple_language[255],
                                        /* APPLE_LANGUAGE env variable */
 #endif /* __APPLE__ */
+                       auth_info_required[255],
+                                       /* AUTH_INFO_REQUIRED env variable */
                        ppd[1024],      /* PPD env variable */
                        printer_info[255],
                                        /* PRINTER_INFO env variable */
@@ -503,6 +515,8 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
                                        /* PRINTER_LOCATION env variable */
                        printer_name[255],
                                        /* PRINTER env variable */
+                       *printer_state_reasons = NULL,
+                                       /* PRINTER_STATE_REASONS env var */
                        rip_max_cache[255];
                                        /* RIP_MAX_CACHE env variable */
 
@@ -539,8 +553,14 @@ cupsdContinueJob(cupsd_job_t *job) /* I - Job */
     * Local jobs get filtered...
     */
 
-    filters = mimeFilter(MimeDatabase, job->filetypes[job->current_file],
-                         job->printer->filetype, &(job->cost));
+    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],
+                          fileinfo.st_size, job->printer->filetype,
+                          &(job->cost));
 
     if (!filters)
     {
@@ -732,11 +752,11 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
     banner_page = 0;
   else if (job->job_sheets == NULL)
     banner_page = 0;
-  else if (strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
+  else if (_cups_strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
           job->current_file == 0)
     banner_page = 1;
   else if (job->job_sheets->num_values > 1 &&
-          strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
+          _cups_strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
           job->current_file == (job->num_files - 1))
     banner_page = 1;
   else
@@ -876,8 +896,60 @@ cupsdContinueJob(cupsd_job_t *job) /* I - Job */
   snprintf(printer_location, sizeof(printer_name), "PRINTER_LOCATION=%s",
            job->printer->location ? job->printer->location : "");
   snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", job->printer->name);
+  if (job->printer->num_reasons > 0)
+  {
+    char       *psrptr;                /* Pointer into PRINTER_STATE_REASONS */
+    size_t     psrlen;                 /* Size of PRINTER_STATE_REASONS */
+
+    for (psrlen = 22, i = 0; i < job->printer->num_reasons; i ++)
+      psrlen += strlen(job->printer->reasons[i]) + 1;
+
+    if ((printer_state_reasons = malloc(psrlen)) != NULL)
+    {
+     /*
+      * All of these strcpy's are safe because we allocated the psr string...
+      */
+
+      strcpy(printer_state_reasons, "PRINTER_STATE_REASONS=");
+      for (psrptr = printer_state_reasons + 22, i = 0;
+           i < job->printer->num_reasons;
+          i ++)
+      {
+        if (i)
+         *psrptr++ = ',';
+       strcpy(psrptr, job->printer->reasons[i]);
+       psrptr += strlen(psrptr);
+      }
+    }
+  }
   snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
 
+  if (job->printer->num_auth_info_required == 1)
+    snprintf(auth_info_required, sizeof(auth_info_required),
+             "AUTH_INFO_REQUIRED=%s",
+            job->printer->auth_info_required[0]);
+  else if (job->printer->num_auth_info_required == 2)
+    snprintf(auth_info_required, sizeof(auth_info_required),
+             "AUTH_INFO_REQUIRED=%s,%s",
+            job->printer->auth_info_required[0],
+            job->printer->auth_info_required[1]);
+  else if (job->printer->num_auth_info_required == 3)
+    snprintf(auth_info_required, sizeof(auth_info_required),
+             "AUTH_INFO_REQUIRED=%s,%s,%s",
+            job->printer->auth_info_required[0],
+            job->printer->auth_info_required[1],
+            job->printer->auth_info_required[2]);
+  else if (job->printer->num_auth_info_required == 4)
+    snprintf(auth_info_required, sizeof(auth_info_required),
+             "AUTH_INFO_REQUIRED=%s,%s,%s,%s",
+            job->printer->auth_info_required[0],
+            job->printer->auth_info_required[1],
+            job->printer->auth_info_required[2],
+            job->printer->auth_info_required[3]);
+  else
+    strlcpy(auth_info_required, "AUTH_INFO_REQUIRED=none",
+           sizeof(auth_info_required));
+
   envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
 
   envp[envc ++] = charset;
@@ -892,6 +964,8 @@ cupsdContinueJob(cupsd_job_t *job)  /* I - Job */
   envp[envc ++] = printer_info;
   envp[envc ++] = printer_location;
   envp[envc ++] = printer_name;
+  envp[envc ++] = printer_state_reasons ? printer_state_reasons :
+                                          "PRINTER_STATE_REASONS=none";
   envp[envc ++] = banner_page ? "CUPS_FILETYPE=job-sheet" :
                                 "CUPS_FILETYPE=document";
 
@@ -938,12 +1012,16 @@ cupsdContinueJob(cupsd_job_t *job)       /* I - Job */
     envp[envc ++] = class_name;
   }
 
-  if (job->auth_username)
-    envp[envc ++] = job->auth_username;
-  if (job->auth_domain)
-    envp[envc ++] = job->auth_domain;
-  if (job->auth_password)
-    envp[envc ++] = job->auth_password;
+  envp[envc ++] = auth_info_required;
+
+  for (i = 0;
+       i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
+       i ++)
+    if (job->auth_env[i])
+      envp[envc ++] = job->auth_env[i];
+    else
+      break;
+
   if (job->auth_uid)
     envp[envc ++] = job->auth_uid;
 
@@ -1158,6 +1236,8 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
   }
 
   free(argv);
+  if (printer_state_reasons)
+    free(printer_state_reasons);
 
   cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)update_job, NULL,
                  job);
@@ -1194,6 +1274,9 @@ cupsdContinueJob(cupsd_job_t *job)        /* I - Job */
     free(argv);
   }
 
+  if (printer_state_reasons)
+    free(printer_state_reasons);
+
   cupsdClosePipe(job->print_pipes);
   cupsdClosePipe(job->back_pipes);
   cupsdClosePipe(job->side_pipes);
@@ -1233,6 +1316,10 @@ void
 cupsdDeleteJob(cupsd_job_t       *job, /* I - Job */
                cupsd_jobaction_t action)/* I - Action */
 {
+  int  i;                              /* Looping var */
+  char filename[1024];                 /* Job filename */
+
+
   if (job->printer)
     finalize_job(job, 1);
 
@@ -1242,18 +1329,20 @@ cupsdDeleteJob(cupsd_job_t       *job,  /* I - Job */
     * Remove the job info file...
     */
 
-    char       filename[1024];         /* Job filename */
-
     snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
             job->id);
-    unlink(filename);
+    if (Classification)
+      cupsdRemoveFile(filename);
+    else
+      unlink(filename);
   }
 
   cupsdClearString(&job->username);
   cupsdClearString(&job->dest);
-  cupsdClearString(&job->auth_username);
-  cupsdClearString(&job->auth_domain);
-  cupsdClearString(&job->auth_password);
+  for (i = 0;
+       i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
+       i ++)
+    cupsdClearString(job->auth_env + i);
   cupsdClearString(&job->auth_uid);
 
   if (job->num_files > 0)
@@ -1261,7 +1350,22 @@ cupsdDeleteJob(cupsd_job_t       *job,   /* I - Job */
     free(job->compressions);
     free(job->filetypes);
 
-    job->num_files = 0;
+    if (action == CUPSD_JOB_PURGE)
+    {
+      while (job->num_files > 0)
+      {
+       snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
+                job->id, job->num_files);
+       if (Classification)
+         cupsdRemoveFile(filename);
+       else
+         unlink(filename);
+
+       job->num_files --;
+      }
+    }
+    else
+      job->num_files = 0;
   }
 
   if (job->history)
@@ -1336,7 +1440,7 @@ cupsdGetPrinterJobCount(
   for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
        job;
        job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
-    if (job->dest && !strcasecmp(job->dest, dest))
+    if (job->dest && !_cups_strcasecmp(job->dest, dest))
       count ++;
 
   return (count);
@@ -1359,7 +1463,7 @@ cupsdGetUserJobCount(
   for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
        job;
        job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
-    if (!strcasecmp(job->username, username))
+    if (!_cups_strcasecmp(job->username, username))
       count ++;
 
   return (count);
@@ -1447,6 +1551,7 @@ cupsdLoadAllJobs(void)
 int                                    /* O - 1 on success, 0 on failure */
 cupsdLoadJob(cupsd_job_t *job)         /* I - Job */
 {
+  int                  i;              /* Looping var */
   char                 jobfile[1024];  /* Job filename */
   cups_file_t          *fp;            /* Job file */
   int                  fileid;         /* Current file ID */
@@ -1480,16 +1585,25 @@ cupsdLoadJob(cupsd_job_t *job)          /* I - Job */
   snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id);
   if ((fp = cupsFileOpen(jobfile, "r")) == NULL)
   {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                   "[Job %d] Unable to open job control file \"%s\" - %s!",
-                   job->id, jobfile, strerror(errno));
-    goto error;
+    char newfile[1024];                        /* New job filename */
+
+    snprintf(newfile, sizeof(newfile), "%s/c%05d.N", RequestRoot, job->id);
+    if ((fp = cupsFileOpen(newfile, "r")) == NULL)
+    {
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                     "[Job %d] Unable to open job control file \"%s\": %s",
+                     job->id, jobfile, strerror(errno));
+      goto error;
+    }
+
+    unlink(jobfile);
+    rename(newfile, jobfile);
   }
 
   if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                   "[Job %d] Unable to read job control file \"%s\"!", job->id,
+                   "[Job %d] Unable to read job control file \"%s\".", job->id,
                    jobfile);
     cupsFileClose(fp);
     goto error;
@@ -1693,21 +1807,22 @@ cupsdLoadJob(cupsd_job_t *job)          /* I - Job */
   {
     snprintf(jobfile, sizeof(jobfile), "%s/a%05d", RequestRoot, job->id);
 
-    cupsdClearString(&job->auth_username);
-    cupsdClearString(&job->auth_domain);
-    cupsdClearString(&job->auth_password);
+    for (i = 0;
+        i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
+        i ++)
+      cupsdClearString(job->auth_env + i);
     cupsdClearString(&job->auth_uid);
 
     if ((fp = cupsFileOpen(jobfile, "r")) != NULL)
     {
-      int      i,                      /* Looping var */
-               bytes;                  /* Size of auth data */
-      char     line[255],              /* Line from file */
-               data[255];              /* Decoded data */
+      int      bytes;                  /* Size of auth data */
+      char     line[65536],            /* Line from file */
+               data[65536];            /* Decoded data */
 
 
       for (i = 0;
            i < destptr->num_auth_info_required &&
+              i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0])) &&
               cupsFileGets(fp, line, sizeof(line));
           i ++)
       {
@@ -1715,11 +1830,13 @@ cupsdLoadJob(cupsd_job_t *job)          /* I - Job */
         httpDecode64_2(data, &bytes, line);
 
        if (!strcmp(destptr->auth_info_required[i], "username"))
-         cupsdSetStringf(&job->auth_username, "AUTH_USERNAME=%s", data);
+         cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s", data);
        else if (!strcmp(destptr->auth_info_required[i], "domain"))
-         cupsdSetStringf(&job->auth_domain, "AUTH_DOMAIN=%s", data);
+         cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s", data);
        else if (!strcmp(destptr->auth_info_required[i], "password"))
-         cupsdSetStringf(&job->auth_password, "AUTH_PASSWORD=%s", data);
+         cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s", data);
+        else if (!strcmp(destptr->auth_info_required[i], "negotiate"))
+         cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s", line);
       }
 
       if (cupsFileGets(fp, line, sizeof(line)) && isdigit(line[0] & 255))
@@ -1755,7 +1872,10 @@ cupsdLoadJob(cupsd_job_t *job)           /* I - Job */
 
   job->num_files = 0;
 
-  unlink(jobfile);
+  if (Classification)
+    cupsdRemoveFile(jobfile);
+  else
+    unlink(jobfile);
 
   return (0);
 }
@@ -1796,8 +1916,9 @@ cupsdMoveJob(cupsd_job_t     *job,        /* I - Job */
   * Change the destination information...
   */
 
-  cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
-                   "Stopping job prior to move.");
+  if (job->state_value > IPP_JOB_HELD)
+    cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
+                    "Stopping job prior to move.");
 
   cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, oldp, job,
                 "Job #%d moved from %s to %s.", job->id, olddest,
@@ -1869,30 +1990,19 @@ void
 cupsdSaveAllJobs(void)
 {
   int          i;                      /* Looping var */
-  cups_file_t  *fp;                    /* Job cache file */
-  char         temp[1024];             /* Temporary string */
+  cups_file_t  *fp;                    /* job.cache file */
+  char         filename[1024],         /* job.cache filename */
+               temp[1024];             /* Temporary string */
   cupsd_job_t  *job;                   /* Current job */
   time_t       curtime;                /* Current time */
   struct tm    *curdate;               /* Current date */
 
 
-  snprintf(temp, sizeof(temp), "%s/job.cache", CacheDir);
-  if ((fp = cupsFileOpen(temp, "w")) == NULL)
-  {
-    cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "Unable to create job cache file \"%s\" - %s",
-                    temp, strerror(errno));
+  snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir);
+  if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
     return;
-  }
-
-  cupsdLogMessage(CUPSD_LOG_INFO, "Saving job cache file \"%s\"...", temp);
-
- /*
-  * Restrict access to the file...
-  */
 
-  fchown(cupsFileNumber(fp), getuid(), Group);
-  fchmod(cupsFileNumber(fp), ConfigFilePerm);
+  cupsdLogMessage(CUPSD_LOG_INFO, "Saving job.cache...");
 
  /*
   * Write a small header to the file...
@@ -1928,7 +2038,7 @@ cupsdSaveAllJobs(void)
     cupsFilePuts(fp, "</Job>\n");
   }
 
-  cupsFileClose(fp);
+  cupsdCloseCreatedConfFile(fp, filename);
 }
 
 
@@ -1939,7 +2049,8 @@ cupsdSaveAllJobs(void)
 void
 cupsdSaveJob(cupsd_job_t *job)         /* I - Job */
 {
-  char         filename[1024];         /* Job control filename */
+  char         filename[1024],         /* Job control filename */
+               newfile[1024];          /* New job control filename */
   cups_file_t  *fp;                    /* Job file */
 
 
@@ -1947,12 +2058,13 @@ cupsdSaveJob(cupsd_job_t *job)          /* I - Job */
                   job, job->id, job->attrs);
 
   snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
+  snprintf(newfile, sizeof(newfile), "%s/c%05d.N", RequestRoot, job->id);
 
-  if ((fp = cupsFileOpen(filename, "w")) == NULL)
+  if ((fp = cupsFileOpen(newfile, "w")) == NULL)
   {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                   "[Job %d] Unable to create job control file \"%s\" - %s.",
-                   job->id, filename, strerror(errno));
+                   "[Job %d] Unable to create job control file \"%s\": %s",
+                   job->id, newfile, strerror(errno));
     return;
   }
 
@@ -1963,12 +2075,28 @@ cupsdSaveJob(cupsd_job_t *job)          /* I - Job */
 
   if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL,
                  job->attrs) != IPP_DATA)
+  {
     cupsdLogMessage(CUPSD_LOG_ERROR,
-                    "[Job %d] Unable to write job control file!", job->id);
-
-  cupsFileClose(fp);
+                    "[Job %d] Unable to write job control file.", job->id);
+    cupsFileClose(fp);
+    unlink(newfile);
+    return;
+  }
 
-  job->dirty = 0;
+  if (cupsFileClose(fp))
+    cupsdLogMessage(CUPSD_LOG_ERROR,
+                   "[Job %d] Unable to close job control file: %s",
+                   job->id, strerror(errno));
+  else
+  {
+    unlink(filename);
+    if (rename(newfile, filename))
+      cupsdLogMessage(CUPSD_LOG_ERROR,
+                      "[Job %d] Unable to finalize job control file: %s",
+                     job->id, strerror(errno));
+    else
+      job->dirty = 0;
+  }
 }
 
 
@@ -2214,14 +2342,14 @@ cupsdSetJobState(
   if (!cupsdLoadJob(job))
     return;
 
 /*
-   * Don't do anything if the state is unchanged and we aren't purging the
-   * job...
-   */
+ /*
+  * Don't do anything if the state is unchanged and we aren't purging the
+  * job...
+  */
 
-   oldstate = job->state_value;
-   if (newstate == oldstate && action != CUPSD_JOB_PURGE)
-     return;
+  oldstate = job->state_value;
+  if (newstate == oldstate && action != CUPSD_JOB_PURGE)
+    return;
 
  /*
   * Stop any processes that are working on the current job...
@@ -2376,9 +2504,11 @@ cupsdSetJobState(
                          "Unable to remove authentication cache: %s",
                          strerror(errno));
 
-       cupsdClearString(&job->auth_username);
-       cupsdClearString(&job->auth_domain);
-       cupsdClearString(&job->auth_password);
+       for (i = 0;
+            i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
+            i ++)
+         cupsdClearString(job->auth_env + i);
+
        cupsdClearString(&job->auth_uid);
 
        /*
@@ -2392,7 +2522,10 @@ cupsdSetJobState(
          {
            snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
                     job->id, i);
-           unlink(filename);
+           if (Classification)
+             cupsdRemoveFile(filename);
+           else
+             unlink(filename);
          }
 
          if (job->num_files > 0)
@@ -2507,6 +2640,8 @@ compare_active_jobs(void *first,  /* I - First job */
   int  diff;                           /* Difference */
 
 
+  (void)data;
+
   if ((diff = ((cupsd_job_t *)second)->priority -
               ((cupsd_job_t *)first)->priority) != 0)
     return (diff);
@@ -2524,6 +2659,8 @@ compare_jobs(void *first,         /* I - First job */
              void *second,             /* I - Second job */
             void *data)                /* I - App data (not used) */
 {
+  (void)data;
+
   return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
 }
 
@@ -2773,7 +2910,6 @@ finalize_job(cupsd_job_t *job,            /* I - Job */
 
     int exit_code;                     /* Exit code from backend */
 
-
    /*
     * 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
@@ -2946,6 +3082,57 @@ finalize_job(cupsd_job_t *job,           /* I - Job */
            message   = "Job held for authentication.";
           }
           break;
+
+      case CUPS_BACKEND_RETRY :
+         if (job_state == IPP_JOB_COMPLETED)
+         {
+          /*
+           * Hold the job if the number of retries is less than the
+           * JobRetryLimit, otherwise abort the job.
+           */
+
+           job->tries ++;
+
+           if (job->tries > JobRetryLimit && JobRetryLimit > 0)
+           {
+            /*
+             * Too many tries...
+             */
+
+             snprintf(buffer, sizeof(buffer),
+                      "Job aborted after %d unsuccessful attempts.",
+                      JobRetryLimit);
+             job_state = IPP_JOB_ABORTED;
+             message   = buffer;
+           }
+           else
+           {
+            /*
+             * Try again in N seconds...
+             */
+
+             snprintf(buffer, sizeof(buffer),
+                      "Job held for %d seconds since it could not be sent.",
+                      JobRetryInterval);
+
+             job->hold_until = time(NULL) + JobRetryInterval;
+             job_state       = IPP_JOB_HELD;
+             message         = buffer;
+           }
+         }
+          break;
+
+      case CUPS_BACKEND_RETRY_CURRENT :
+        /*
+         * Mark the job as pending and retry on the same printer...
+         */
+
+         if (job_state == IPP_JOB_COMPLETED)
+         {
+           job_state = IPP_JOB_PENDING;
+           message   = "Retrying job on same printer.";
+         }
+          break;
     }
   }
   else if (job->status > 0)
@@ -2966,7 +3153,7 @@ finalize_job(cupsd_job_t *job,            /* I - Job */
   * Update the printer and job state.
   */
 
-  if (job_state != job->state_value)
+  if (set_job_state && job_state != job->state_value)
     cupsdSetJobState(job, job_state, CUPSD_JOB_DEFAULT, "%s", message);
 
   cupsdSetPrinterState(job->printer, printer_state,
@@ -2975,7 +3162,9 @@ finalize_job(cupsd_job_t *job,            /* I - Job */
 
   if (job->history)
   {
-    if (job->status)
+    if (job->status &&
+        (job->state_value == IPP_JOB_ABORTED ||
+         job->state_value == IPP_JOB_STOPPED))
       dump_job_history(job);
     else
       free_job_history(job);
@@ -3182,6 +3371,9 @@ get_options(cupsd_job_t *job,             /* I - Job */
   * Then allocate/reallocate the option buffer as needed...
   */
 
+  if (newlength == 0)                  /* This can never happen, but Clang */
+    newlength = 1;                     /* thinks it can... */
+
   if (newlength > optlength || !options)
   {
     if (!options)
@@ -3260,11 +3452,11 @@ get_options(cupsd_job_t *job,           /* I - Job */
            !strncmp(attr->name, "number-up", 9) ||
           !strcmp(attr->name, "page-ranges") ||
           !strcmp(attr->name, "page-set") ||
-          !strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
-          !strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") ||
-          !strcasecmp(attr->name, "com.apple.print.PrintSettings."
+          !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
+          !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") ||
+          !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
                                   "PMTotalSidesImaged..n.") ||
-          !strcasecmp(attr->name, "com.apple.print.PrintSettings."
+          !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
                                   "PMTotalBeginPages..n.")) &&
          banner_page)
         continue;
@@ -3514,15 +3706,9 @@ load_job_cache(const char *filename)     /* I - job.cache filename */
   * Open the job.cache file...
   */
 
-  if ((fp = cupsFileOpen(filename, "r")) == NULL)
+  if ((fp = cupsdOpenConfFile(filename)) == NULL)
   {
-    if (errno != ENOENT)
-      cupsdLogMessage(CUPSD_LOG_ERROR,
-                      "Unable to open job cache file \"%s\": %s",
-                      filename, strerror(errno));
-
     load_request_root();
-
     return;
   }
 
@@ -3538,12 +3724,12 @@ load_job_cache(const char *filename)    /* I - job.cache filename */
 
   while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
   {
-    if (!strcasecmp(line, "NextJobId"))
+    if (!_cups_strcasecmp(line, "NextJobId"))
     {
       if (value)
         NextJobId = atoi(value);
     }
-    else if (!strcasecmp(line, "<Job"))
+    else if (!_cups_strcasecmp(line, "<Job"))
     {
       if (job)
       {
@@ -3570,9 +3756,13 @@ load_job_cache(const char *filename)     /* I - job.cache filename */
       snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, jobid);
       if (access(jobfile, 0))
       {
-        cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Files have gone away!",
-                       jobid);
-        continue;
+       snprintf(jobfile, sizeof(jobfile), "%s/c%05d.N", RequestRoot, jobid);
+       if (access(jobfile, 0))
+       {
+         cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Files have gone away!",
+                         jobid);
+         continue;
+       }
       }
 
       job = calloc(1, sizeof(cupsd_job_t));
@@ -3602,7 +3792,7 @@ load_job_cache(const char *filename)      /* I - job.cache filename */
                      "Missing <Job #> directive on line %d!", linenum);
       continue;
     }
-    else if (!strcasecmp(line, "</Job>"))
+    else if (!_cups_strcasecmp(line, "</Job>"))
     {
       cupsArrayAdd(Jobs, job);
 
@@ -3616,7 +3806,7 @@ load_job_cache(const char *filename)      /* I - job.cache filename */
       cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d!", linenum);
       continue;
     }
-    else if (!strcasecmp(line, "State"))
+    else if (!_cups_strcasecmp(line, "State"))
     {
       job->state_value = (ipp_jstate_t)atoi(value);
 
@@ -3625,27 +3815,27 @@ 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 (!strcasecmp(line, "HoldUntil"))
+    else if (!_cups_strcasecmp(line, "HoldUntil"))
     {
       job->hold_until = atoi(value);
     }
-    else if (!strcasecmp(line, "Priority"))
+    else if (!_cups_strcasecmp(line, "Priority"))
     {
       job->priority = atoi(value);
     }
-    else if (!strcasecmp(line, "Username"))
+    else if (!_cups_strcasecmp(line, "Username"))
     {
       cupsdSetString(&job->username, value);
     }
-    else if (!strcasecmp(line, "Destination"))
+    else if (!_cups_strcasecmp(line, "Destination"))
     {
       cupsdSetString(&job->dest, value);
     }
-    else if (!strcasecmp(line, "DestType"))
+    else if (!_cups_strcasecmp(line, "DestType"))
     {
       job->dtype = (cups_ptype_t)atoi(value);
     }
-    else if (!strcasecmp(line, "NumFiles"))
+    else if (!_cups_strcasecmp(line, "NumFiles"))
     {
       job->num_files = atoi(value);
 
@@ -3681,7 +3871,7 @@ load_job_cache(const char *filename)      /* I - job.cache filename */
        }
       }
     }
-    else if (!strcasecmp(line, "File"))
+    else if (!_cups_strcasecmp(line, "File"))
     {
       int      number,                 /* File number */
                compression;            /* Compression value */
@@ -3777,7 +3967,7 @@ load_next_job_id(const char *filename)    /* I - job.cache filename */
 
   while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
   {
-    if (!strcasecmp(line, "NextJobId"))
+    if (!_cups_strcasecmp(line, "NextJobId"))
     {
       if (value)
       {
@@ -3926,6 +4116,9 @@ start_job(cupsd_job_t     *job,           /* I - Job ID */
   if (!cupsdLoadJob(job))
     return;
 
+  if (job->printer_message)
+    cupsdSetString(&(job->printer_message->values[0].string.text), "");
+
   cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL);
   cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
   cupsdSetPrinterReasons(printer, "-cups-remote-pending,"
@@ -3970,9 +4163,6 @@ start_job(cupsd_job_t     *job,           /* I - Job ID */
   job->status_buffer = cupsdStatBufNew(job->status_pipes[0], NULL);
   job->status_level  = CUPSD_LOG_INFO;
 
-  if (job->printer_message)
-    cupsdSetString(&(job->printer_message->values[0].string.text), "");
-
  /*
   * Create the backchannel pipes and make them non-blocking...
   */
@@ -4168,7 +4358,7 @@ update_job(cupsd_job_t *job)              /* I - Job to check */
 
       if (job->sheets)
       {
-        if (!strncasecmp(message, "total ", 6))
+        if (!_cups_strncasecmp(message, "total ", 6))
        {
         /*
          * Got a total count of pages from a backend or filter...