+ job->attrs = NULL;
+ job->state = NULL;
+ job->sheets = NULL;
+ job->job_sheets = NULL;
+ job->printer_message = NULL;
+ job->printer_reasons = NULL;
+}
+
+
+/*
+ * 'update_job()' - Read a status update from a job's filters.
+ */
+
+void
+update_job(cupsd_job_t *job) /* I - Job to check */
+{
+ int i; /* Looping var */
+ int copies; /* Number of copies printed */
+ char message[1024], /* Message text */
+ *ptr; /* Pointer update... */
+ int loglevel, /* Log level for message */
+ event = 0; /* Events? */
+
+
+ while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
+ message, sizeof(message))) != NULL)
+ {
+ /*
+ * Process page and printer state messages as needed...
+ */
+
+ if (loglevel == CUPSD_LOG_PAGE)
+ {
+ /*
+ * Page message; send the message to the page_log file and update the
+ * job sheet count...
+ */
+
+ if (job->sheets)
+ {
+ if (!strncasecmp(message, "total ", 6))
+ {
+ /*
+ * Got a total count of pages from a backend or filter...
+ */
+
+ copies = atoi(message + 6);
+ copies -= job->sheets->values[0].integer; /* Just track the delta */
+ }
+ else if (!sscanf(message, "%*d%d", &copies))
+ copies = 1;
+
+ job->sheets->values[0].integer += copies;
+
+ if (job->printer->page_limit)
+ {
+ cupsd_quota_t *q = cupsdUpdateQuota(job->printer, job->username,
+ copies, 0);
+
+#ifdef __APPLE__
+ if (AppleQuotas && q->page_count == -3)
+ {
+ /*
+ * Quota limit exceeded, cancel job in progress immediately...
+ */
+
+ cupsdLogMessage(CUPSD_LOG_INFO,
+ "[Job %d] Canceled because pages exceed user %s "
+ "quota limit on printer %s (%s).",
+ job->id, job->username, job->printer->name,
+ job->printer->info);
+
+ cupsdCancelJob(job, 1, IPP_JOB_CANCELED);
+ return;
+ }
+#else
+ (void)q;
+#endif /* __APPLE__ */
+ }
+ }
+
+ cupsdLogPage(job, message);
+
+ if (job->sheets)
+ cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
+ "Printed %d page(s).", job->sheets->values[0].integer);
+ }
+ else if (loglevel == CUPSD_LOG_STATE)
+ {
+ if (!strcmp(message, "paused"))
+ {
+ cupsdStopPrinter(job->printer, 1);
+ return;
+ }
+ else
+ {
+ cupsdSetPrinterReasons(job->printer, message);
+ cupsdAddPrinterHistory(job->printer);
+ event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
+ }
+
+ update_job_attrs(job);
+ }
+ else if (loglevel == CUPSD_LOG_ATTR)
+ {
+ /*
+ * Set attribute(s)...
+ */
+
+ int num_attrs; /* Number of attributes */
+ cups_option_t *attrs; /* Attributes */
+ const char *attr; /* Attribute */
+
+
+ num_attrs = cupsParseOptions(message, 0, &attrs);
+
+ if ((attr = cupsGetOption("auth-info-required", num_attrs,
+ attrs)) != NULL)
+ {
+ cupsdSetAuthInfoRequired(job->printer, attr, NULL);
+ cupsdSetPrinterAttrs(job->printer);
+ cupsdSaveAllPrinters();
+ }
+
+ if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL)
+ {
+ cupsdSetString(&job->printer->alert, attr);
+ event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
+ }
+
+ if ((attr = cupsGetOption("printer-alert-description", num_attrs,
+ attrs)) != NULL)
+ {
+ cupsdSetString(&job->printer->alert_description, attr);
+ event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
+ }
+
+ cupsFreeOptions(num_attrs, attrs);
+ }
+#ifdef __APPLE__
+ else if (!strncmp(message, "recoverable:", 12))
+ {
+ cupsdSetPrinterReasons(job->printer,
+ "+com.apple.print.recoverable-warning");
+
+ ptr = message + 12;
+ while (isspace(*ptr & 255))
+ ptr ++;
+
+ cupsdSetString(&job->printer->recoverable, ptr);
+ cupsdAddPrinterHistory(job->printer);
+ event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
+ }
+ else if (!strncmp(message, "recovered:", 10))
+ {
+ cupsdSetPrinterReasons(job->printer,
+ "-com.apple.print.recoverable-warning");
+
+ ptr = message + 10;
+ while (isspace(*ptr & 255))
+ ptr ++;
+
+ cupsdSetString(&job->printer->recoverable, ptr);
+ cupsdAddPrinterHistory(job->printer);
+ event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
+ }
+#endif /* __APPLE__ */
+ else if (loglevel <= job->status_level)
+ {
+ /*
+ * Some message to show in the printer-state-message attribute...
+ */
+
+ if (loglevel != CUPSD_LOG_NOTICE)
+ job->status_level = loglevel;
+
+ strlcpy(job->printer->state_message, message,
+ sizeof(job->printer->state_message));
+ cupsdAddPrinterHistory(job->printer);
+ event |= CUPSD_EVENT_PRINTER_STATE_CHANGED;
+
+ update_job_attrs(job);
+ }
+
+ if (!strchr(job->status_buffer->buffer, '\n'))
+ break;
+ }
+
+ if ((event & CUPSD_EVENT_PRINTER_STATE_CHANGED))
+ cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE_CHANGED, job->printer, NULL,
+ (job->printer->type & CUPS_PRINTER_CLASS) ?
+ "Class \"%s\" state changed." :
+ "Printer \"%s\" state changed.",
+ job->printer->name);
+
+ if (ptr == NULL && !job->status_buffer->bufused)
+ {
+ /*
+ * See if all of the filters and the backend have returned their
+ * exit statuses.
+ */
+
+ for (i = 0; job->filters[i] < 0; i ++);
+
+ if (job->filters[i])
+ return;
+
+ if (job->current_file >= job->num_files && job->backend > 0)
+ return;
+
+ /*
+ * Handle the end of job stuff...
+ */
+
+ cupsdFinishJob(job);
+ }
+}
+
+
+/*
+ * 'update_job_attrs()' - Update the job-printer-* attributes.
+ */
+
+void
+update_job_attrs(cupsd_job_t *job) /* I - Job to update */
+{
+ int i; /* Looping var */
+ int num_reasons; /* Actual number of reasons */
+ const char * const *reasons; /* Reasons */
+ static const char *none = "none", /* "none" */
+ *paused = "paused";
+ /* "paused" */
+
+
+ /*
+ * Get/create the job-printer-state-* attributes...
+ */
+
+ if (!job->printer_message)
+ {
+ if ((job->printer_message = ippFindAttribute(job->attrs,
+ "job-printer-state-message",
+ IPP_TAG_TEXT)) == NULL)
+ job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT,
+ "job-printer-state-message",
+ NULL, "");
+ }
+
+ if (!job->printer_reasons)
+ job->printer_reasons = ippFindAttribute(job->attrs,
+ "job-printer-state-reasons",
+ IPP_TAG_KEYWORD);
+
+ /*
+ * If the job isn't printing, return now...
+ */
+
+ if (!job->printer)
+ return;
+
+ /*
+ * Otherwise copy the printer-state-message value...
+ */
+
+ if (job->printer->state_message[0])
+ cupsdSetString(&(job->printer_message->values[0].string.text),
+ job->printer->state_message);
+
+ /*
+ * ... and the printer-state-reasons value...
+ */
+
+ if (job->printer->num_reasons == 0)
+ {
+ num_reasons = 1;
+ reasons = job->printer->state == IPP_PRINTER_STOPPED ? &paused : &none;
+ }
+ else
+ {
+ num_reasons = job->printer->num_reasons;
+ reasons = (const char * const *)job->printer->reasons;
+ }
+
+ if (!job->printer_reasons || job->printer_reasons->num_values != num_reasons)
+ {
+ ippDeleteAttribute(job->attrs, job->printer_reasons);
+
+ job->printer_reasons = ippAddStrings(job->attrs,
+ IPP_TAG_JOB, IPP_TAG_KEYWORD,
+ "job-printer-state-reasons",
+ num_reasons, NULL, NULL);
+ }
+
+ for (i = 0; i < num_reasons; i ++)
+ cupsdSetString(&(job->printer_reasons->values[i].string.text), reasons[i]);