]> git.ipfire.org Git - thirdparty/cups.git/blame - scheduler/job.c
Sync up with CUPS 1.6svn-r10269 (changes from Zin TOT merged into cups.org TOT)
[thirdparty/cups.git] / scheduler / job.c
CommitLineData
ef416fc2 1/*
b19ccc9e 2 * "$Id: job.c 7902 2008-09-03 14:20:17Z mike $"
ef416fc2 3 *
7cf5915e 4 * Job management routines for the CUPS scheduler.
ef416fc2 5 *
12f89d24 6 * Copyright 2007-2012 by Apple Inc.
f7deaa1a 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
ef416fc2 14 *
15 * Contents:
16 *
b9faaae1 17 * cupsdAddJob() - Add a new job to the job queue.
bd7854cb 18 * cupsdCancelJobs() - Cancel all jobs for the given
b9faaae1 19 * destination/user.
ed6e7faf
MS
20 * cupsdCheckJobs() - Check the pending jobs and start any if the
21 * destination is available.
ef416fc2 22 * cupsdCleanJobs() - Clean out old jobs.
b9faaae1 23 * cupsdContinueJob() - Continue printing with the next file in a job.
3dfe78b3 24 * cupsdDeleteJob() - Free all memory used by a job.
ef416fc2 25 * cupsdFreeAllJobs() - Free all jobs from memory.
26 * cupsdFindJob() - Find the specified job.
ed6e7faf
MS
27 * cupsdGetPrinterJobCount() - Get the number of pending, processing, or held
28 * jobs in a printer or class.
29 * cupsdGetUserJobCount() - Get the number of pending, processing, or held
30 * jobs for a user.
ef416fc2 31 * cupsdLoadAllJobs() - Load all jobs from disk.
b9faaae1 32 * cupsdLoadJob() - Load a single job.
ef416fc2 33 * cupsdMoveJob() - Move the specified job to a different
34 * destination.
35 * cupsdReleaseJob() - Release the specified job.
36 * cupsdRestartJob() - Restart the specified job.
bd7854cb 37 * cupsdSaveAllJobs() - Save a summary of all jobs to disk.
ef416fc2 38 * cupsdSaveJob() - Save a job to disk.
b9faaae1 39 * cupsdSetJobHoldUntil() - Set the hold time for a job.
ed6e7faf
MS
40 * cupsdSetJobPriority() - Set the priority of a job, moving it up/down
41 * in the list as needed.
b9faaae1 42 * cupsdSetJobState() - Set the state of the specified print job.
ef416fc2 43 * cupsdStopAllJobs() - Stop all print jobs.
bd7854cb 44 * cupsdUnloadCompletedJobs() - Flush completed job history from memory.
bd7854cb 45 * compare_active_jobs() - Compare the job IDs and priorities of two
46 * jobs.
ef416fc2 47 * compare_jobs() - Compare the job IDs of two jobs.
178cb736
MS
48 * dump_job_history() - Dump any debug messages for a job.
49 * free_job_history() - Free any log history.
b9faaae1
MS
50 * finalize_job() - Cleanup after job filter processes and support
51 * data.
52 * get_options() - Get a string containing the job options.
ed6e7faf
MS
53 * ipp_length() - Compute the size of the buffer needed to hold
54 * the textual IPP attributes.
bd7854cb 55 * load_job_cache() - Load jobs from the job.cache file.
ed6e7faf
MS
56 * load_next_job_id() - Load the NextJobId value from the job.cache
57 * file.
bd7854cb 58 * load_request_root() - Load jobs from the RequestRoot directory.
b9faaae1 59 * set_time() - Set one of the "time-at-xyz" attributes.
e1d6a774 60 * start_job() - Start a print job.
b9faaae1 61 * stop_job() - Stop a print job.
e1d6a774 62 * unload_job() - Unload a job from memory.
ed6e7faf 63 * update_job() - Read a status update from a job's filters.
bc44d920 64 * update_job_attrs() - Update the job-printer-* attributes.
ef416fc2 65 */
66
67/*
68 * Include necessary headers...
69 */
70
71#include "cupsd.h"
72#include <grp.h>
73#include <cups/backend.h>
74#include <cups/dir.h>
88f9aafc
MS
75#ifdef __APPLE__
76# include <IOKit/pwr_mgt/IOPMLib.h>
77# ifdef HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H
78# include <IOKit/pwr_mgt/IOPMLibPrivate.h>
79# endif /* HAVE_IOKIT_PWR_MGT_IOPMLIBPRIVATE_H */
80#endif /* __APPLE__ */
ef416fc2 81
82
b9faaae1
MS
83/*
84 * Design Notes for Job Management
85 * -------------------------------
86 *
87 * STATE CHANGES
88 *
89 * pending Do nothing/check jobs
90 * pending-held Send SIGTERM to filters and backend
91 * processing Do nothing/start job
92 * stopped Send SIGKILL to filters and backend
93 * canceled Send SIGTERM to filters and backend
94 * aborted Finalize
95 * completed Finalize
96 *
97 * Finalize clears the printer <-> job association, deletes the status
98 * buffer, closes all of the pipes, etc. and doesn't get run until all of
99 * the print processes are finished.
100 *
101 * UNLOADING OF JOBS (cupsdUnloadCompletedJobs)
102 *
103 * We unload the job attributes when they are not needed to reduce overall
104 * memory consumption. We don't unload jobs where job->state_value <
105 * IPP_JOB_STOPPED, job->printer != NULL, or job->access_time is recent.
106 *
107 * STARTING OF JOBS (start_job)
108 *
109 * When a job is started, a status buffer, several pipes, a security
110 * profile, and a backend process are created for the life of that job.
111 * These are shared for every file in a job. For remote print jobs, the
112 * IPP backend is provided with every file in the job and no filters are
113 * run.
114 *
115 * The job->printer member tracks which printer is printing a job, which
116 * can be different than the destination in job->dest for classes. The
117 * printer object also has a job pointer to track which job is being
118 * printed.
119 *
120 * PRINTING OF JOB FILES (cupsdContinueJob)
121 *
122 * Each file in a job is filtered by 0 or more programs. After getting the
123 * list of filters needed and the total cost, the job is either passed or
124 * put back to the processing state until the current FilterLevel comes down
125 * enough to allow printing.
126 *
127 * If we can print, we build a string for the print options and run each of
128 * the filters, piping the output from one into the next.
129 *
130 * JOB STATUS UPDATES (update_job)
131 *
132 * The update_job function gets called whenever there are pending messages
133 * on the status pipe. These generally are updates to the marker-*,
134 * printer-state-message, or printer-state-reasons attributes. On EOF,
135 * finalize_job is called to clean up.
136 *
137 * FINALIZING JOBS (finalize_job)
138 *
139 * When all filters and the backend are done, we set the job state to
140 * completed (no errors), aborted (filter errors or abort-job policy),
141 * pending-held (auth required or retry-job policy), or pending
142 * (retry-current-job or stop-printer policies) as appropriate.
143 *
144 * Then we close the pipes and free the status buffers and profiles.
145 *
146 * JOB FILE COMPLETION (process_children in main.c)
147 *
148 * For multiple-file jobs, process_children (in main.c) sees that all
149 * filters have exited and calls in to print the next file if there are
150 * more files in the job, otherwise it waits for the backend to exit and
151 * update_job to do the cleanup.
152 */
153
154
ef416fc2 155/*
156 * Local globals...
157 */
158
159static mime_filter_t gziptoany_filter =
160 {
161 NULL, /* Source type */
162 NULL, /* Destination type */
163 0, /* Cost */
164 "gziptoany" /* Filter program to run */
165 };
166
167
168/*
169 * Local functions...
170 */
171
172static int compare_active_jobs(void *first, void *second, void *data);
173static int compare_jobs(void *first, void *second, void *data);
178cb736 174static void dump_job_history(cupsd_job_t *job);
ef55b745 175static void finalize_job(cupsd_job_t *job, int set_job_state);
178cb736 176static void free_job_history(cupsd_job_t *job);
b9faaae1
MS
177static char *get_options(cupsd_job_t *job, int banner_page, char *copies,
178 size_t copies_size, char *title,
179 size_t title_size);
c7017ecc 180static size_t ipp_length(ipp_t *ipp);
bd7854cb 181static void load_job_cache(const char *filename);
182static void load_next_job_id(const char *filename);
183static void load_request_root(void);
b9faaae1 184static void set_time(cupsd_job_t *job, const char *name);
e1d6a774 185static void start_job(cupsd_job_t *job, cupsd_printer_t *printer);
b9faaae1 186static void stop_job(cupsd_job_t *job, cupsd_jobaction_t action);
e1d6a774 187static void unload_job(cupsd_job_t *job);
f899b121 188static void update_job(cupsd_job_t *job);
4509bb49 189static void update_job_attrs(cupsd_job_t *job, int do_message);
ef416fc2 190
191
192/*
b9faaae1 193 * 'cupsdAddJob()' - Add a new job to the job queue.
ef416fc2 194 */
195
196cupsd_job_t * /* O - New job record */
197cupsdAddJob(int priority, /* I - Job priority */
198 const char *dest) /* I - Job destination */
199{
200 cupsd_job_t *job; /* New job record */
201
202
91c84a35
MS
203 if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
204 return (NULL);
ef416fc2 205
89d46774 206 job->id = NextJobId ++;
207 job->priority = priority;
208 job->back_pipes[0] = -1;
209 job->back_pipes[1] = -1;
210 job->print_pipes[0] = -1;
211 job->print_pipes[1] = -1;
f7deaa1a 212 job->side_pipes[0] = -1;
213 job->side_pipes[1] = -1;
89d46774 214 job->status_pipes[0] = -1;
215 job->status_pipes[1] = -1;
ef416fc2 216
217 cupsdSetString(&job->dest, dest);
218
219 /*
220 * Add the new job to the "all jobs" and "active jobs" lists...
221 */
222
223 cupsArrayAdd(Jobs, job);
224 cupsArrayAdd(ActiveJobs, job);
225
226 return (job);
227}
228
229
230/*
b9faaae1 231 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user.
ef416fc2 232 */
233
234void
235cupsdCancelJobs(const char *dest, /* I - Destination to cancel */
236 const char *username, /* I - Username or NULL */
237 int purge) /* I - Purge jobs? */
238{
239 cupsd_job_t *job; /* Current job */
240
241
242 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
243 job;
244 job = (cupsd_job_t *)cupsArrayNext(Jobs))
0a682745 245 {
b9faaae1 246 if ((!job->dest || !job->username) && !cupsdLoadJob(job))
0a682745
MS
247 continue;
248
b9faaae1
MS
249 if ((!dest || !strcmp(job->dest, dest)) &&
250 (!username || !strcmp(job->username, username)))
ef416fc2 251 {
252 /*
253 * Cancel all jobs matching this destination/user...
254 */
255
b9faaae1
MS
256 if (purge)
257 cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_PURGE,
258 "Job purged by user.");
41681883 259 else if (job->state_value < IPP_JOB_CANCELED)
b9faaae1
MS
260 cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
261 "Job canceled by user.");
ef416fc2 262 }
0a682745 263 }
ef416fc2 264
265 cupsdCheckJobs();
266}
267
268
269/*
270 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
271 * is available.
272 */
273
274void
275cupsdCheckJobs(void)
276{
277 cupsd_job_t *job; /* Current job in queue */
278 cupsd_printer_t *printer, /* Printer destination */
279 *pclass; /* Printer class destination */
ac884b6a 280 ipp_attribute_t *attr; /* Job attribute */
238c3832 281 time_t curtime; /* Current time */
ef416fc2 282
283
bd7854cb 284 cupsdLogMessage(CUPSD_LOG_DEBUG2,
285 "cupsdCheckJobs: %d active jobs, sleeping=%d, reload=%d",
286 cupsArrayCount(ActiveJobs), Sleeping, NeedReload);
287
238c3832
MS
288 curtime = time(NULL);
289
ef416fc2 290 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs);
291 job;
292 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
293 {
238c3832
MS
294 /*
295 * Kill jobs if they are unresponsive...
296 */
297
298 if (job->kill_time && job->kill_time <= curtime)
299 {
dcb445bc 300 cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Stopping unresponsive job.",
10d09e33
MS
301 job->id);
302
238c3832
MS
303 stop_job(job, CUPSD_JOB_FORCE);
304 continue;
305 }
306
dcb445bc
MS
307 /*
308 * Cancel stuck jobs...
309 */
310
311 if (job->cancel_time && job->cancel_time <= curtime)
312 {
313 cupsdSetJobState(job, IPP_JOB_CANCELED, CUPSD_JOB_DEFAULT,
314 "Canceling stuck job after %d seconds.", MaxJobTime);
315 continue;
316 }
317
ef416fc2 318 /*
319 * Start held jobs if they are ready...
320 */
321
bd7854cb 322 if (job->state_value == IPP_JOB_HELD &&
ef416fc2 323 job->hold_until &&
238c3832 324 job->hold_until < curtime)
bd7854cb 325 {
09a101d6 326 if (job->pending_timeout)
91c84a35 327 {
dfd5680b
MS
328 /*
329 * This job is pending; check that we don't have an active Send-Document
330 * operation in progress on any of the client connections, then timeout
331 * the job so we can start printing...
332 */
333
334 cupsd_client_t *con; /* Current client connection */
335
336
337 for (con = (cupsd_client_t *)cupsArrayFirst(Clients);
338 con;
339 con = (cupsd_client_t *)cupsArrayNext(Clients))
340 if (con->request &&
341 con->request->request.op.operation_id == IPP_SEND_DOCUMENT)
342 break;
343
344 if (con)
345 continue;
346
91c84a35
MS
347 if (cupsdTimeoutJob(job))
348 continue;
349 }
09a101d6 350
b9faaae1
MS
351 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
352 "Job submission timed out.");
bd7854cb 353 }
ef416fc2 354
e07d4801
MS
355 /*
356 * Continue jobs that are waiting on the FilterLimit...
357 */
358
359 if (job->pending_cost > 0 &&
360 ((FilterLevel + job->pending_cost) < FilterLimit || FilterLevel == 0))
361 cupsdContinueJob(job);
362
ef416fc2 363 /*
364 * Start pending jobs if the destination is available...
365 */
366
88f9aafc
MS
367 if (job->state_value == IPP_JOB_PENDING && !NeedReload &&
368#ifndef kIOPMAssertionTypeDenySystemSleep
369 !Sleeping &&
370#endif /* !kIOPMAssertionTypeDenySystemSleep */
7cf5915e 371 !DoingShutdown && !job->printer)
ef416fc2 372 {
373 printer = cupsdFindDest(job->dest);
374 pclass = NULL;
375
a2326b5b 376 while (printer && (printer->type & CUPS_PRINTER_CLASS))
ef416fc2 377 {
378 /*
379 * If the class is remote, just pass it to the remote server...
380 */
381
382 pclass = printer;
383
c0e1af83 384 if (pclass->state == IPP_PRINTER_STOPPED)
b86bc4cf 385 printer = NULL;
c0e1af83 386 else if (pclass->type & CUPS_PRINTER_REMOTE)
387 break;
388 else
389 printer = cupsdFindAvailablePrinter(printer->name);
ef416fc2 390 }
391
392 if (!printer && !pclass)
393 {
394 /*
395 * Whoa, the printer and/or class for this destination went away;
396 * cancel the job...
397 */
398
b9faaae1
MS
399 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_PURGE,
400 "Job aborted because the destination printer/class "
401 "has gone away.");
ef416fc2 402 }
61cf44e2 403 else if (printer && !printer->holding_new_jobs)
ef416fc2 404 {
405 /*
406 * See if the printer is available or remote and not printing a job;
407 * if so, start the job...
408 */
409
410 if (pclass)
411 {
412 /*
413 * Add/update a job-actual-printer-uri attribute for this job
414 * so that we know which printer actually printed the job...
415 */
416
ef416fc2 417 if ((attr = ippFindAttribute(job->attrs, "job-actual-printer-uri",
418 IPP_TAG_URI)) != NULL)
419 cupsdSetString(&attr->values[0].string.text, printer->uri);
420 else
421 ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_URI,
422 "job-actual-printer-uri", NULL, printer->uri);
3dfe78b3
MS
423
424 job->dirty = 1;
425 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 426 }
427
a2326b5b 428 if (printer->state == IPP_PRINTER_IDLE)
bc44d920 429 {
bc44d920 430 /*
431 * Start the job...
432 */
433
e1d6a774 434 start_job(job, printer);
bc44d920 435 }
ef416fc2 436 }
437 }
438 }
439}
440
441
442/*
443 * 'cupsdCleanJobs()' - Clean out old jobs.
444 */
445
446void
447cupsdCleanJobs(void)
448{
449 cupsd_job_t *job; /* Current job */
450
451
ef55b745 452 if (MaxJobs <= 0 && JobHistory)
ef416fc2 453 return;
454
455 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
ef55b745 456 job && (cupsArrayCount(Jobs) >= MaxJobs || !JobHistory);
ef416fc2 457 job = (cupsd_job_t *)cupsArrayNext(Jobs))
b9faaae1
MS
458 if (job->state_value >= IPP_JOB_CANCELED && !job->printer)
459 cupsdDeleteJob(job, CUPSD_JOB_PURGE);
ef416fc2 460}
461
462
3dfe78b3 463/*
b9faaae1 464 * 'cupsdContinueJob()' - Continue printing with the next file in a job.
3dfe78b3
MS
465 */
466
467void
b9faaae1 468cupsdContinueJob(cupsd_job_t *job) /* I - Job */
3dfe78b3 469{
b9faaae1
MS
470 int i; /* Looping var */
471 int slot; /* Pipe slot */
e07d4801 472 cups_array_t *filters = NULL,/* Filters for job */
b9faaae1
MS
473 *prefilters; /* Filters with prefilters */
474 mime_filter_t *filter, /* Current filter */
475 *prefilter, /* Prefilter */
476 port_monitor; /* Port monitor filter */
477 char scheme[255]; /* Device URI scheme */
478 ipp_attribute_t *attr; /* Current attribute */
479 const char *ptr, /* Pointer into value */
480 *abort_message; /* Abort message */
f11a948a 481 ipp_jstate_t abort_state = IPP_JOB_STOPPED;
e07d4801 482 /* New job state on abort */
b9faaae1
MS
483 struct stat backinfo; /* Backend file information */
484 int backroot; /* Run backend as root? */
485 int pid; /* Process ID of new filter process */
486 int banner_page; /* 1 if banner page, 0 otherwise */
e07d4801
MS
487 int filterfds[2][2] = { { -1, -1 }, { -1, -1 } };
488 /* Pipes used between filters */
b9faaae1 489 int envc; /* Number of environment variables */
771bd8cb 490 struct stat fileinfo; /* Job file information */
e07d4801 491 char **argv = NULL, /* Filter command-line arguments */
b9faaae1
MS
492 filename[1024], /* Job filename */
493 command[1024], /* Full path to command */
494 jobid[255], /* Job ID string */
495 title[IPP_MAX_NAME],
496 /* Job title string */
497 copies[255], /* # copies string */
498 *options, /* Options string */
eac3a0a0 499 *envp[MAX_ENV + 21],
b9faaae1
MS
500 /* Environment variables */
501 charset[255], /* CHARSET env variable */
502 class_name[255],/* CLASS env variable */
503 classification[1024],
504 /* CLASSIFICATION env variable */
505 content_type[1024],
506 /* CONTENT_TYPE env variable */
507 device_uri[1024],
508 /* DEVICE_URI env variable */
509 final_content_type[1024],
510 /* FINAL_CONTENT_TYPE env variable */
511 lang[255], /* LANG env variable */
512#ifdef __APPLE__
513 apple_language[255],
514 /* APPLE_LANGUAGE env variable */
515#endif /* __APPLE__ */
22c9029b
MS
516 auth_info_required[255],
517 /* AUTH_INFO_REQUIRED env variable */
b9faaae1
MS
518 ppd[1024], /* PPD env variable */
519 printer_info[255],
520 /* PRINTER_INFO env variable */
521 printer_location[255],
522 /* PRINTER_LOCATION env variable */
523 printer_name[255],
524 /* PRINTER env variable */
eac3a0a0
MS
525 *printer_state_reasons = NULL,
526 /* PRINTER_STATE_REASONS env var */
b9faaae1
MS
527 rip_max_cache[255];
528 /* RIP_MAX_CACHE env variable */
529
530
531 cupsdLogMessage(CUPSD_LOG_DEBUG2,
532 "cupsdContinueJob(job=%p(%d)): current_file=%d, num_files=%d",
533 job, job->id, job->current_file, job->num_files);
3dfe78b3 534
3dfe78b3 535 /*
b9faaae1
MS
536 * Figure out what filters are required to convert from
537 * the source to the destination type...
3dfe78b3
MS
538 */
539
b9faaae1 540 FilterLevel -= job->cost;
3dfe78b3 541
e07d4801
MS
542 job->cost = 0;
543 job->pending_cost = 0;
544
545 memset(job->filters, 0, sizeof(job->filters));
546
3dfe78b3 547
b9faaae1 548 if (job->printer->raw)
3dfe78b3 549 {
b9faaae1
MS
550 /*
551 * Remote jobs and raw queues go directly to the printer without
552 * filtering...
553 */
3dfe78b3 554
b9faaae1 555 cupsdLogJob(job, CUPSD_LOG_DEBUG, "Sending job to queue tagged as raw...");
b9faaae1
MS
556 }
557 else
558 {
559 /*
560 * Local jobs get filtered...
561 */
3dfe78b3 562
771bd8cb
MS
563 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
564 job->id, job->current_file + 1);
565 if (stat(filename, &fileinfo))
566 fileinfo.st_size = 0;
567
568 filters = mimeFilter2(MimeDatabase, job->filetypes[job->current_file],
569 fileinfo.st_size, job->printer->filetype,
570 &(job->cost));
ef416fc2 571
b9faaae1
MS
572 if (!filters)
573 {
574 cupsdLogJob(job, CUPSD_LOG_ERROR,
12f89d24 575 "Unable to convert file %d to printable format.",
b9faaae1 576 job->current_file);
ef416fc2 577
e07d4801 578 abort_message = "Aborting job because it cannot be printed.";
f11a948a
MS
579 abort_state = IPP_JOB_ABORTED;
580
12f89d24 581 ippSetString(job->attrs, &job->reasons, 0, "document-unprintable-error");
e07d4801 582 goto abort_job;
b9faaae1 583 }
ef416fc2 584
ef416fc2 585 /*
b9faaae1 586 * Remove NULL ("-") filters...
ef416fc2 587 */
588
b9faaae1
MS
589 for (filter = (mime_filter_t *)cupsArrayFirst(filters);
590 filter;
591 filter = (mime_filter_t *)cupsArrayNext(filters))
592 if (!strcmp(filter->filter, "-"))
593 cupsArrayRemove(filters, filter);
ef416fc2 594
b9faaae1
MS
595 if (cupsArrayCount(filters) == 0)
596 {
597 cupsArrayDelete(filters);
598 filters = NULL;
599 }
ef416fc2 600
b9faaae1
MS
601 /*
602 * If this printer has any pre-filters, insert the required pre-filter
603 * in the filters array...
604 */
ef416fc2 605
b9faaae1
MS
606 if (job->printer->prefiltertype && filters)
607 {
608 prefilters = cupsArrayNew(NULL, NULL);
609
610 for (filter = (mime_filter_t *)cupsArrayFirst(filters);
611 filter;
612 filter = (mime_filter_t *)cupsArrayNext(filters))
613 {
614 if ((prefilter = mimeFilterLookup(MimeDatabase, filter->src,
615 job->printer->prefiltertype)))
616 {
617 cupsArrayAdd(prefilters, prefilter);
618 job->cost += prefilter->cost;
619 }
ef416fc2 620
b9faaae1
MS
621 cupsArrayAdd(prefilters, filter);
622 }
e00b005a 623
b9faaae1
MS
624 cupsArrayDelete(filters);
625 filters = prefilters;
626 }
627 }
bc44d920 628
c168a833 629 /*
b9faaae1
MS
630 * Set a minimum cost of 100 for all jobs so that FilterLimit
631 * works with raw queues and other low-cost paths.
c168a833
MS
632 */
633
b9faaae1
MS
634 if (job->cost < 100)
635 job->cost = 100;
c168a833 636
745129be 637 /*
b9faaae1 638 * See if the filter cost is too high...
745129be
MS
639 */
640
b9faaae1
MS
641 if ((FilterLevel + job->cost) > FilterLimit && FilterLevel > 0 &&
642 FilterLimit > 0)
ef416fc2 643 {
644 /*
b9faaae1 645 * Don't print this job quite yet...
ef416fc2 646 */
647
b9faaae1 648 cupsArrayDelete(filters);
a74454a7 649
b9faaae1
MS
650 cupsdLogJob(job, CUPSD_LOG_INFO,
651 "Holding because filter limit has been reached.");
652 cupsdLogJob(job, CUPSD_LOG_DEBUG2,
653 "cupsdContinueJob: file=%d, cost=%d, level=%d, limit=%d",
654 job->current_file, job->cost, FilterLevel,
655 FilterLimit);
e07d4801
MS
656
657 job->pending_cost = job->cost;
658 job->cost = 0;
b9faaae1
MS
659 return;
660 }
a74454a7 661
b9faaae1 662 FilterLevel += job->cost;
a74454a7 663
b9faaae1
MS
664 /*
665 * Add decompression/raw filter as needed...
666 */
a74454a7 667
b9faaae1
MS
668 if ((!job->printer->raw && job->compressions[job->current_file]) ||
669 (!filters && !job->printer->remote &&
670 (job->num_files > 1 || !strncmp(job->printer->device_uri, "file:", 5))))
671 {
a74454a7 672 /*
b9faaae1 673 * Add gziptoany filter to the front of the list...
a74454a7 674 */
675
b9faaae1
MS
676 if (!filters)
677 filters = cupsArrayNew(NULL, NULL);
ef416fc2 678
b9faaae1
MS
679 if (!cupsArrayInsert(filters, &gziptoany_filter))
680 {
681 cupsdLogJob(job, CUPSD_LOG_DEBUG,
682 "Unable to add decompression filter - %s", strerror(errno));
ed486911 683
b9faaae1 684 cupsArrayDelete(filters);
ed486911 685
e07d4801 686 abort_message = "Stopping job because the scheduler ran out of memory.";
ed486911 687
e07d4801 688 goto abort_job;
b9faaae1
MS
689 }
690 }
ef416fc2 691
b9faaae1
MS
692 /*
693 * Add port monitor, if any...
694 */
ef416fc2 695
b9faaae1
MS
696 if (job->printer->port_monitor)
697 {
698 /*
699 * Add port monitor to the end of the list...
700 */
ef416fc2 701
b9faaae1
MS
702 if (!filters)
703 filters = cupsArrayNew(NULL, NULL);
ef416fc2 704
b9faaae1
MS
705 port_monitor.src = NULL;
706 port_monitor.dst = NULL;
707 port_monitor.cost = 0;
ef416fc2 708
b9faaae1
MS
709 snprintf(port_monitor.filter, sizeof(port_monitor.filter),
710 "%s/monitor/%s", ServerBin, job->printer->port_monitor);
ef416fc2 711
b9faaae1
MS
712 if (!cupsArrayAdd(filters, &port_monitor))
713 {
714 cupsdLogJob(job, CUPSD_LOG_DEBUG,
715 "Unable to add port monitor - %s", strerror(errno));
ef416fc2 716
e07d4801 717 abort_message = "Stopping job because the scheduler ran out of memory.";
07725fee 718
e07d4801 719 goto abort_job;
b9faaae1
MS
720 }
721 }
ef416fc2 722
b9faaae1
MS
723 /*
724 * Make sure we don't go over the "MAX_FILTERS" limit...
725 */
ef416fc2 726
b9faaae1
MS
727 if (cupsArrayCount(filters) > MAX_FILTERS)
728 {
729 cupsdLogJob(job, CUPSD_LOG_DEBUG,
12f89d24 730 "Too many filters (%d > %d), unable to print.",
b9faaae1 731 cupsArrayCount(filters), MAX_FILTERS);
ef416fc2 732
e07d4801 733 abort_message = "Aborting job because it needs too many filters to print.";
f11a948a
MS
734 abort_state = IPP_JOB_ABORTED;
735
12f89d24
MS
736 ippSetString(job->attrs, &job->reasons, 0, "document-unprintable-error");
737
e07d4801 738 goto abort_job;
b9faaae1 739 }
07725fee 740
b9faaae1
MS
741 /*
742 * Determine if we are printing a banner page or not...
743 */
07725fee 744
b9faaae1
MS
745 if (job->job_sheets == NULL)
746 {
747 cupsdLogJob(job, CUPSD_LOG_DEBUG, "No job-sheets attribute.");
748 if ((job->job_sheets =
749 ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_ZERO)) != NULL)
750 cupsdLogJob(job, CUPSD_LOG_DEBUG,
12f89d24 751 "... but someone added one without setting job_sheets.");
b9faaae1
MS
752 }
753 else if (job->job_sheets->num_values == 1)
754 cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s",
755 job->job_sheets->values[0].string.text);
756 else
757 cupsdLogJob(job, CUPSD_LOG_DEBUG, "job-sheets=%s,%s",
758 job->job_sheets->values[0].string.text,
759 job->job_sheets->values[1].string.text);
07725fee 760
a2326b5b 761 if (job->printer->type & CUPS_PRINTER_REMOTE)
b9faaae1
MS
762 banner_page = 0;
763 else if (job->job_sheets == NULL)
764 banner_page = 0;
88f9aafc 765 else if (_cups_strcasecmp(job->job_sheets->values[0].string.text, "none") != 0 &&
b9faaae1
MS
766 job->current_file == 0)
767 banner_page = 1;
768 else if (job->job_sheets->num_values > 1 &&
88f9aafc 769 _cups_strcasecmp(job->job_sheets->values[1].string.text, "none") != 0 &&
b9faaae1
MS
770 job->current_file == (job->num_files - 1))
771 banner_page = 1;
772 else
773 banner_page = 0;
07725fee 774
b9faaae1
MS
775 if ((options = get_options(job, banner_page, copies, sizeof(copies), title,
776 sizeof(title))) == NULL)
777 {
e07d4801 778 abort_message = "Stopping job because the scheduler ran out of memory.";
ef416fc2 779
e07d4801 780 goto abort_job;
b9faaae1 781 }
ef416fc2 782
b9faaae1
MS
783 /*
784 * Build the command-line arguments for the filters. Each filter
785 * has 6 or 7 arguments:
786 *
787 * argv[0] = printer
788 * argv[1] = job ID
789 * argv[2] = username
790 * argv[3] = title
791 * argv[4] = # copies
792 * argv[5] = options
793 * argv[6] = filename (optional; normally stdin)
794 *
795 * This allows legacy printer drivers that use the old System V
796 * printing interface to be used by CUPS.
797 *
798 * For remote jobs, we send all of the files in the argument list.
799 */
07725fee 800
b9faaae1
MS
801 if (job->printer->remote)
802 argv = calloc(7 + job->num_files, sizeof(char *));
803 else
804 argv = calloc(8, sizeof(char *));
07725fee 805
b9faaae1
MS
806 if (!argv)
807 {
808 cupsdLogMessage(CUPSD_LOG_DEBUG, "Unable to allocate argument array - %s",
809 strerror(errno));
07725fee 810
e07d4801
MS
811 abort_message = "Stopping job because the scheduler ran out of memory.";
812
813 goto abort_job;
b9faaae1 814 }
ef416fc2 815
b9faaae1 816 sprintf(jobid, "%d", job->id);
ef416fc2 817
b9faaae1
MS
818 argv[0] = job->printer->name;
819 argv[1] = jobid;
820 argv[2] = job->username;
821 argv[3] = title;
822 argv[4] = copies;
823 argv[5] = options;
ef416fc2 824
b9faaae1 825 if (job->printer->remote && job->num_files > 1)
ef416fc2 826 {
b9faaae1
MS
827 for (i = 0; i < job->num_files; i ++)
828 {
829 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
830 job->id, i + 1);
831 argv[6 + i] = strdup(filename);
832 }
ef416fc2 833 }
834 else
835 {
b9faaae1
MS
836 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
837 job->id, job->current_file + 1);
838 argv[6] = filename;
ef416fc2 839 }
ef416fc2 840
b9faaae1
MS
841 for (i = 0; argv[i]; i ++)
842 cupsdLogJob(job, CUPSD_LOG_DEBUG, "argv[%d]=\"%s\"", i, argv[i]);
ef416fc2 843
b9faaae1
MS
844 /*
845 * Create environment variable strings for the filters...
846 */
bd7854cb 847
b9faaae1
MS
848 attr = ippFindAttribute(job->attrs, "attributes-natural-language",
849 IPP_TAG_LANGUAGE);
ef416fc2 850
b9faaae1
MS
851#ifdef __APPLE__
852 strcpy(apple_language, "APPLE_LANGUAGE=");
853 _cupsAppleLanguage(attr->values[0].string.text,
854 apple_language + 15, sizeof(apple_language) - 15);
855#endif /* __APPLE__ */
ef416fc2 856
b9faaae1 857 switch (strlen(attr->values[0].string.text))
ef416fc2 858 {
b9faaae1
MS
859 default :
860 /*
861 * This is an unknown or badly formatted language code; use
862 * the POSIX locale...
863 */
ef416fc2 864
b9faaae1
MS
865 strcpy(lang, "LANG=C");
866 break;
ef416fc2 867
b9faaae1
MS
868 case 2 :
869 /*
870 * Just the language code (ll)...
871 */
ef416fc2 872
1340db2d 873 snprintf(lang, sizeof(lang), "LANG=%s.UTF-8",
b9faaae1
MS
874 attr->values[0].string.text);
875 break;
ef416fc2 876
b9faaae1
MS
877 case 5 :
878 /*
879 * Language and country code (ll-cc)...
880 */
ef416fc2 881
1340db2d 882 snprintf(lang, sizeof(lang), "LANG=%c%c_%c%c.UTF-8",
b9faaae1
MS
883 attr->values[0].string.text[0],
884 attr->values[0].string.text[1],
885 toupper(attr->values[0].string.text[3] & 255),
886 toupper(attr->values[0].string.text[4] & 255));
887 break;
888 }
ef416fc2 889
b9faaae1
MS
890 if ((attr = ippFindAttribute(job->attrs, "document-format",
891 IPP_TAG_MIMETYPE)) != NULL &&
892 (ptr = strstr(attr->values[0].string.text, "charset=")) != NULL)
893 snprintf(charset, sizeof(charset), "CHARSET=%s", ptr + 8);
894 else
895 strlcpy(charset, "CHARSET=utf-8", sizeof(charset));
ef416fc2 896
b9faaae1
MS
897 snprintf(content_type, sizeof(content_type), "CONTENT_TYPE=%s/%s",
898 job->filetypes[job->current_file]->super,
899 job->filetypes[job->current_file]->type);
900 snprintf(device_uri, sizeof(device_uri), "DEVICE_URI=%s",
901 job->printer->device_uri);
902 snprintf(ppd, sizeof(ppd), "PPD=%s/ppd/%s.ppd", ServerRoot,
903 job->printer->name);
904 snprintf(printer_info, sizeof(printer_name), "PRINTER_INFO=%s",
905 job->printer->info ? job->printer->info : "");
906 snprintf(printer_location, sizeof(printer_name), "PRINTER_LOCATION=%s",
907 job->printer->location ? job->printer->location : "");
908 snprintf(printer_name, sizeof(printer_name), "PRINTER=%s", job->printer->name);
eac3a0a0
MS
909 if (job->printer->num_reasons > 0)
910 {
911 char *psrptr; /* Pointer into PRINTER_STATE_REASONS */
912 size_t psrlen; /* Size of PRINTER_STATE_REASONS */
913
914 for (psrlen = 22, i = 0; i < job->printer->num_reasons; i ++)
915 psrlen += strlen(job->printer->reasons[i]) + 1;
916
917 if ((printer_state_reasons = malloc(psrlen)) != NULL)
918 {
919 /*
920 * All of these strcpy's are safe because we allocated the psr string...
921 */
922
923 strcpy(printer_state_reasons, "PRINTER_STATE_REASONS=");
924 for (psrptr = printer_state_reasons + 22, i = 0;
925 i < job->printer->num_reasons;
926 i ++)
927 {
928 if (i)
929 *psrptr++ = ',';
930 strcpy(psrptr, job->printer->reasons[i]);
931 psrptr += strlen(psrptr);
932 }
933 }
934 }
b9faaae1 935 snprintf(rip_max_cache, sizeof(rip_max_cache), "RIP_MAX_CACHE=%s", RIPCache);
ef416fc2 936
22c9029b
MS
937 if (job->printer->num_auth_info_required == 1)
938 snprintf(auth_info_required, sizeof(auth_info_required),
939 "AUTH_INFO_REQUIRED=%s",
940 job->printer->auth_info_required[0]);
941 else if (job->printer->num_auth_info_required == 2)
942 snprintf(auth_info_required, sizeof(auth_info_required),
943 "AUTH_INFO_REQUIRED=%s,%s",
944 job->printer->auth_info_required[0],
945 job->printer->auth_info_required[1]);
946 else if (job->printer->num_auth_info_required == 3)
947 snprintf(auth_info_required, sizeof(auth_info_required),
948 "AUTH_INFO_REQUIRED=%s,%s,%s",
949 job->printer->auth_info_required[0],
950 job->printer->auth_info_required[1],
951 job->printer->auth_info_required[2]);
952 else if (job->printer->num_auth_info_required == 4)
953 snprintf(auth_info_required, sizeof(auth_info_required),
954 "AUTH_INFO_REQUIRED=%s,%s,%s,%s",
955 job->printer->auth_info_required[0],
956 job->printer->auth_info_required[1],
957 job->printer->auth_info_required[2],
958 job->printer->auth_info_required[3]);
959 else
960 strlcpy(auth_info_required, "AUTH_INFO_REQUIRED=none",
961 sizeof(auth_info_required));
962
b9faaae1 963 envc = cupsdLoadEnv(envp, (int)(sizeof(envp) / sizeof(envp[0])));
ef416fc2 964
b9faaae1
MS
965 envp[envc ++] = charset;
966 envp[envc ++] = lang;
967#ifdef __APPLE__
968 envp[envc ++] = apple_language;
969#endif /* __APPLE__ */
970 envp[envc ++] = ppd;
971 envp[envc ++] = rip_max_cache;
972 envp[envc ++] = content_type;
973 envp[envc ++] = device_uri;
974 envp[envc ++] = printer_info;
975 envp[envc ++] = printer_location;
976 envp[envc ++] = printer_name;
eac3a0a0
MS
977 envp[envc ++] = printer_state_reasons ? printer_state_reasons :
978 "PRINTER_STATE_REASONS=none";
b9faaae1
MS
979 envp[envc ++] = banner_page ? "CUPS_FILETYPE=job-sheet" :
980 "CUPS_FILETYPE=document";
ef416fc2 981
b9faaae1
MS
982 if (!job->printer->remote && !job->printer->raw)
983 {
984 filter = (mime_filter_t *)cupsArrayLast(filters);
ef416fc2 985
b9faaae1
MS
986 if (job->printer->port_monitor)
987 filter = (mime_filter_t *)cupsArrayPrev(filters);
ef416fc2 988
b9faaae1
MS
989 if (filter && filter->dst)
990 {
f14324a7 991 if ((ptr = strchr(filter->dst->type, '/')) != NULL)
c8fef167 992 snprintf(final_content_type, sizeof(final_content_type),
f14324a7 993 "FINAL_CONTENT_TYPE=%s", ptr + 1);
c8fef167
MS
994 else
995 snprintf(final_content_type, sizeof(final_content_type),
996 "FINAL_CONTENT_TYPE=%s/%s", filter->dst->super,
997 filter->dst->type);
b9faaae1
MS
998 envp[envc ++] = final_content_type;
999 }
1000 }
ef416fc2 1001
b9faaae1
MS
1002 if (Classification && !banner_page)
1003 {
1004 if ((attr = ippFindAttribute(job->attrs, "job-sheets",
1005 IPP_TAG_NAME)) == NULL)
1006 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1007 Classification);
1008 else if (attr->num_values > 1 &&
1009 strcmp(attr->values[1].string.text, "none") != 0)
1010 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1011 attr->values[1].string.text);
1012 else
1013 snprintf(classification, sizeof(classification), "CLASSIFICATION=%s",
1014 attr->values[0].string.text);
ef416fc2 1015
b9faaae1
MS
1016 envp[envc ++] = classification;
1017 }
ef416fc2 1018
a2326b5b 1019 if (job->dtype & CUPS_PRINTER_CLASS)
b9faaae1
MS
1020 {
1021 snprintf(class_name, sizeof(class_name), "CLASS=%s", job->dest);
1022 envp[envc ++] = class_name;
1023 }
ef416fc2 1024
22c9029b 1025 envp[envc ++] = auth_info_required;
88f9aafc
MS
1026
1027 for (i = 0;
1028 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
1029 i ++)
1030 if (job->auth_env[i])
1031 envp[envc ++] = job->auth_env[i];
1032 else
1033 break;
1034
07ed0e9a
MS
1035 if (job->auth_uid)
1036 envp[envc ++] = job->auth_uid;
ef416fc2 1037
b9faaae1 1038 envp[envc] = NULL;
ef416fc2 1039
b9faaae1
MS
1040 for (i = 0; i < envc; i ++)
1041 if (!strncmp(envp[i], "AUTH_", 5))
1042 cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"AUTH_%c****\"", i,
1043 envp[i][5]);
1044 else if (strncmp(envp[i], "DEVICE_URI=", 11))
1045 cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"%s\"", i, envp[i]);
1046 else
1047 cupsdLogJob(job, CUPSD_LOG_DEBUG, "envp[%d]=\"DEVICE_URI=%s\"", i,
1048 job->printer->sanitized_device_uri);
ef416fc2 1049
b9faaae1
MS
1050 if (job->printer->remote)
1051 job->current_file = job->num_files;
1052 else
1053 job->current_file ++;
ef416fc2 1054
b9faaae1
MS
1055 /*
1056 * Now create processes for all of the filters...
1057 */
ef416fc2 1058
82f97232
MS
1059 cupsdSetPrinterReasons(job->printer, "-cups-missing-filter-warning,"
1060 "cups-insecure-filter-warning");
1061
b9faaae1
MS
1062 for (i = 0, slot = 0, filter = (mime_filter_t *)cupsArrayFirst(filters);
1063 filter;
1064 i ++, filter = (mime_filter_t *)cupsArrayNext(filters))
1065 {
1066 if (filter->filter[0] != '/')
1067 snprintf(command, sizeof(command), "%s/filter/%s", ServerBin,
1068 filter->filter);
1069 else
1070 strlcpy(command, filter->filter, sizeof(command));
ef416fc2 1071
b9faaae1
MS
1072 if (i < (cupsArrayCount(filters) - 1))
1073 {
1074 if (cupsdOpenPipe(filterfds[slot]))
1075 {
1076 abort_message = "Stopping job because the scheduler could not create "
1077 "the filter pipes.";
ef416fc2 1078
b9faaae1
MS
1079 goto abort_job;
1080 }
1081 }
1082 else
1083 {
82f97232
MS
1084 if (job->current_file == 1 ||
1085 (job->printer->pc && job->printer->pc->single_file))
b9faaae1
MS
1086 {
1087 if (strncmp(job->printer->device_uri, "file:", 5) != 0)
1088 {
1089 if (cupsdOpenPipe(job->print_pipes))
1090 {
1091 abort_message = "Stopping job because the scheduler could not "
1092 "create the backend pipes.";
ef416fc2 1093
b9faaae1
MS
1094 goto abort_job;
1095 }
1096 }
1097 else
1098 {
1099 job->print_pipes[0] = -1;
1100 if (!strcmp(job->printer->device_uri, "file:/dev/null") ||
1101 !strcmp(job->printer->device_uri, "file:///dev/null"))
1102 job->print_pipes[1] = -1;
1103 else
1104 {
1105 if (!strncmp(job->printer->device_uri, "file:/dev/", 10))
1106 job->print_pipes[1] = open(job->printer->device_uri + 5,
1107 O_WRONLY | O_EXCL);
1108 else if (!strncmp(job->printer->device_uri, "file:///dev/", 12))
1109 job->print_pipes[1] = open(job->printer->device_uri + 7,
1110 O_WRONLY | O_EXCL);
1111 else if (!strncmp(job->printer->device_uri, "file:///", 8))
1112 job->print_pipes[1] = open(job->printer->device_uri + 7,
1113 O_WRONLY | O_CREAT | O_TRUNC, 0600);
1114 else
1115 job->print_pipes[1] = open(job->printer->device_uri + 5,
1116 O_WRONLY | O_CREAT | O_TRUNC, 0600);
ef416fc2 1117
b9faaae1
MS
1118 if (job->print_pipes[1] < 0)
1119 {
1120 abort_message = "Stopping job because the scheduler could not "
1121 "open the output file.";
bd7854cb 1122
b9faaae1
MS
1123 goto abort_job;
1124 }
ef416fc2 1125
b9faaae1
MS
1126 fcntl(job->print_pipes[1], F_SETFD,
1127 fcntl(job->print_pipes[1], F_GETFD) | FD_CLOEXEC);
1128 }
1129 }
1130 }
ef416fc2 1131
b9faaae1
MS
1132 filterfds[slot][0] = job->print_pipes[0];
1133 filterfds[slot][1] = job->print_pipes[1];
1134 }
ef416fc2 1135
b9faaae1
MS
1136 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
1137 filterfds[slot][1], job->status_pipes[1],
1138 job->back_pipes[0], job->side_pipes[0], 0,
38e73f87 1139 job->profile, job, job->filters + i);
ef416fc2 1140
b9faaae1 1141 cupsdClosePipe(filterfds[!slot]);
ef416fc2 1142
b9faaae1
MS
1143 if (pid == 0)
1144 {
1145 cupsdLogJob(job, CUPSD_LOG_ERROR, "Unable to start filter \"%s\" - %s.",
1146 filter->filter, strerror(errno));
3dfe78b3 1147
f11a948a 1148 abort_message = "Stopping job because the scheduler could not execute a "
b9faaae1 1149 "filter.";
ef416fc2 1150
b9faaae1
MS
1151 goto abort_job;
1152 }
ef416fc2 1153
b9faaae1
MS
1154 cupsdLogJob(job, CUPSD_LOG_INFO, "Started filter %s (PID %d)", command,
1155 pid);
bd7854cb 1156
b9faaae1
MS
1157 argv[6] = NULL;
1158 slot = !slot;
bd7854cb 1159 }
1160
b9faaae1
MS
1161 cupsArrayDelete(filters);
1162 filters = NULL;
ef416fc2 1163
1164 /*
b9faaae1 1165 * Finally, pipe the final output into a backend process if needed...
ef416fc2 1166 */
1167
b9faaae1 1168 if (strncmp(job->printer->device_uri, "file:", 5) != 0)
bd7854cb 1169 {
82f97232
MS
1170 if (job->current_file == 1 || job->printer->remote ||
1171 (job->printer->pc && job->printer->pc->single_file))
b9faaae1
MS
1172 {
1173 sscanf(job->printer->device_uri, "%254[^:]", scheme);
1174 snprintf(command, sizeof(command), "%s/backend/%s", ServerBin, scheme);
ef416fc2 1175
b9faaae1
MS
1176 /*
1177 * See if the backend needs to run as root...
1178 */
ef416fc2 1179
b9faaae1
MS
1180 if (RunUser)
1181 backroot = 0;
1182 else if (stat(command, &backinfo))
1183 backroot = 0;
1184 else
1185 backroot = !(backinfo.st_mode & (S_IRWXG | S_IRWXO));
ef416fc2 1186
b9faaae1 1187 argv[0] = job->printer->sanitized_device_uri;
ef416fc2 1188
b9faaae1
MS
1189 filterfds[slot][0] = -1;
1190 filterfds[slot][1] = -1;
ef416fc2 1191
b9faaae1
MS
1192 pid = cupsdStartProcess(command, argv, envp, filterfds[!slot][0],
1193 filterfds[slot][1], job->status_pipes[1],
1194 job->back_pipes[1], job->side_pipes[1],
38e73f87 1195 backroot, job->profile, job, &(job->backend));
ef416fc2 1196
b9faaae1
MS
1197 if (pid == 0)
1198 {
1199 abort_message = "Stopping job because the sheduler could not execute "
1200 "the backend.";
1201
1202 goto abort_job;
1203 }
1204 else
1205 {
1206 cupsdLogJob(job, CUPSD_LOG_INFO, "Started backend %s (PID %d)",
1207 command, pid);
1208 }
1209 }
ef416fc2 1210
82f97232
MS
1211 if (job->current_file == job->num_files ||
1212 (job->printer->pc && job->printer->pc->single_file))
1213 cupsdClosePipe(job->print_pipes);
1214
b9faaae1
MS
1215 if (job->current_file == job->num_files)
1216 {
b9faaae1
MS
1217 cupsdClosePipe(job->back_pipes);
1218 cupsdClosePipe(job->side_pipes);
ef416fc2 1219
b9faaae1
MS
1220 close(job->status_pipes[1]);
1221 job->status_pipes[1] = -1;
1222 }
1223 }
1224 else
bd7854cb 1225 {
b9faaae1
MS
1226 filterfds[slot][0] = -1;
1227 filterfds[slot][1] = -1;
ef416fc2 1228
82f97232
MS
1229 if (job->current_file == job->num_files ||
1230 (job->printer->pc && job->printer->pc->single_file))
b9faaae1
MS
1231 cupsdClosePipe(job->print_pipes);
1232
82f97232
MS
1233 if (job->current_file == job->num_files)
1234 {
b9faaae1
MS
1235 close(job->status_pipes[1]);
1236 job->status_pipes[1] = -1;
1237 }
bd7854cb 1238 }
ef416fc2 1239
b9faaae1
MS
1240 cupsdClosePipe(filterfds[slot]);
1241
1242 if (job->printer->remote && job->num_files > 1)
bd7854cb 1243 {
b9faaae1
MS
1244 for (i = 0; i < job->num_files; i ++)
1245 free(argv[i + 6]);
bd7854cb 1246 }
ef416fc2 1247
b9faaae1 1248 free(argv);
eac3a0a0
MS
1249 if (printer_state_reasons)
1250 free(printer_state_reasons);
ef416fc2 1251
b9faaae1
MS
1252 cupsdAddSelect(job->status_buffer->fd, (cupsd_selfunc_t)update_job, NULL,
1253 job);
ef416fc2 1254
b9faaae1
MS
1255 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "Job #%d started.",
1256 job->id);
ef416fc2 1257
b9faaae1 1258 return;
ef416fc2 1259
ef416fc2 1260
bd7854cb 1261 /*
b9faaae1
MS
1262 * If we get here, we need to abort the current job and close out all
1263 * files and pipes...
bd7854cb 1264 */
ef416fc2 1265
b9faaae1 1266 abort_job:
8b450588 1267
e07d4801
MS
1268 FilterLevel -= job->cost;
1269 job->cost = 0;
1270
b9faaae1
MS
1271 for (slot = 0; slot < 2; slot ++)
1272 cupsdClosePipe(filterfds[slot]);
1273
e07d4801
MS
1274 cupsArrayDelete(filters);
1275
1276 if (argv)
1277 {
1278 if (job->printer->remote && job->num_files > 1)
1279 {
1280 for (i = 0; i < job->num_files; i ++)
1281 free(argv[i + 6]);
1282 }
1283
1284 free(argv);
1285 }
1286
eac3a0a0
MS
1287 if (printer_state_reasons)
1288 free(printer_state_reasons);
1289
e07d4801
MS
1290 cupsdClosePipe(job->print_pipes);
1291 cupsdClosePipe(job->back_pipes);
1292 cupsdClosePipe(job->side_pipes);
1293
1294 cupsdRemoveSelect(job->status_pipes[0]);
b9faaae1
MS
1295 cupsdClosePipe(job->status_pipes);
1296 cupsdStatBufDelete(job->status_buffer);
1297 job->status_buffer = NULL;
1298
e07d4801
MS
1299 /*
1300 * Update the printer and job state.
1301 */
b9faaae1 1302
e07d4801
MS
1303 cupsdSetJobState(job, abort_state, CUPSD_JOB_DEFAULT, "%s", abort_message);
1304 cupsdSetPrinterState(job->printer, IPP_PRINTER_IDLE, 0);
1305 update_job_attrs(job, 0);
ef416fc2 1306
178cb736
MS
1307 if (job->history)
1308 free_job_history(job);
1309
e07d4801 1310 cupsArrayRemove(PrintingJobs, job);
ef416fc2 1311
e07d4801
MS
1312 /*
1313 * Clear the printer <-> job association...
1314 */
1315
1316 job->printer->job = NULL;
1317 job->printer = NULL;
b9faaae1 1318}
bd7854cb 1319
ef416fc2 1320
b9faaae1
MS
1321/*
1322 * 'cupsdDeleteJob()' - Free all memory used by a job.
1323 */
ef416fc2 1324
b9faaae1
MS
1325void
1326cupsdDeleteJob(cupsd_job_t *job, /* I - Job */
1327 cupsd_jobaction_t action)/* I - Action */
1328{
88f9aafc 1329 int i; /* Looping var */
22c9029b
MS
1330 char filename[1024]; /* Job filename */
1331
1332
b9faaae1 1333 if (job->printer)
ef55b745 1334 finalize_job(job, 1);
ef416fc2 1335
b9faaae1 1336 if (action == CUPSD_JOB_PURGE)
bd7854cb 1337 {
1338 /*
b9faaae1 1339 * Remove the job info file...
bd7854cb 1340 */
ef416fc2 1341
b9faaae1
MS
1342 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot,
1343 job->id);
321d8d57
MS
1344 if (Classification)
1345 cupsdRemoveFile(filename);
1346 else
1347 unlink(filename);
bd7854cb 1348 }
ef416fc2 1349
b9faaae1
MS
1350 cupsdClearString(&job->username);
1351 cupsdClearString(&job->dest);
88f9aafc
MS
1352 for (i = 0;
1353 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
1354 i ++)
1355 cupsdClearString(job->auth_env + i);
07ed0e9a 1356 cupsdClearString(&job->auth_uid);
09a101d6 1357
b9faaae1
MS
1358 if (job->num_files > 0)
1359 {
1360 free(job->compressions);
1361 free(job->filetypes);
09a101d6 1362
771bd8cb 1363 if (action == CUPSD_JOB_PURGE)
22c9029b 1364 {
771bd8cb
MS
1365 while (job->num_files > 0)
1366 {
1367 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
1368 job->id, job->num_files);
1369 if (Classification)
1370 cupsdRemoveFile(filename);
1371 else
1372 unlink(filename);
22c9029b 1373
771bd8cb
MS
1374 job->num_files --;
1375 }
22c9029b 1376 }
771bd8cb
MS
1377 else
1378 job->num_files = 0;
09a101d6 1379 }
ed6e7faf 1380
178cb736
MS
1381 if (job->history)
1382 free_job_history(job);
1383
b9faaae1 1384 unload_job(job);
dfd5680b 1385
b9faaae1
MS
1386 cupsArrayRemove(Jobs, job);
1387 cupsArrayRemove(ActiveJobs, job);
1388 cupsArrayRemove(PrintingJobs, job);
dfd5680b 1389
b9faaae1 1390 free(job);
ef416fc2 1391}
1392
1393
1394/*
b9faaae1 1395 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
ef416fc2 1396 */
1397
1398void
b9faaae1 1399cupsdFreeAllJobs(void)
ef416fc2 1400{
b9faaae1 1401 cupsd_job_t *job; /* Current job */
ef416fc2 1402
ef416fc2 1403
b9faaae1 1404 if (!Jobs)
ef416fc2 1405 return;
1406
b9faaae1 1407 cupsdHoldSignals();
ef416fc2 1408
238c3832 1409 cupsdStopAllJobs(CUPSD_JOB_FORCE, 0);
b9faaae1 1410 cupsdSaveAllJobs();
e53920b9 1411
b9faaae1
MS
1412 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
1413 job;
1414 job = (cupsd_job_t *)cupsArrayNext(Jobs))
1415 cupsdDeleteJob(job, CUPSD_JOB_DEFAULT);
ef416fc2 1416
b9faaae1
MS
1417 cupsdReleaseSignals();
1418}
ef416fc2 1419
bd7854cb 1420
b9faaae1
MS
1421/*
1422 * 'cupsdFindJob()' - Find the specified job.
1423 */
e53920b9 1424
b9faaae1
MS
1425cupsd_job_t * /* O - Job data */
1426cupsdFindJob(int id) /* I - Job ID */
1427{
1428 cupsd_job_t key; /* Search key */
ef416fc2 1429
ef416fc2 1430
b9faaae1 1431 key.id = id;
e53920b9 1432
b9faaae1 1433 return ((cupsd_job_t *)cupsArrayFind(Jobs, &key));
ef416fc2 1434}
1435
1436
1437/*
b9faaae1
MS
1438 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
1439 * or held jobs in a printer or class.
ef416fc2 1440 */
1441
b9faaae1
MS
1442int /* O - Job count */
1443cupsdGetPrinterJobCount(
1444 const char *dest) /* I - Printer or class name */
ef416fc2 1445{
b9faaae1
MS
1446 int count; /* Job count */
1447 cupsd_job_t *job; /* Current job */
005dd1eb 1448
005dd1eb 1449
b9faaae1
MS
1450 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
1451 job;
1452 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
88f9aafc 1453 if (job->dest && !_cups_strcasecmp(job->dest, dest))
b9faaae1 1454 count ++;
ef416fc2 1455
b9faaae1 1456 return (count);
ef416fc2 1457}
1458
1459
1460/*
b9faaae1
MS
1461 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
1462 * or held jobs for a user.
ef416fc2 1463 */
1464
b9faaae1
MS
1465int /* O - Job count */
1466cupsdGetUserJobCount(
1467 const char *username) /* I - Username */
ef416fc2 1468{
b9faaae1
MS
1469 int count; /* Job count */
1470 cupsd_job_t *job; /* Current job */
07725fee 1471
07725fee 1472
b9faaae1
MS
1473 for (job = (cupsd_job_t *)cupsArrayFirst(ActiveJobs), count = 0;
1474 job;
1475 job = (cupsd_job_t *)cupsArrayNext(ActiveJobs))
88f9aafc 1476 if (!_cups_strcasecmp(job->username, username))
b9faaae1 1477 count ++;
07725fee 1478
b9faaae1 1479 return (count);
ef416fc2 1480}
1481
1482
1483/*
b9faaae1 1484 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
ef416fc2 1485 */
1486
1487void
b9faaae1 1488cupsdLoadAllJobs(void)
bd7854cb 1489{
b9faaae1
MS
1490 char filename[1024]; /* Full filename of job.cache file */
1491 struct stat fileinfo, /* Information on job.cache file */
1492 dirinfo; /* Information on RequestRoot dir */
bd7854cb 1493
bd7854cb 1494
bd7854cb 1495
1496 /*
b9faaae1 1497 * Create the job arrays as needed...
bd7854cb 1498 */
1499
b9faaae1
MS
1500 if (!Jobs)
1501 Jobs = cupsArrayNew(compare_jobs, NULL);
1502
1503 if (!ActiveJobs)
1504 ActiveJobs = cupsArrayNew(compare_active_jobs, NULL);
1505
1506 if (!PrintingJobs)
1507 PrintingJobs = cupsArrayNew(compare_jobs, NULL);
bd7854cb 1508
1509 /*
b9faaae1 1510 * See whether the job.cache file is older than the RequestRoot directory...
bd7854cb 1511 */
1512
b9faaae1 1513 snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir);
bd7854cb 1514
b9faaae1
MS
1515 if (stat(filename, &fileinfo))
1516 {
1517 fileinfo.st_mtime = 0;
1518
1519 if (errno != ENOENT)
1520 cupsdLogMessage(CUPSD_LOG_ERROR,
1521 "Unable to get file information for \"%s\" - %s",
1522 filename, strerror(errno));
1523 }
1524
1525 if (stat(RequestRoot, &dirinfo))
1526 {
1527 dirinfo.st_mtime = 0;
1528
1529 if (errno != ENOENT)
1530 cupsdLogMessage(CUPSD_LOG_ERROR,
1531 "Unable to get directory information for \"%s\" - %s",
1532 RequestRoot, strerror(errno));
1533 }
bd7854cb 1534
1535 /*
b9faaae1 1536 * Load the most recent source for job data...
bd7854cb 1537 */
1538
b9faaae1 1539 if (dirinfo.st_mtime > fileinfo.st_mtime)
bd7854cb 1540 {
b9faaae1
MS
1541 load_request_root();
1542
1543 load_next_job_id(filename);
bd7854cb 1544 }
b9faaae1
MS
1545 else
1546 load_job_cache(filename);
bd7854cb 1547
b9faaae1
MS
1548 /*
1549 * Clean out old jobs as needed...
1550 */
1551
1552 if (MaxJobs > 0 && cupsArrayCount(Jobs) >= MaxJobs)
1553 cupsdCleanJobs();
bd7854cb 1554}
1555
1556
1557/*
b9faaae1 1558 * 'cupsdLoadJob()' - Load a single job.
bd7854cb 1559 */
1560
b9faaae1
MS
1561int /* O - 1 on success, 0 on failure */
1562cupsdLoadJob(cupsd_job_t *job) /* I - Job */
ef416fc2 1563{
88f9aafc 1564 int i; /* Looping var */
b9faaae1
MS
1565 char jobfile[1024]; /* Job filename */
1566 cups_file_t *fp; /* Job file */
1567 int fileid; /* Current file ID */
1568 ipp_attribute_t *attr; /* Job attribute */
1569 const char *dest; /* Destination name */
1570 cupsd_printer_t *destptr; /* Pointer to destination */
1571 mime_type_t **filetypes; /* New filetypes array */
1572 int *compressions; /* New compressions array */
ef416fc2 1573
1574
b9faaae1
MS
1575 if (job->attrs)
1576 {
1577 if (job->state_value > IPP_JOB_STOPPED)
1578 job->access_time = time(NULL);
bd7854cb 1579
b9faaae1
MS
1580 return (1);
1581 }
ef416fc2 1582
b9faaae1 1583 if ((job->attrs = ippNew()) == NULL)
ef416fc2 1584 {
12f89d24 1585 cupsdLogJob(job, CUPSD_LOG_ERROR, "Ran out of memory for job attributes.");
b9faaae1 1586 return (0);
ef416fc2 1587 }
1588
b9faaae1
MS
1589 /*
1590 * Load job attributes...
1591 */
ef416fc2 1592
b9faaae1 1593 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading attributes...", job->id);
bd7854cb 1594
b9faaae1
MS
1595 snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, job->id);
1596 if ((fp = cupsFileOpen(jobfile, "r")) == NULL)
1597 {
321d8d57
MS
1598 char newfile[1024]; /* New job filename */
1599
1600 snprintf(newfile, sizeof(newfile), "%s/c%05d.N", RequestRoot, job->id);
1601 if ((fp = cupsFileOpen(newfile, "r")) == NULL)
1602 {
1603 cupsdLogMessage(CUPSD_LOG_ERROR,
1604 "[Job %d] Unable to open job control file \"%s\": %s",
1605 job->id, jobfile, strerror(errno));
1606 goto error;
1607 }
1608
1609 unlink(jobfile);
1610 rename(newfile, jobfile);
b9faaae1 1611 }
ef416fc2 1612
b9faaae1
MS
1613 if (ippReadIO(fp, (ipp_iocb_t)cupsFileRead, 1, NULL, job->attrs) != IPP_DATA)
1614 {
1615 cupsdLogMessage(CUPSD_LOG_ERROR,
321d8d57 1616 "[Job %d] Unable to read job control file \"%s\".", job->id,
b9faaae1
MS
1617 jobfile);
1618 cupsFileClose(fp);
1619 goto error;
1620 }
ef416fc2 1621
b9faaae1 1622 cupsFileClose(fp);
ef416fc2 1623
b9faaae1
MS
1624 /*
1625 * Copy attribute data to the job object...
1626 */
ef416fc2 1627
b9faaae1 1628 if (!ippFindAttribute(job->attrs, "time-at-creation", IPP_TAG_INTEGER))
ef416fc2 1629 {
b9faaae1
MS
1630 cupsdLogMessage(CUPSD_LOG_ERROR,
1631 "[Job %d] Missing or bad time-at-creation attribute in "
12f89d24 1632 "control file.", job->id);
b9faaae1 1633 goto error;
ef416fc2 1634 }
b9faaae1
MS
1635
1636 if ((job->state = ippFindAttribute(job->attrs, "job-state",
1637 IPP_TAG_ENUM)) == NULL)
ef416fc2 1638 {
b9faaae1
MS
1639 cupsdLogMessage(CUPSD_LOG_ERROR,
1640 "[Job %d] Missing or bad job-state attribute in control "
12f89d24 1641 "file.", job->id);
b9faaae1
MS
1642 goto error;
1643 }
ef416fc2 1644
b9faaae1 1645 job->state_value = (ipp_jstate_t)job->state->values[0].integer;
ef416fc2 1646
b9faaae1 1647 if (!job->dest)
ef416fc2 1648 {
b9faaae1
MS
1649 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
1650 IPP_TAG_URI)) == NULL)
1651 {
1652 cupsdLogMessage(CUPSD_LOG_ERROR,
12f89d24 1653 "[Job %d] No job-printer-uri attribute in control file.",
b9faaae1
MS
1654 job->id);
1655 goto error;
1656 }
ef416fc2 1657
b9faaae1
MS
1658 if ((dest = cupsdValidateDest(attr->values[0].string.text, &(job->dtype),
1659 &destptr)) == NULL)
1660 {
1661 cupsdLogMessage(CUPSD_LOG_ERROR,
12f89d24 1662 "[Job %d] Unable to queue job for destination \"%s\".",
b9faaae1
MS
1663 job->id, attr->values[0].string.text);
1664 goto error;
1665 }
ef416fc2 1666
b9faaae1 1667 cupsdSetString(&job->dest, dest);
f7deaa1a 1668 }
b9faaae1 1669 else if ((destptr = cupsdFindDest(job->dest)) == NULL)
ef416fc2 1670 {
b9faaae1 1671 cupsdLogMessage(CUPSD_LOG_ERROR,
12f89d24 1672 "[Job %d] Unable to queue job for destination \"%s\".",
b9faaae1
MS
1673 job->id, job->dest);
1674 goto error;
1675 }
ef416fc2 1676
12f89d24
MS
1677 if ((job->reasons = ippFindAttribute(job->attrs, "job-state-reasons",
1678 IPP_TAG_KEYWORD)) == NULL)
1679 {
1680 const char *reason; /* job-state-reason keyword */
1681
1682 cupsdLogMessage(CUPSD_LOG_DEBUG,
1683 "[Job %d] Adding missing job-state-reasons attribute to "
1684 " control file.", job->id);
1685
1686 switch (job->state_value)
1687 {
1688 case IPP_JOB_PENDING :
1689 if (destptr->state == IPP_PRINTER_STOPPED)
1690 reason = "printer-stopped";
1691 else
1692 reason = "none";
1693 break;
1694
1695 case IPP_JOB_HELD :
1696 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1697 IPP_TAG_ZERO)) != NULL &&
1698 (attr->value_tag == IPP_TAG_NAME ||
1699 attr->value_tag == IPP_TAG_NAMELANG ||
1700 attr->value_tag == IPP_TAG_KEYWORD) &&
1701 strcmp(attr->values[0].string.text, "no-hold"))
1702 reason = "job-hold-until-specified";
1703 else
1704 reason = "job-incoming";
1705 break;
1706
1707 case IPP_JOB_PROCESSING :
1708 reason = "job-printing";
1709 break;
1710
1711 case IPP_JOB_STOPPED :
1712 reason = "job-stopped";
1713 break;
1714
1715 case IPP_JOB_CANCELED :
1716 reason = "job-canceled-by-user";
1717 break;
1718
1719 case IPP_JOB_ABORTED :
1720 reason = "aborted-by-system";
1721 break;
1722
1723 case IPP_JOB_COMPLETED :
1724 reason = "job-completed-successfully";
1725 break;
1726 }
1727
1728 job->reasons = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
1729 "job-state-reasons", NULL, reason);
1730 }
1731 else if (job->state_value == IPP_JOB_PENDING)
1732 {
1733 if (destptr->state == IPP_PRINTER_STOPPED)
1734 ippSetString(job->attrs, &job->reasons, 0, "printer-stopped");
1735 else
1736 ippSetString(job->attrs, &job->reasons, 0, "none");
1737 }
1738
b9faaae1
MS
1739 job->sheets = ippFindAttribute(job->attrs, "job-media-sheets-completed",
1740 IPP_TAG_INTEGER);
1741 job->job_sheets = ippFindAttribute(job->attrs, "job-sheets", IPP_TAG_NAME);
ef416fc2 1742
b9faaae1
MS
1743 if (!job->priority)
1744 {
1745 if ((attr = ippFindAttribute(job->attrs, "job-priority",
1746 IPP_TAG_INTEGER)) == NULL)
1747 {
1748 cupsdLogMessage(CUPSD_LOG_ERROR,
1749 "[Job %d] Missing or bad job-priority attribute in "
12f89d24 1750 "control file.", job->id);
b9faaae1
MS
1751 goto error;
1752 }
1753
1754 job->priority = attr->values[0].integer;
f7deaa1a 1755 }
b9faaae1
MS
1756
1757 if (!job->username)
ef416fc2 1758 {
b9faaae1
MS
1759 if ((attr = ippFindAttribute(job->attrs, "job-originating-user-name",
1760 IPP_TAG_NAME)) == NULL)
1761 {
1762 cupsdLogMessage(CUPSD_LOG_ERROR,
1763 "[Job %d] Missing or bad job-originating-user-name "
12f89d24 1764 "attribute in control file.", job->id);
b9faaae1
MS
1765 goto error;
1766 }
ef416fc2 1767
b9faaae1
MS
1768 cupsdSetString(&job->username, attr->values[0].string.text);
1769 }
ef416fc2 1770
b9faaae1
MS
1771 /*
1772 * Set the job hold-until time and state...
1773 */
1774
1775 if (job->state_value == IPP_JOB_HELD)
1776 {
1777 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
1778 IPP_TAG_KEYWORD)) == NULL)
1779 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
1780
1781 if (attr)
1782 cupsdSetJobHoldUntil(job, attr->values[0].string.text, CUPSD_JOB_DEFAULT);
ef416fc2 1783 else
b9faaae1
MS
1784 {
1785 job->state->values[0].integer = IPP_JOB_PENDING;
1786 job->state_value = IPP_JOB_PENDING;
1787 }
f7deaa1a 1788 }
b9faaae1
MS
1789 else if (job->state_value == IPP_JOB_PROCESSING)
1790 {
1791 job->state->values[0].integer = IPP_JOB_PENDING;
1792 job->state_value = IPP_JOB_PENDING;
1793 }
1794
1795 if (!job->num_files)
ef416fc2 1796 {
1797 /*
b9faaae1 1798 * Find all the d##### files...
ef416fc2 1799 */
1800
b9faaae1
MS
1801 for (fileid = 1; fileid < 10000; fileid ++)
1802 {
1803 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
1804 job->id, fileid);
ef416fc2 1805
b9faaae1
MS
1806 if (access(jobfile, 0))
1807 break;
1808
1809 cupsdLogMessage(CUPSD_LOG_DEBUG,
1810 "[Job %d] Auto-typing document file \"%s\"...", job->id,
1811 jobfile);
1812
1813 if (fileid > job->num_files)
1814 {
1815 if (job->num_files == 0)
1816 {
1817 compressions = (int *)calloc(fileid, sizeof(int));
1818 filetypes = (mime_type_t **)calloc(fileid, sizeof(mime_type_t *));
1819 }
1820 else
1821 {
1822 compressions = (int *)realloc(job->compressions,
1823 sizeof(int) * fileid);
1824 filetypes = (mime_type_t **)realloc(job->filetypes,
1825 sizeof(mime_type_t *) *
1826 fileid);
1827 }
1828
1829 if (!compressions || !filetypes)
1830 {
1831 cupsdLogMessage(CUPSD_LOG_ERROR,
12f89d24 1832 "[Job %d] Ran out of memory for job file types.",
b9faaae1 1833 job->id);
ef55b745
MS
1834
1835 ippDelete(job->attrs);
1836 job->attrs = NULL;
1837
1838 if (compressions)
1839 free(compressions);
1840
1841 if (filetypes)
1842 free(filetypes);
1843
1844 if (job->compressions)
1845 {
1846 free(job->compressions);
1847 job->compressions = NULL;
1848 }
1849
1850 if (job->filetypes)
1851 {
1852 free(job->filetypes);
1853 job->filetypes = NULL;
1854 }
1855
1856 job->num_files = 0;
1857 return (0);
b9faaae1
MS
1858 }
1859
1860 job->compressions = compressions;
1861 job->filetypes = filetypes;
1862 job->num_files = fileid;
1863 }
1864
1865 job->filetypes[fileid - 1] = mimeFileType(MimeDatabase, jobfile, NULL,
1866 job->compressions + fileid - 1);
1867
1868 if (!job->filetypes[fileid - 1])
1869 job->filetypes[fileid - 1] = mimeType(MimeDatabase, "application",
1870 "vnd.cups-raw");
1871 }
ef416fc2 1872 }
b9faaae1
MS
1873
1874 /*
1875 * Load authentication information as needed...
1876 */
1877
1878 if (job->state_value < IPP_JOB_STOPPED)
ef416fc2 1879 {
b9faaae1 1880 snprintf(jobfile, sizeof(jobfile), "%s/a%05d", RequestRoot, job->id);
ef416fc2 1881
88f9aafc
MS
1882 for (i = 0;
1883 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
1884 i ++)
1885 cupsdClearString(job->auth_env + i);
07ed0e9a 1886 cupsdClearString(&job->auth_uid);
ef416fc2 1887
b9faaae1
MS
1888 if ((fp = cupsFileOpen(jobfile, "r")) != NULL)
1889 {
dcb445bc
MS
1890 int bytes, /* Size of auth data */
1891 linenum = 1; /* Current line number */
85dda01c 1892 char line[65536], /* Line from file */
dcb445bc 1893 *value, /* Value from line */
85dda01c 1894 data[65536]; /* Decoded data */
ef416fc2 1895
ef416fc2 1896
dcb445bc
MS
1897 if (cupsFileGets(fp, line, sizeof(line)) &&
1898 !strcmp(line, "CUPSD-AUTH-V2"))
b9faaae1 1899 {
dcb445bc
MS
1900 i = 0;
1901 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1902 {
1903 /*
1904 * Decode value...
1905 */
1906
1907 bytes = sizeof(data);
1908 httpDecode64_2(data, &bytes, value);
b9faaae1 1909
dcb445bc
MS
1910 /*
1911 * Assign environment variables...
1912 */
1913
1914 if (!strcmp(line, "uid"))
1915 {
1916 cupsdSetStringf(&job->auth_uid, "AUTH_UID=%s", value);
1917 continue;
1918 }
1919 else if (i >= (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0])))
1920 break;
1921
1922 if (!strcmp(line, "username"))
1923 cupsdSetStringf(job->auth_env + i, "AUTH_USERNAME=%s", data);
1924 else if (!strcmp(line, "domain"))
1925 cupsdSetStringf(job->auth_env + i, "AUTH_DOMAIN=%s", data);
1926 else if (!strcmp(line, "password"))
1927 cupsdSetStringf(job->auth_env + i, "AUTH_PASSWORD=%s", data);
1928 else if (!strcmp(line, "negotiate"))
1929 cupsdSetStringf(job->auth_env + i, "AUTH_NEGOTIATE=%s", line);
1930 else
1931 continue;
1932
1933 i ++;
1934 }
1935 }
07ed0e9a 1936
b9faaae1
MS
1937 cupsFileClose(fp);
1938 }
ef416fc2 1939 }
1940
b9faaae1
MS
1941 job->access_time = time(NULL);
1942 return (1);
1943
1944 /*
1945 * If we get here then something bad happened...
1946 */
1947
1948 error:
1949
1950 ippDelete(job->attrs);
1951 job->attrs = NULL;
ef55b745
MS
1952
1953 if (job->compressions)
1954 {
1955 free(job->compressions);
1956 job->compressions = NULL;
1957 }
1958
1959 if (job->filetypes)
1960 {
1961 free(job->filetypes);
1962 job->filetypes = NULL;
1963 }
1964
1965 job->num_files = 0;
1966
321d8d57
MS
1967 if (Classification)
1968 cupsdRemoveFile(jobfile);
1969 else
1970 unlink(jobfile);
b9faaae1
MS
1971
1972 return (0);
ef416fc2 1973}
1974
1975
1976/*
b9faaae1 1977 * 'cupsdMoveJob()' - Move the specified job to a different destination.
ef416fc2 1978 */
1979
1980void
b9faaae1
MS
1981cupsdMoveJob(cupsd_job_t *job, /* I - Job */
1982 cupsd_printer_t *p) /* I - Destination printer or class */
ef416fc2 1983{
b9faaae1
MS
1984 ipp_attribute_t *attr; /* job-printer-uri attribute */
1985 const char *olddest; /* Old destination */
1986 cupsd_printer_t *oldp; /* Old pointer */
ef416fc2 1987
1988
1989 /*
b9faaae1 1990 * Don't move completed jobs...
ef416fc2 1991 */
1992
b9faaae1 1993 if (job->state_value > IPP_JOB_STOPPED)
ef416fc2 1994 return;
1995
1996 /*
b9faaae1 1997 * Get the old destination...
ef416fc2 1998 */
1999
b9faaae1 2000 olddest = job->dest;
ef416fc2 2001
b9faaae1
MS
2002 if (job->printer)
2003 oldp = job->printer;
ef416fc2 2004 else
b9faaae1 2005 oldp = cupsdFindDest(olddest);
ef416fc2 2006
b9faaae1
MS
2007 /*
2008 * Change the destination information...
2009 */
2010
eac3a0a0
MS
2011 if (job->state_value > IPP_JOB_HELD)
2012 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
2013 "Stopping job prior to move.");
b9faaae1
MS
2014
2015 cupsdAddEvent(CUPSD_EVENT_JOB_CONFIG_CHANGED, oldp, job,
2016 "Job #%d moved from %s to %s.", job->id, olddest,
2017 p->name);
2018
2019 cupsdSetString(&job->dest, p->name);
a2326b5b 2020 job->dtype = p->type & (CUPS_PRINTER_CLASS | CUPS_PRINTER_REMOTE);
b9faaae1
MS
2021
2022 if ((attr = ippFindAttribute(job->attrs, "job-printer-uri",
2023 IPP_TAG_URI)) != NULL)
2024 cupsdSetString(&(attr->values[0].string.text), p->uri);
2025
2026 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED, p, job,
2027 "Job #%d moved from %s to %s.", job->id, olddest,
2028 p->name);
ef416fc2 2029
3dfe78b3
MS
2030 job->dirty = 1;
2031 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
ef416fc2 2032}
2033
2034
2035/*
b9faaae1 2036 * 'cupsdReleaseJob()' - Release the specified job.
ef416fc2 2037 */
2038
2039void
b9faaae1 2040cupsdReleaseJob(cupsd_job_t *job) /* I - Job */
ef416fc2 2041{
b9faaae1
MS
2042 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdReleaseJob(job=%p(%d))", job,
2043 job->id);
ef416fc2 2044
b9faaae1
MS
2045 if (job->state_value == IPP_JOB_HELD)
2046 {
2047 /*
2048 * Add trailing banner as needed...
2049 */
ef416fc2 2050
b9faaae1
MS
2051 if (job->pending_timeout)
2052 cupsdTimeoutJob(job);
ef416fc2 2053
b9faaae1
MS
2054 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
2055 "Job released by user.");
2056 }
e1d6a774 2057}
ef416fc2 2058
ef416fc2 2059
e1d6a774 2060/*
b9faaae1 2061 * 'cupsdRestartJob()' - Restart the specified job.
e1d6a774 2062 */
ef416fc2 2063
e1d6a774 2064void
b9faaae1 2065cupsdRestartJob(cupsd_job_t *job) /* I - Job */
e1d6a774 2066{
b9faaae1
MS
2067 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdRestartJob(job=%p(%d))", job,
2068 job->id);
ef416fc2 2069
b9faaae1
MS
2070 if (job->state_value == IPP_JOB_STOPPED || job->num_files)
2071 cupsdSetJobState(job, IPP_JOB_PENDING, CUPSD_JOB_DEFAULT,
2072 "Job restarted by user.");
2073}
ef416fc2 2074
ef416fc2 2075
b9faaae1
MS
2076/*
2077 * 'cupsdSaveAllJobs()' - Save a summary of all jobs to disk.
2078 */
ef416fc2 2079
b9faaae1
MS
2080void
2081cupsdSaveAllJobs(void)
2082{
2083 int i; /* Looping var */
321d8d57
MS
2084 cups_file_t *fp; /* job.cache file */
2085 char filename[1024], /* job.cache filename */
2086 temp[1024]; /* Temporary string */
b9faaae1
MS
2087 cupsd_job_t *job; /* Current job */
2088 time_t curtime; /* Current time */
2089 struct tm *curdate; /* Current date */
ef416fc2 2090
ef416fc2 2091
321d8d57
MS
2092 snprintf(filename, sizeof(filename), "%s/job.cache", CacheDir);
2093 if ((fp = cupsdCreateConfFile(filename, ConfigFilePerm)) == NULL)
c168a833 2094 return;
ef416fc2 2095
321d8d57 2096 cupsdLogMessage(CUPSD_LOG_INFO, "Saving job.cache...");
bd7854cb 2097
b9faaae1
MS
2098 /*
2099 * Write a small header to the file...
2100 */
bd7854cb 2101
b9faaae1
MS
2102 curtime = time(NULL);
2103 curdate = localtime(&curtime);
2104 strftime(temp, sizeof(temp) - 1, "%Y-%m-%d %H:%M", curdate);
ef416fc2 2105
b9faaae1
MS
2106 cupsFilePuts(fp, "# Job cache file for " CUPS_SVERSION "\n");
2107 cupsFilePrintf(fp, "# Written by cupsd on %s\n", temp);
2108 cupsFilePrintf(fp, "NextJobId %d\n", NextJobId);
f7deaa1a 2109
b9faaae1
MS
2110 /*
2111 * Write each job known to the system...
2112 */
f7deaa1a 2113
b9faaae1
MS
2114 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
2115 job;
2116 job = (cupsd_job_t *)cupsArrayNext(Jobs))
ef416fc2 2117 {
b9faaae1
MS
2118 cupsFilePrintf(fp, "<Job %d>\n", job->id);
2119 cupsFilePrintf(fp, "State %d\n", job->state_value);
2120 cupsFilePrintf(fp, "Priority %d\n", job->priority);
8b116e60 2121 cupsFilePrintf(fp, "HoldUntil %d\n", (int)job->hold_until);
b9faaae1
MS
2122 cupsFilePrintf(fp, "Username %s\n", job->username);
2123 cupsFilePrintf(fp, "Destination %s\n", job->dest);
2124 cupsFilePrintf(fp, "DestType %d\n", job->dtype);
2125 cupsFilePrintf(fp, "NumFiles %d\n", job->num_files);
2126 for (i = 0; i < job->num_files; i ++)
2127 cupsFilePrintf(fp, "File %d %s/%s %d\n", i + 1, job->filetypes[i]->super,
2128 job->filetypes[i]->type, job->compressions[i]);
2129 cupsFilePuts(fp, "</Job>\n");
ef416fc2 2130 }
b9faaae1 2131
321d8d57 2132 cupsdCloseCreatedConfFile(fp, filename);
e1d6a774 2133}
ef416fc2 2134
ef416fc2 2135
e1d6a774 2136/*
b9faaae1 2137 * 'cupsdSaveJob()' - Save a job to disk.
e1d6a774 2138 */
ef416fc2 2139
e1d6a774 2140void
b9faaae1 2141cupsdSaveJob(cupsd_job_t *job) /* I - Job */
e1d6a774 2142{
321d8d57
MS
2143 char filename[1024], /* Job control filename */
2144 newfile[1024]; /* New job control filename */
b9faaae1 2145 cups_file_t *fp; /* Job file */
3dfe78b3 2146
ef416fc2 2147
b9faaae1
MS
2148 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSaveJob(job=%p(%d)): job->attrs=%p",
2149 job, job->id, job->attrs);
ef416fc2 2150
b9faaae1 2151 snprintf(filename, sizeof(filename), "%s/c%05d", RequestRoot, job->id);
321d8d57 2152 snprintf(newfile, sizeof(newfile), "%s/c%05d.N", RequestRoot, job->id);
ef416fc2 2153
321d8d57 2154 if ((fp = cupsFileOpen(newfile, "w")) == NULL)
b9faaae1
MS
2155 {
2156 cupsdLogMessage(CUPSD_LOG_ERROR,
321d8d57
MS
2157 "[Job %d] Unable to create job control file \"%s\": %s",
2158 job->id, newfile, strerror(errno));
b9faaae1
MS
2159 return;
2160 }
ef416fc2 2161
b9faaae1
MS
2162 fchmod(cupsFileNumber(fp), 0600);
2163 fchown(cupsFileNumber(fp), RunUser, Group);
ef416fc2 2164
b9faaae1 2165 job->attrs->state = IPP_IDLE;
ef416fc2 2166
b9faaae1
MS
2167 if (ippWriteIO(fp, (ipp_iocb_t)cupsFileWrite, 1, NULL,
2168 job->attrs) != IPP_DATA)
321d8d57 2169 {
b9faaae1 2170 cupsdLogMessage(CUPSD_LOG_ERROR,
321d8d57
MS
2171 "[Job %d] Unable to write job control file.", job->id);
2172 cupsFileClose(fp);
2173 unlink(newfile);
2174 return;
2175 }
ef416fc2 2176
321d8d57
MS
2177 if (cupsFileClose(fp))
2178 cupsdLogMessage(CUPSD_LOG_ERROR,
2179 "[Job %d] Unable to close job control file: %s",
2180 job->id, strerror(errno));
2181 else
2182 {
2183 unlink(filename);
2184 if (rename(newfile, filename))
2185 cupsdLogMessage(CUPSD_LOG_ERROR,
2186 "[Job %d] Unable to finalize job control file: %s",
2187 job->id, strerror(errno));
2188 else
2189 job->dirty = 0;
2190 }
e1d6a774 2191}
ef416fc2 2192
ef416fc2 2193
e1d6a774 2194/*
b9faaae1 2195 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job.
e1d6a774 2196 */
ef416fc2 2197
b9faaae1
MS
2198void
2199cupsdSetJobHoldUntil(cupsd_job_t *job, /* I - Job */
2200 const char *when, /* I - When to resume */
2201 int update)/* I - Update job-hold-until attr? */
e1d6a774 2202{
b9faaae1
MS
2203 time_t curtime; /* Current time */
2204 struct tm *curdate; /* Current date */
2205 int hour; /* Hold hour */
2206 int minute; /* Hold minute */
2207 int second = 0; /* Hold second */
ef416fc2 2208
ef416fc2 2209
b9faaae1
MS
2210 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2211 "cupsdSetJobHoldUntil(job=%p(%d), when=\"%s\", update=%d)",
2212 job, job->id, when, update);
ef416fc2 2213
b9faaae1 2214 if (update)
ef416fc2 2215 {
e1d6a774 2216 /*
b9faaae1 2217 * Update the job-hold-until attribute...
e1d6a774 2218 */
ef416fc2 2219
b9faaae1 2220 ipp_attribute_t *attr; /* job-hold-until attribute */
ef416fc2 2221
b9faaae1
MS
2222 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
2223 IPP_TAG_KEYWORD)) == NULL)
2224 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
ef416fc2 2225
b9faaae1
MS
2226 if (attr)
2227 cupsdSetString(&(attr->values[0].string.text), when);
2228 else
2229 attr = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_KEYWORD,
2230 "job-hold-until", NULL, when);
ef416fc2 2231
b9faaae1
MS
2232 if (attr)
2233 {
2234 if (isdigit(when[0] & 255))
2235 attr->value_tag = IPP_TAG_NAME;
2236 else
2237 attr->value_tag = IPP_TAG_KEYWORD;
2238
2239 job->dirty = 1;
2240 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2241 }
2242 }
ef416fc2 2243
b9faaae1
MS
2244 /*
2245 * Update the hold time...
2246 */
2247
2248 if (!strcmp(when, "indefinite") || !strcmp(when, "auth-info-required"))
2249 {
e1d6a774 2250 /*
b9faaae1 2251 * Hold indefinitely...
e1d6a774 2252 */
ef416fc2 2253
b9faaae1
MS
2254 job->hold_until = 0;
2255 }
2256 else if (!strcmp(when, "day-time"))
2257 {
e1d6a774 2258 /*
b9faaae1 2259 * Hold to 6am the next morning unless local time is < 6pm.
e1d6a774 2260 */
ef416fc2 2261
b9faaae1
MS
2262 curtime = time(NULL);
2263 curdate = localtime(&curtime);
ef416fc2 2264
b9faaae1
MS
2265 if (curdate->tm_hour < 18)
2266 job->hold_until = curtime;
2267 else
2268 job->hold_until = curtime +
2269 ((29 - curdate->tm_hour) * 60 + 59 -
2270 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2271 }
2272 else if (!strcmp(when, "evening") || !strcmp(when, "night"))
2273 {
2274 /*
2275 * Hold to 6pm unless local time is > 6pm or < 6am.
2276 */
ef416fc2 2277
b9faaae1
MS
2278 curtime = time(NULL);
2279 curdate = localtime(&curtime);
ef416fc2 2280
b9faaae1
MS
2281 if (curdate->tm_hour < 6 || curdate->tm_hour >= 18)
2282 job->hold_until = curtime;
2283 else
2284 job->hold_until = curtime +
2285 ((17 - curdate->tm_hour) * 60 + 59 -
2286 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2287 }
2288 else if (!strcmp(when, "second-shift"))
2289 {
2290 /*
2291 * Hold to 4pm unless local time is > 4pm.
2292 */
ef416fc2 2293
b9faaae1
MS
2294 curtime = time(NULL);
2295 curdate = localtime(&curtime);
ef416fc2 2296
b9faaae1
MS
2297 if (curdate->tm_hour >= 16)
2298 job->hold_until = curtime;
2299 else
2300 job->hold_until = curtime +
2301 ((15 - curdate->tm_hour) * 60 + 59 -
2302 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2303 }
2304 else if (!strcmp(when, "third-shift"))
2305 {
2306 /*
2307 * Hold to 12am unless local time is < 8am.
2308 */
ef416fc2 2309
b9faaae1
MS
2310 curtime = time(NULL);
2311 curdate = localtime(&curtime);
ef416fc2 2312
b9faaae1
MS
2313 if (curdate->tm_hour < 8)
2314 job->hold_until = curtime;
2315 else
2316 job->hold_until = curtime +
2317 ((23 - curdate->tm_hour) * 60 + 59 -
2318 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2319 }
2320 else if (!strcmp(when, "weekend"))
2321 {
2322 /*
2323 * Hold to weekend unless we are in the weekend.
2324 */
ef416fc2 2325
b9faaae1
MS
2326 curtime = time(NULL);
2327 curdate = localtime(&curtime);
ef416fc2 2328
b9faaae1
MS
2329 if (curdate->tm_wday == 0 || curdate->tm_wday == 6)
2330 job->hold_until = curtime;
2331 else
2332 job->hold_until = curtime +
2333 (((5 - curdate->tm_wday) * 24 +
2334 (17 - curdate->tm_hour)) * 60 + 59 -
2335 curdate->tm_min) * 60 + 60 - curdate->tm_sec;
2336 }
2337 else if (sscanf(when, "%d:%d:%d", &hour, &minute, &second) >= 2)
2338 {
2339 /*
2340 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
2341 */
bd7854cb 2342
b9faaae1
MS
2343 curtime = time(NULL);
2344 curdate = gmtime(&curtime);
2345
2346 job->hold_until = curtime +
2347 ((hour - curdate->tm_hour) * 60 + minute -
2348 curdate->tm_min) * 60 + second - curdate->tm_sec;
2349
2350 /*
2351 * Hold until next day as needed...
2352 */
2353
2354 if (job->hold_until < curtime)
2355 job->hold_until += 24 * 60 * 60;
ef416fc2 2356 }
2357
b9faaae1
MS
2358 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdSetJobHoldUntil: hold_until=%d",
2359 (int)job->hold_until);
e1d6a774 2360}
ef416fc2 2361
ef416fc2 2362
e1d6a774 2363/*
b9faaae1
MS
2364 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
2365 * the list as needed.
e1d6a774 2366 */
ef416fc2 2367
b9faaae1
MS
2368void
2369cupsdSetJobPriority(
2370 cupsd_job_t *job, /* I - Job ID */
2371 int priority) /* I - New priority (0 to 100) */
e1d6a774 2372{
b9faaae1 2373 ipp_attribute_t *attr; /* Job attribute */
ef416fc2 2374
ef416fc2 2375
e1d6a774 2376 /*
b9faaae1 2377 * Don't change completed jobs...
e1d6a774 2378 */
bd7854cb 2379
b9faaae1 2380 if (job->state_value >= IPP_JOB_PROCESSING)
ef416fc2 2381 return;
ef416fc2 2382
e1d6a774 2383 /*
b9faaae1 2384 * Set the new priority and re-add the job into the active list...
e1d6a774 2385 */
ef416fc2 2386
b9faaae1 2387 cupsArrayRemove(ActiveJobs, job);
ef416fc2 2388
b9faaae1 2389 job->priority = priority;
ef416fc2 2390
b9faaae1
MS
2391 if ((attr = ippFindAttribute(job->attrs, "job-priority",
2392 IPP_TAG_INTEGER)) != NULL)
2393 attr->values[0].integer = priority;
2394 else
2395 ippAddInteger(job->attrs, IPP_TAG_JOB, IPP_TAG_INTEGER, "job-priority",
2396 priority);
ef416fc2 2397
b9faaae1 2398 cupsArrayAdd(ActiveJobs, job);
bd7854cb 2399
b9faaae1
MS
2400 job->dirty = 1;
2401 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2402}
bd7854cb 2403
ef416fc2 2404
b9faaae1
MS
2405/*
2406 * 'cupsdSetJobState()' - Set the state of the specified print job.
2407 */
e1d6a774 2408
b9faaae1
MS
2409void
2410cupsdSetJobState(
2411 cupsd_job_t *job, /* I - Job to cancel */
2412 ipp_jstate_t newstate, /* I - New job state */
2413 cupsd_jobaction_t action, /* I - Action to take */
2414 const char *message, /* I - Message to log */
2415 ...) /* I - Additional arguments as needed */
2416{
2417 int i; /* Looping var */
2418 ipp_jstate_t oldstate; /* Old state */
2419 char filename[1024]; /* Job filename */
2420 ipp_attribute_t *attr; /* Job attribute */
ef416fc2 2421
ef416fc2 2422
b9faaae1
MS
2423 cupsdLogMessage(CUPSD_LOG_DEBUG2,
2424 "cupsdSetJobState(job=%p(%d), state=%d, newstate=%d, "
2425 "action=%d, message=\"%s\")", job, job->id, job->state_value,
2426 newstate, action, message ? message : "(null)");
ef416fc2 2427
bd7854cb 2428
b9faaae1
MS
2429 /*
2430 * Make sure we have the job attributes...
2431 */
e1d6a774 2432
b9faaae1
MS
2433 if (!cupsdLoadJob(job))
2434 return;
bd7854cb 2435
f99f3698
MS
2436 /*
2437 * Don't do anything if the state is unchanged and we aren't purging the
2438 * job...
2439 */
2440
2441 oldstate = job->state_value;
2442 if (newstate == oldstate && action != CUPSD_JOB_PURGE)
2443 return;
e1d6a774 2444
b9faaae1
MS
2445 /*
2446 * Stop any processes that are working on the current job...
2447 */
e1d6a774 2448
b9faaae1 2449 if (oldstate == IPP_JOB_PROCESSING)
ef55b745 2450 stop_job(job, action);
b9faaae1
MS
2451
2452 /*
2453 * Set the new job state...
2454 */
2455
2456 job->state->values[0].integer = newstate;
2457 job->state_value = newstate;
2458
2459 switch (newstate)
2460 {
2461 case IPP_JOB_PENDING :
2462 /*
2463 * Update job-hold-until as needed...
2464 */
2465
2466 if ((attr = ippFindAttribute(job->attrs, "job-hold-until",
2467 IPP_TAG_KEYWORD)) == NULL)
2468 attr = ippFindAttribute(job->attrs, "job-hold-until", IPP_TAG_NAME);
2469
2470 if (attr)
ef416fc2 2471 {
b9faaae1
MS
2472 attr->value_tag = IPP_TAG_KEYWORD;
2473 cupsdSetString(&(attr->values[0].string.text), "no-hold");
e1d6a774 2474 }
ef416fc2 2475
b9faaae1
MS
2476 default :
2477 break;
ef416fc2 2478
b9faaae1
MS
2479 case IPP_JOB_ABORTED :
2480 case IPP_JOB_CANCELED :
2481 case IPP_JOB_COMPLETED :
2482 set_time(job, "time-at-completed");
12f89d24 2483 ippSetString(job->attrs, &job->reasons, 0, "processing-to-stop-point");
b9faaae1
MS
2484 break;
2485 }
ef416fc2 2486
b9faaae1
MS
2487 /*
2488 * Log message as needed...
2489 */
ef416fc2 2490
b9faaae1
MS
2491 if (message)
2492 {
2493 char buffer[2048]; /* Message buffer */
2494 va_list ap; /* Pointer to additional arguments */
ef416fc2 2495
b9faaae1
MS
2496 va_start(ap, message);
2497 vsnprintf(buffer, sizeof(buffer), message, ap);
2498 va_end(ap);
bd7854cb 2499
b9faaae1
MS
2500 if (newstate > IPP_JOB_STOPPED)
2501 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED, job->printer, job, "%s", buffer);
2502 else
2503 cupsdAddEvent(CUPSD_EVENT_JOB_STATE, job->printer, job, "%s", buffer);
2504
2505 if (newstate == IPP_JOB_STOPPED || newstate == IPP_JOB_ABORTED)
2506 cupsdLogJob(job, CUPSD_LOG_ERROR, "%s", buffer);
2507 else
2508 cupsdLogJob(job, CUPSD_LOG_INFO, "%s", buffer);
2509 }
2510
2511 /*
2512 * Handle post-state-change actions...
2513 */
2514
2515 switch (newstate)
2516 {
2517 case IPP_JOB_PROCESSING :
e1d6a774 2518 /*
b9faaae1 2519 * Add the job to the "printing" list...
e1d6a774 2520 */
bd7854cb 2521
b9faaae1
MS
2522 if (!cupsArrayFind(PrintingJobs, job))
2523 cupsArrayAdd(PrintingJobs, job);
ef416fc2 2524
b9faaae1
MS
2525 /*
2526 * Set the processing time...
2527 */
ef416fc2 2528
b9faaae1
MS
2529 set_time(job, "time-at-processing");
2530
2531 case IPP_JOB_PENDING :
2532 case IPP_JOB_HELD :
2533 case IPP_JOB_STOPPED :
e1d6a774 2534 /*
b9faaae1 2535 * Make sure the job is in the active list...
e1d6a774 2536 */
ef416fc2 2537
b9faaae1
MS
2538 if (!cupsArrayFind(ActiveJobs, job))
2539 cupsArrayAdd(ActiveJobs, job);
ef416fc2 2540
b9faaae1
MS
2541 /*
2542 * Save the job state to disk...
2543 */
ef416fc2 2544
b9faaae1
MS
2545 job->dirty = 1;
2546 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2547 break;
ef416fc2 2548
b9faaae1
MS
2549 case IPP_JOB_ABORTED :
2550 case IPP_JOB_CANCELED :
2551 case IPP_JOB_COMPLETED :
7a0cbd5e
MS
2552 if (newstate == IPP_JOB_CANCELED)
2553 {
2554 /*
2555 * Remove the job from the active list if there are no processes still
2556 * running for it...
2557 */
ef416fc2 2558
7a0cbd5e
MS
2559 for (i = 0; job->filters[i] < 0; i++);
2560
2561 if (!job->filters[i] && job->backend <= 0)
2562 cupsArrayRemove(ActiveJobs, job);
2563 }
2564 else
2565 {
2566 /*
2567 * Otherwise just remove the job from the active list immediately...
2568 */
2569
2570 cupsArrayRemove(ActiveJobs, job);
2571 }
ef416fc2 2572
b9faaae1 2573 /*
7a0cbd5e 2574 * Expire job subscriptions since the job is now "completed"...
b9faaae1 2575 */
ef416fc2 2576
7a0cbd5e 2577 cupsdExpireSubscriptions(NULL, job);
ef416fc2 2578
b9faaae1
MS
2579#ifdef __APPLE__
2580 /*
2581 * If we are going to sleep and the PrintingJobs count is now 0, allow the
2582 * sleep to happen immediately...
2583 */
ef416fc2 2584
b9faaae1
MS
2585 if (Sleeping && cupsArrayCount(PrintingJobs) == 0)
2586 cupsdAllowSleep();
2587#endif /* __APPLE__ */
ef416fc2 2588
b9faaae1
MS
2589 /*
2590 * Remove any authentication data...
2591 */
bd7854cb 2592
b9faaae1
MS
2593 snprintf(filename, sizeof(filename), "%s/a%05d", RequestRoot, job->id);
2594 if (cupsdRemoveFile(filename) && errno != ENOENT)
2595 cupsdLogMessage(CUPSD_LOG_ERROR,
2596 "Unable to remove authentication cache: %s",
2597 strerror(errno));
bd7854cb 2598
88f9aafc
MS
2599 for (i = 0;
2600 i < (int)(sizeof(job->auth_env) / sizeof(job->auth_env[0]));
2601 i ++)
2602 cupsdClearString(job->auth_env + i);
2603
07ed0e9a 2604 cupsdClearString(&job->auth_uid);
ef416fc2 2605
b9faaae1
MS
2606 /*
2607 * Remove the print file for good if we aren't preserving jobs or
2608 * files...
2609 */
e1d6a774 2610
b9faaae1
MS
2611 if (!JobHistory || !JobFiles || action == CUPSD_JOB_PURGE)
2612 {
2613 for (i = 1; i <= job->num_files; i ++)
2614 {
2615 snprintf(filename, sizeof(filename), "%s/d%05d-%03d", RequestRoot,
2616 job->id, i);
321d8d57
MS
2617 if (Classification)
2618 cupsdRemoveFile(filename);
2619 else
2620 unlink(filename);
b9faaae1 2621 }
e1d6a774 2622
b9faaae1
MS
2623 if (job->num_files > 0)
2624 {
2625 free(job->filetypes);
2626 free(job->compressions);
ef416fc2 2627
b9faaae1
MS
2628 job->num_files = 0;
2629 job->filetypes = NULL;
2630 job->compressions = NULL;
2631 }
2632 }
ef416fc2 2633
b9faaae1
MS
2634 if (JobHistory && action != CUPSD_JOB_PURGE)
2635 {
2636 /*
2637 * Save job state info...
2638 */
e1d6a774 2639
b9faaae1
MS
2640 job->dirty = 1;
2641 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
2642 }
ba55dc12
MS
2643 else if (!job->printer)
2644 {
2645 /*
2646 * Delete the job immediately if not actively printing...
2647 */
2648
2649 cupsdDeleteJob(job, CUPSD_JOB_PURGE);
2650 job = NULL;
2651 }
b9faaae1 2652 break;
e1d6a774 2653 }
2654
e07d4801
MS
2655 /*
2656 * Finalize the job immediately if we forced things...
2657 */
2658
ba55dc12 2659 if (action >= CUPSD_JOB_FORCE && job && job->printer)
ef55b745 2660 finalize_job(job, 0);
e07d4801 2661
e1d6a774 2662 /*
b9faaae1 2663 * Update the server "busy" state...
e1d6a774 2664 */
ef416fc2 2665
b9faaae1
MS
2666 cupsdSetBusyState();
2667}
e00b005a 2668
ef416fc2 2669
b9faaae1
MS
2670/*
2671 * 'cupsdStopAllJobs()' - Stop all print jobs.
2672 */
ef416fc2 2673
b9faaae1
MS
2674void
2675cupsdStopAllJobs(
238c3832
MS
2676 cupsd_jobaction_t action, /* I - Action */
2677 int kill_delay) /* I - Number of seconds before we kill */
b9faaae1
MS
2678{
2679 cupsd_job_t *job; /* Current job */
ef416fc2 2680
ef416fc2 2681
b9faaae1 2682 DEBUG_puts("cupsdStopAllJobs()");
ef416fc2 2683
b9faaae1
MS
2684 for (job = (cupsd_job_t *)cupsArrayFirst(PrintingJobs);
2685 job;
2686 job = (cupsd_job_t *)cupsArrayNext(PrintingJobs))
238c3832
MS
2687 {
2688 if (kill_delay)
2689 job->kill_time = time(NULL) + kill_delay;
2690
b9faaae1 2691 cupsdSetJobState(job, IPP_JOB_PENDING, action, NULL);
238c3832 2692 }
b9faaae1 2693}
bd7854cb 2694
bd7854cb 2695
b9faaae1
MS
2696/*
2697 * 'cupsdUnloadCompletedJobs()' - Flush completed job history from memory.
2698 */
ef416fc2 2699
b9faaae1
MS
2700void
2701cupsdUnloadCompletedJobs(void)
2702{
2703 cupsd_job_t *job; /* Current job */
2704 time_t expire; /* Expiration time */
ef416fc2 2705
b9faaae1
MS
2706
2707 expire = time(NULL) - 60;
2708
2709 for (job = (cupsd_job_t *)cupsArrayFirst(Jobs);
2710 job;
2711 job = (cupsd_job_t *)cupsArrayNext(Jobs))
2712 if (job->attrs && job->state_value >= IPP_JOB_STOPPED && !job->printer &&
2713 job->access_time < expire)
2714 {
2715 if (job->dirty)
2716 cupsdSaveJob(job);
2717
2718 unload_job(job);
2719 }
e1d6a774 2720}
ef416fc2 2721
ef416fc2 2722
e1d6a774 2723/*
b9faaae1 2724 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
e1d6a774 2725 */
ef416fc2 2726
b9faaae1
MS
2727static int /* O - Difference */
2728compare_active_jobs(void *first, /* I - First job */
2729 void *second, /* I - Second job */
2730 void *data) /* I - App data (not used) */
e1d6a774 2731{
b9faaae1 2732 int diff; /* Difference */
ef416fc2 2733
ef416fc2 2734
321d8d57
MS
2735 (void)data;
2736
b9faaae1
MS
2737 if ((diff = ((cupsd_job_t *)second)->priority -
2738 ((cupsd_job_t *)first)->priority) != 0)
2739 return (diff);
2740 else
2741 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
e1d6a774 2742}
bd7854cb 2743
ef416fc2 2744
e1d6a774 2745/*
b9faaae1 2746 * 'compare_jobs()' - Compare the job IDs of two jobs.
e1d6a774 2747 */
ef416fc2 2748
b9faaae1
MS
2749static int /* O - Difference */
2750compare_jobs(void *first, /* I - First job */
2751 void *second, /* I - Second job */
2752 void *data) /* I - App data (not used) */
e1d6a774 2753{
321d8d57
MS
2754 (void)data;
2755
b9faaae1
MS
2756 return (((cupsd_job_t *)first)->id - ((cupsd_job_t *)second)->id);
2757}
ef416fc2 2758
ef416fc2 2759
178cb736
MS
2760/*
2761 * 'dump_job_history()' - Dump any debug messages for a job.
2762 */
2763
2764static void
2765dump_job_history(cupsd_job_t *job) /* I - Job */
2766{
2767 int i, /* Looping var */
2768 oldsize; /* Current MaxLogSize */
2769 struct tm *date; /* Date/time value */
2770 cupsd_joblog_t *message; /* Current message */
2771 char temp[2048], /* Log message */
2772 *ptr, /* Pointer into log message */
2773 start[256], /* Start time */
2774 end[256]; /* End time */
2775 cupsd_printer_t *printer; /* Printer for job */
2776
2777
2778 /*
2779 * See if we have anything to dump...
2780 */
2781
2782 if (!job->history)
2783 return;
2784
2785 /*
2786 * Disable log rotation temporarily...
2787 */
2788
2789 oldsize = MaxLogSize;
2790 MaxLogSize = 0;
2791
2792 /*
2793 * Copy the debug messages to the log...
2794 */
2795
2796 message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
2797 date = localtime(&(message->time));
2798 strftime(start, sizeof(start), "%X", date);
2799
2800 message = (cupsd_joblog_t *)cupsArrayLast(job->history);
2801 date = localtime(&(message->time));
2802 strftime(end, sizeof(end), "%X", date);
2803
2804 snprintf(temp, sizeof(temp),
2805 "[Job %d] The following messages were recorded from %s to %s",
2806 job->id, start, end);
2807 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2808
2809 for (message = (cupsd_joblog_t *)cupsArrayFirst(job->history);
2810 message;
2811 message = (cupsd_joblog_t *)cupsArrayNext(job->history))
2812 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, message->message);
2813
2814 snprintf(temp, sizeof(temp), "[Job %d] End of messages", job->id);
2815 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2816
2817 /*
2818 * Log the printer state values...
2819 */
2820
2821 if ((printer = job->printer) == NULL)
2822 printer = cupsdFindDest(job->dest);
2823
2824 if (printer)
2825 {
2826 snprintf(temp, sizeof(temp), "[Job %d] printer-state=%d(%s)", job->id,
2827 printer->state,
2828 printer->state == IPP_PRINTER_IDLE ? "idle" :
2829 printer->state == IPP_PRINTER_PROCESSING ? "processing" :
2830 "stopped");
2831 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2832
2833 snprintf(temp, sizeof(temp), "[Job %d] printer-state-message=\"%s\"",
2834 job->id, printer->state_message);
2835 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2836
2837 snprintf(temp, sizeof(temp), "[Job %d] printer-state-reasons=", job->id);
2838 ptr = temp + strlen(temp);
2839 if (printer->num_reasons == 0)
2840 strlcpy(ptr, "none", sizeof(temp) - (ptr - temp));
2841 else
2842 {
2843 for (i = 0;
2844 i < printer->num_reasons && ptr < (temp + sizeof(temp) - 2);
2845 i ++)
2846 {
2847 if (i)
2848 *ptr++ = ',';
2849
2850 strlcpy(ptr, printer->reasons[i], sizeof(temp) - (ptr - temp));
2851 ptr += strlen(ptr);
2852 }
2853 }
2854 cupsdWriteErrorLog(CUPSD_LOG_DEBUG, temp);
2855 }
2856
2857 /*
2858 * Restore log file rotation...
2859 */
2860
2861 MaxLogSize = oldsize;
2862
2863 /*
2864 * Free all messages...
2865 */
2866
2867 free_job_history(job);
2868}
2869
2870
2871/*
2872 * 'free_job_history()' - Free any log history.
2873 */
2874
2875static void
2876free_job_history(cupsd_job_t *job) /* I - Job */
2877{
2878 char *message; /* Current message */
2879
2880
2881 if (!job->history)
2882 return;
2883
2884 for (message = (char *)cupsArrayFirst(job->history);
2885 message;
2886 message = (char *)cupsArrayNext(job->history))
2887 free(message);
2888
2889 cupsArrayDelete(job->history);
2890 job->history = NULL;
2891}
2892
2893
b9faaae1
MS
2894/*
2895 * 'finalize_job()' - Cleanup after job filter processes and support data.
2896 */
ef416fc2 2897
b9faaae1 2898static void
ef55b745
MS
2899finalize_job(cupsd_job_t *job, /* I - Job */
2900 int set_job_state) /* I - 1 = set the job state */
b9faaae1
MS
2901{
2902 ipp_pstate_t printer_state; /* New printer state value */
2903 ipp_jstate_t job_state; /* New job state value */
2904 const char *message; /* Message for job state */
2905 char buffer[1024]; /* Buffer for formatted messages */
e00b005a 2906
b9faaae1
MS
2907
2908 cupsdLogMessage(CUPSD_LOG_DEBUG2, "finalize_job(job=%p(%d))", job, job->id);
ef416fc2 2909
e1d6a774 2910 /*
4d301e69 2911 * Clear the "connecting-to-device" reason, which is only valid when a printer
07ed0e9a 2912 * is processing, along with any remote printing job state...
e1d6a774 2913 */
ef416fc2 2914
07ed0e9a
MS
2915 cupsdSetPrinterReasons(job->printer, "-connecting-to-device,"
2916 "cups-remote-pending,"
2917 "cups-remote-pending-held,"
2918 "cups-remote-processing,"
2919 "cups-remote-stopped,"
2920 "cups-remote-canceled,"
2921 "cups-remote-aborted,"
2922 "cups-remote-completed");
ef416fc2 2923
e1d6a774 2924 /*
b9faaae1
MS
2925 * Similarly, clear the "offline-report" reason for non-USB devices since we
2926 * rarely have current information for network devices...
e1d6a774 2927 */
ef416fc2 2928
b9faaae1
MS
2929 if (strncmp(job->printer->device_uri, "usb:", 4))
2930 cupsdSetPrinterReasons(job->printer, "-offline-report");
ef416fc2 2931
b9faaae1
MS
2932 /*
2933 * Free the security profile...
2934 */
ef416fc2 2935
b9faaae1
MS
2936 cupsdDestroyProfile(job->profile);
2937 job->profile = NULL;
ef416fc2 2938
10d09e33
MS
2939 /*
2940 * Clear the unresponsive job watchdog timer...
2941 */
2942
2943 job->kill_time = 0;
2944
b9faaae1
MS
2945 /*
2946 * Close pipes and status buffer...
2947 */
ef416fc2 2948
b9faaae1
MS
2949 cupsdClosePipe(job->print_pipes);
2950 cupsdClosePipe(job->back_pipes);
2951 cupsdClosePipe(job->side_pipes);
ef416fc2 2952
e07d4801
MS
2953 cupsdRemoveSelect(job->status_pipes[0]);
2954 cupsdClosePipe(job->status_pipes);
b9faaae1
MS
2955 cupsdStatBufDelete(job->status_buffer);
2956 job->status_buffer = NULL;
ef416fc2 2957
e1d6a774 2958 /*
b9faaae1 2959 * Process the exit status...
e1d6a774 2960 */
ef416fc2 2961
1340db2d
MS
2962 if (job->printer->state == IPP_PRINTER_PROCESSING)
2963 printer_state = IPP_PRINTER_IDLE;
2964 else
2965 printer_state = job->printer->state;
2966
2967 switch (job_state = job->state_value)
2968 {
2969 case IPP_JOB_PENDING :
2970 message = "Job paused.";
2971 break;
2972
2973 case IPP_JOB_HELD :
2974 message = "Job held.";
2975 break;
2976
2977 default :
2978 case IPP_JOB_PROCESSING :
2979 case IPP_JOB_COMPLETED :
f11a948a
MS
2980 job_state = IPP_JOB_COMPLETED;
2981 message = "Job completed.";
12f89d24
MS
2982
2983 ippSetString(job->attrs, &job->reasons, 0,
2984 "job-completed-successfully");
1340db2d
MS
2985 break;
2986
2987 case IPP_JOB_STOPPED :
2988 message = "Job stopped.";
12f89d24
MS
2989
2990 ippSetString(job->attrs, &job->reasons, 0, "job-stopped");
1340db2d
MS
2991 break;
2992
2993 case IPP_JOB_CANCELED :
2994 message = "Job canceled.";
12f89d24
MS
2995
2996 ippSetString(job->attrs, &job->reasons, 0, "job-canceled-by-user");
1340db2d
MS
2997 break;
2998
2999 case IPP_JOB_ABORTED :
3000 message = "Job aborted.";
3001 break;
3002 }
ef416fc2 3003
b9faaae1 3004 if (job->status < 0)
e1d6a774 3005 {
3006 /*
b9faaae1 3007 * Backend had errors...
e1d6a774 3008 */
ef416fc2 3009
b9faaae1
MS
3010 int exit_code; /* Exit code from backend */
3011
e1d6a774 3012 /*
b9faaae1
MS
3013 * Convert the status to an exit code. Due to the way the W* macros are
3014 * implemented on MacOS X (bug?), we have to store the exit status in a
3015 * variable first and then convert...
e1d6a774 3016 */
ef416fc2 3017
b9faaae1
MS
3018 exit_code = -job->status;
3019 if (WIFEXITED(exit_code))
3020 exit_code = WEXITSTATUS(exit_code);
3021 else
12f89d24
MS
3022 {
3023 ippSetString(job->attrs, &job->reasons, 0, "cups-backend-crashed");
b9faaae1 3024 exit_code = job->status;
12f89d24 3025 }
ef416fc2 3026
b9faaae1
MS
3027 cupsdLogJob(job, CUPSD_LOG_INFO, "Backend returned status %d (%s)",
3028 exit_code,
3029 exit_code == CUPS_BACKEND_FAILED ? "failed" :
3030 exit_code == CUPS_BACKEND_AUTH_REQUIRED ?
3031 "authentication required" :
3032 exit_code == CUPS_BACKEND_HOLD ? "hold job" :
3033 exit_code == CUPS_BACKEND_STOP ? "stop printer" :
3034 exit_code == CUPS_BACKEND_CANCEL ? "cancel job" :
3035 exit_code < 0 ? "crashed" : "unknown");
ef416fc2 3036
ef416fc2 3037 /*
b9faaae1 3038 * Do what needs to be done...
ef416fc2 3039 */
3040
b9faaae1 3041 switch (exit_code)
f7deaa1a 3042 {
b9faaae1
MS
3043 default :
3044 case CUPS_BACKEND_FAILED :
3045 /*
3046 * Backend failure, use the error-policy to determine how to
3047 * act...
3048 */
f7deaa1a 3049
a2326b5b 3050 if (job->dtype & CUPS_PRINTER_CLASS)
b9faaae1
MS
3051 {
3052 /*
3053 * Queued on a class - mark the job as pending and we'll retry on
3054 * another printer...
3055 */
f7deaa1a 3056
8b116e60
MS
3057 if (job_state == IPP_JOB_COMPLETED)
3058 {
3059 job_state = IPP_JOB_PENDING;
3060 message = "Retrying job on another printer.";
12f89d24
MS
3061
3062 ippSetString(job->attrs, &job->reasons, 0,
3063 "resources-are-not-ready");
8b116e60 3064 }
b9faaae1
MS
3065 }
3066 else if (!strcmp(job->printer->error_policy, "retry-current-job"))
3067 {
3068 /*
3069 * The error policy is "retry-current-job" - mark the job as pending
3070 * and we'll retry on the same printer...
3071 */
f7deaa1a 3072
8b116e60
MS
3073 if (job_state == IPP_JOB_COMPLETED)
3074 {
3075 job_state = IPP_JOB_PENDING;
3076 message = "Retrying job on same printer.";
12f89d24
MS
3077
3078 ippSetString(job->attrs, &job->reasons, 0, "none");
8b116e60 3079 }
b9faaae1
MS
3080 }
3081 else if ((job->printer->type & CUPS_PRINTER_FAX) ||
3082 !strcmp(job->printer->error_policy, "retry-job"))
3083 {
8b116e60 3084 if (job_state == IPP_JOB_COMPLETED)
b9faaae1
MS
3085 {
3086 /*
8b116e60
MS
3087 * The job was queued on a fax or the error policy is "retry-job" -
3088 * hold the job if the number of retries is less than the
3089 * JobRetryLimit, otherwise abort the job.
b9faaae1 3090 */
ef416fc2 3091
8b116e60
MS
3092 job->tries ++;
3093
7cf5915e 3094 if (job->tries > JobRetryLimit && JobRetryLimit > 0)
8b116e60
MS
3095 {
3096 /*
3097 * Too many tries...
3098 */
3099
3100 snprintf(buffer, sizeof(buffer),
3101 "Job aborted after %d unsuccessful attempts.",
3102 JobRetryLimit);
3103 job_state = IPP_JOB_ABORTED;
3104 message = buffer;
12f89d24
MS
3105
3106 ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
8b116e60
MS
3107 }
3108 else
3109 {
3110 /*
3111 * Try again in N seconds...
3112 */
ef416fc2 3113
8b116e60
MS
3114 snprintf(buffer, sizeof(buffer),
3115 "Job held for %d seconds since it could not be sent.",
3116 JobRetryInterval);
0268488e
MS
3117
3118 job->hold_until = time(NULL) + JobRetryInterval;
3119 job_state = IPP_JOB_HELD;
3120 message = buffer;
12f89d24
MS
3121
3122 ippSetString(job->attrs, &job->reasons, 0,
3123 "resources-are-not-ready");
8b116e60
MS
3124 }
3125 }
b9faaae1 3126 }
8b116e60
MS
3127 else if (!strcmp(job->printer->error_policy, "abort-job") &&
3128 job_state == IPP_JOB_COMPLETED)
b9faaae1
MS
3129 {
3130 job_state = IPP_JOB_ABORTED;
3131 message = "Job aborted due to backend errors; please consult "
3132 "the error_log file for details.";
12f89d24
MS
3133
3134 ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
b9faaae1 3135 }
f11a948a 3136 else if (job->state_value == IPP_JOB_PROCESSING)
b9faaae1 3137 {
f11a948a 3138 job_state = IPP_JOB_PENDING;
b9faaae1 3139 printer_state = IPP_PRINTER_STOPPED;
b9faaae1 3140 message = "Printer stopped due to backend errors; please "
8b116e60 3141 "consult the error_log file for details.";
12f89d24
MS
3142
3143 ippSetString(job->attrs, &job->reasons, 0, "none");
b9faaae1
MS
3144 }
3145 break;
bd7854cb 3146
b9faaae1
MS
3147 case CUPS_BACKEND_CANCEL :
3148 /*
3149 * Abort the job...
3150 */
bd7854cb 3151
8b116e60
MS
3152 if (job_state == IPP_JOB_COMPLETED)
3153 {
3154 job_state = IPP_JOB_ABORTED;
3155 message = "Job aborted due to backend errors; please consult "
3156 "the error_log file for details.";
12f89d24
MS
3157
3158 ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
8b116e60 3159 }
b9faaae1 3160 break;
bd7854cb 3161
b9faaae1 3162 case CUPS_BACKEND_HOLD :
8b116e60
MS
3163 if (job_state == IPP_JOB_COMPLETED)
3164 {
3165 /*
3166 * Hold the job...
3167 */
bd7854cb 3168
8b116e60 3169 cupsdSetJobHoldUntil(job, "indefinite", 1);
12f89d24
MS
3170 ippSetString(job->attrs, &job->reasons, 0,
3171 "job-hold-until-specified");
bd7854cb 3172
8b116e60
MS
3173 job_state = IPP_JOB_HELD;
3174 message = "Job held indefinitely due to backend errors; please "
3175 "consult the error_log file for details.";
3176 }
b9faaae1 3177 break;
2abf387c 3178
b9faaae1
MS
3179 case CUPS_BACKEND_STOP :
3180 /*
3181 * Stop the printer...
3182 */
bd7854cb 3183
b9faaae1 3184 printer_state = IPP_PRINTER_STOPPED;
b9faaae1
MS
3185 message = "Printer stopped due to backend errors; please "
3186 "consult the error_log file for details.";
8b116e60
MS
3187
3188 if (job_state == IPP_JOB_COMPLETED)
12f89d24 3189 {
8b116e60 3190 job_state = IPP_JOB_PENDING;
12f89d24
MS
3191
3192 ippSetString(job->attrs, &job->reasons, 0,
3193 "resources-are-not-ready");
3194 }
b9faaae1 3195 break;
bd7854cb 3196
b9faaae1
MS
3197 case CUPS_BACKEND_AUTH_REQUIRED :
3198 /*
3199 * Hold the job for authentication...
3200 */
bd7854cb 3201
8b116e60
MS
3202 if (job_state == IPP_JOB_COMPLETED)
3203 {
3204 cupsdSetJobHoldUntil(job, "auth-info-required", 1);
ef416fc2 3205
8b116e60
MS
3206 job_state = IPP_JOB_HELD;
3207 message = "Job held for authentication.";
12f89d24
MS
3208
3209 ippSetString(job->attrs, &job->reasons, 0,
3210 "cups-held-for-authentication");
8b116e60 3211 }
b9faaae1 3212 break;
22c9029b
MS
3213
3214 case CUPS_BACKEND_RETRY :
3215 if (job_state == IPP_JOB_COMPLETED)
3216 {
3217 /*
3218 * Hold the job if the number of retries is less than the
3219 * JobRetryLimit, otherwise abort the job.
3220 */
3221
3222 job->tries ++;
3223
3224 if (job->tries > JobRetryLimit && JobRetryLimit > 0)
3225 {
3226 /*
3227 * Too many tries...
3228 */
3229
3230 snprintf(buffer, sizeof(buffer),
3231 "Job aborted after %d unsuccessful attempts.",
3232 JobRetryLimit);
3233 job_state = IPP_JOB_ABORTED;
3234 message = buffer;
12f89d24
MS
3235
3236 ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
22c9029b
MS
3237 }
3238 else
3239 {
3240 /*
3241 * Try again in N seconds...
3242 */
3243
3244 snprintf(buffer, sizeof(buffer),
3245 "Job held for %d seconds since it could not be sent.",
3246 JobRetryInterval);
3247
3248 job->hold_until = time(NULL) + JobRetryInterval;
3249 job_state = IPP_JOB_HELD;
3250 message = buffer;
12f89d24
MS
3251
3252 ippSetString(job->attrs, &job->reasons, 0,
3253 "resources-are-not-ready");
22c9029b
MS
3254 }
3255 }
3256 break;
3257
3258 case CUPS_BACKEND_RETRY_CURRENT :
3259 /*
3260 * Mark the job as pending and retry on the same printer...
3261 */
3262
3263 if (job_state == IPP_JOB_COMPLETED)
3264 {
3265 job_state = IPP_JOB_PENDING;
3266 message = "Retrying job on same printer.";
12f89d24
MS
3267
3268 ippSetString(job->attrs, &job->reasons, 0, "none");
22c9029b
MS
3269 }
3270 break;
e1d6a774 3271 }
3272 }
b9faaae1 3273 else if (job->status > 0)
ef416fc2 3274 {
3275 /*
b9faaae1 3276 * Filter had errors; stop job...
ef416fc2 3277 */
3278
8b116e60
MS
3279 if (job_state == IPP_JOB_COMPLETED)
3280 {
3281 job_state = IPP_JOB_STOPPED;
3282 message = "Job stopped due to filter errors; please consult the "
3283 "error_log file for details.";
12f89d24
MS
3284
3285 if (WIFSIGNALED(job->status))
3286 ippSetString(job->attrs, &job->reasons, 0, "cups-filter-crashed");
3287 else
3288 ippSetString(job->attrs, &job->reasons, 0, "job-completed-with-errors");
8b116e60 3289 }
e1d6a774 3290 }
e00b005a 3291
3dfe78b3 3292 /*
b9faaae1 3293 * Update the printer and job state.
3dfe78b3
MS
3294 */
3295
321d8d57 3296 if (set_job_state && job_state != job->state_value)
ef55b745
MS
3297 cupsdSetJobState(job, job_state, CUPSD_JOB_DEFAULT, "%s", message);
3298
b9faaae1
MS
3299 cupsdSetPrinterState(job->printer, printer_state,
3300 printer_state == IPP_PRINTER_STOPPED);
3301 update_job_attrs(job, 0);
3dfe78b3 3302
178cb736
MS
3303 if (job->history)
3304 {
a4845881
MS
3305 if (job->status &&
3306 (job->state_value == IPP_JOB_ABORTED ||
3307 job->state_value == IPP_JOB_STOPPED))
178cb736
MS
3308 dump_job_history(job);
3309 else
3310 free_job_history(job);
3311 }
3312
b9faaae1 3313 cupsArrayRemove(PrintingJobs, job);
3dfe78b3 3314
e1d6a774 3315 /*
b9faaae1 3316 * Clear the printer <-> job association...
e1d6a774 3317 */
ef416fc2 3318
b9faaae1
MS
3319 job->printer->job = NULL;
3320 job->printer = NULL;
ef416fc2 3321
e1d6a774 3322 /*
b9faaae1 3323 * Try printing another job...
e1d6a774 3324 */
ef416fc2 3325
b9faaae1
MS
3326 if (printer_state != IPP_PRINTER_STOPPED)
3327 cupsdCheckJobs();
3328}
ef416fc2 3329
ef416fc2 3330
b9faaae1
MS
3331/*
3332 * 'get_options()' - Get a string containing the job options.
3333 */
3334
3335static char * /* O - Options string */
3336get_options(cupsd_job_t *job, /* I - Job */
3337 int banner_page, /* I - Printing a banner page? */
3338 char *copies, /* I - Copies buffer */
3339 size_t copies_size, /* I - Size of copies buffer */
3340 char *title, /* I - Title buffer */
3341 size_t title_size) /* I - Size of title buffer */
3342{
3343 int i; /* Looping var */
c7017ecc 3344 size_t newlength; /* New option buffer length */
b9faaae1
MS
3345 char *optptr, /* Pointer to options */
3346 *valptr; /* Pointer in value string */
3347 ipp_attribute_t *attr; /* Current attribute */
f14324a7 3348 _ppd_cache_t *pc; /* PPD cache and mapping data */
c7017ecc
MS
3349 int num_pwgppds; /* Number of PWG->PPD options */
3350 cups_option_t *pwgppds, /* PWG->PPD options */
3351 *pwgppd, /* Current PWG->PPD option */
3352 *preset; /* Current preset option */
f14324a7
MS
3353 int print_color_mode,
3354 /* Output mode (if any) */
c7017ecc
MS
3355 print_quality; /* Print quality (if any) */
3356 const char *ppd; /* PPD option choice */
3357 int exact; /* Did we get an exact match? */
b9faaae1 3358 static char *options = NULL;/* Full list of options */
c7017ecc 3359 static size_t optlength = 0; /* Length of option buffer */
b9faaae1 3360
ef416fc2 3361
e1d6a774 3362 /*
c7017ecc
MS
3363 * Building the options string is harder than it needs to be, but for the
3364 * moment we need to pass strings for command-line args and not IPP attribute
3365 * pointers... :)
e1d6a774 3366 *
c7017ecc 3367 * First build an options array for any PWG->PPD mapped option/choice pairs.
e1d6a774 3368 */
ef416fc2 3369
f14324a7 3370 pc = job->printer->pc;
c7017ecc
MS
3371 num_pwgppds = 0;
3372 pwgppds = NULL;
ef416fc2 3373
f14324a7 3374 if (pc &&
7cf5915e
MS
3375 !ippFindAttribute(job->attrs,
3376 "com.apple.print.DocumentTicket.PMSpoolFormat",
3377 IPP_TAG_ZERO) &&
0268488e 3378 !ippFindAttribute(job->attrs, "APPrinterPreset", IPP_TAG_ZERO) &&
7cf5915e 3379 (ippFindAttribute(job->attrs, "output-mode", IPP_TAG_ZERO) ||
f14324a7 3380 ippFindAttribute(job->attrs, "print-color-mode", IPP_TAG_ZERO) ||
7cf5915e 3381 ippFindAttribute(job->attrs, "print-quality", IPP_TAG_ZERO)))
c7017ecc 3382 {
7cf5915e
MS
3383 /*
3384 * Map output-mode and print-quality to a preset...
3385 */
3386
f14324a7
MS
3387 if ((attr = ippFindAttribute(job->attrs, "print-color-mode",
3388 IPP_TAG_KEYWORD)) == NULL)
3389 attr = ippFindAttribute(job->attrs, "output-mode", IPP_TAG_KEYWORD);
3390
3391 if (attr && !strcmp(attr->values[0].string.text, "monochrome"))
3392 print_color_mode = _PWG_PRINT_COLOR_MODE_MONOCHROME;
c7017ecc 3393 else
f14324a7 3394 print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
c7017ecc
MS
3395
3396 if ((attr = ippFindAttribute(job->attrs, "print-quality",
10d09e33 3397 IPP_TAG_ENUM)) != NULL &&
c7017ecc
MS
3398 attr->values[0].integer >= IPP_QUALITY_DRAFT &&
3399 attr->values[0].integer <= IPP_QUALITY_HIGH)
3400 print_quality = attr->values[0].integer - IPP_QUALITY_DRAFT;
3401 else
030ae6a1 3402 print_quality = _PWG_PRINT_QUALITY_NORMAL;
c7017ecc 3403
f14324a7 3404 if (pc->num_presets[print_color_mode][print_quality] == 0)
c7017ecc
MS
3405 {
3406 /*
3407 * Try to find a preset that works so that we maximize the chances of us
3408 * getting a good print using IPP attributes.
3409 */
3410
f14324a7 3411 if (pc->num_presets[print_color_mode][_PWG_PRINT_QUALITY_NORMAL] > 0)
c7017ecc 3412 print_quality = _PWG_PRINT_QUALITY_NORMAL;
f14324a7
MS
3413 else if (pc->num_presets[_PWG_PRINT_COLOR_MODE_COLOR][print_quality] > 0)
3414 print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
c7017ecc
MS
3415 else
3416 {
f14324a7
MS
3417 print_quality = _PWG_PRINT_QUALITY_NORMAL;
3418 print_color_mode = _PWG_PRINT_COLOR_MODE_COLOR;
c7017ecc
MS
3419 }
3420 }
3421
f14324a7 3422 if (pc->num_presets[print_color_mode][print_quality] > 0)
c7017ecc
MS
3423 {
3424 /*
3425 * Copy the preset options as long as the corresponding names are not
3426 * already defined in the IPP request...
3427 */
3428
f14324a7
MS
3429 for (i = pc->num_presets[print_color_mode][print_quality],
3430 preset = pc->presets[print_color_mode][print_quality];
c7017ecc
MS
3431 i > 0;
3432 i --, preset ++)
3433 {
3434 if (!ippFindAttribute(job->attrs, preset->name, IPP_TAG_ZERO))
3435 num_pwgppds = cupsAddOption(preset->name, preset->value, num_pwgppds,
3436 &pwgppds);
3437 }
3438 }
7cf5915e 3439 }
c7017ecc 3440
f14324a7 3441 if (pc)
7cf5915e 3442 {
4220952d
MS
3443 if (!ippFindAttribute(job->attrs, "InputSlot", IPP_TAG_ZERO) &&
3444 !ippFindAttribute(job->attrs, "HPPaperSource", IPP_TAG_ZERO))
3445 {
f14324a7
MS
3446 if ((ppd = _ppdCacheGetInputSlot(pc, job->attrs, NULL)) != NULL)
3447 num_pwgppds = cupsAddOption(pc->source_option, ppd, num_pwgppds,
4220952d 3448 &pwgppds);
4220952d 3449 }
4220952d 3450 if (!ippFindAttribute(job->attrs, "MediaType", IPP_TAG_ZERO) &&
f14324a7 3451 (ppd = _ppdCacheGetMediaType(pc, job->attrs, NULL)) != NULL)
4220952d 3452 num_pwgppds = cupsAddOption("MediaType", ppd, num_pwgppds, &pwgppds);
c7017ecc 3453
4220952d
MS
3454 if (!ippFindAttribute(job->attrs, "PageRegion", IPP_TAG_ZERO) &&
3455 !ippFindAttribute(job->attrs, "PageSize", IPP_TAG_ZERO) &&
f14324a7 3456 (ppd = _ppdCacheGetPageSize(pc, job->attrs, NULL, &exact)) != NULL)
030ae6a1 3457 {
4220952d 3458 num_pwgppds = cupsAddOption("PageSize", ppd, num_pwgppds, &pwgppds);
c7017ecc 3459
030ae6a1
MS
3460 if (!ippFindAttribute(job->attrs, "media", IPP_TAG_ZERO))
3461 num_pwgppds = cupsAddOption("media", ppd, num_pwgppds, &pwgppds);
3462 }
3463
4220952d
MS
3464 if (!ippFindAttribute(job->attrs, "OutputBin", IPP_TAG_ZERO) &&
3465 (attr = ippFindAttribute(job->attrs, "output-bin",
3466 IPP_TAG_ZERO)) != NULL &&
3467 (attr->value_tag == IPP_TAG_KEYWORD ||
3468 attr->value_tag == IPP_TAG_NAME) &&
f14324a7 3469 (ppd = _ppdCacheGetOutputBin(pc, attr->values[0].string.text)) != NULL)
0268488e
MS
3470 {
3471 /*
3472 * Map output-bin to OutputBin option...
3473 */
3474
4220952d 3475 num_pwgppds = cupsAddOption("OutputBin", ppd, num_pwgppds, &pwgppds);
0268488e
MS
3476 }
3477
f14324a7
MS
3478 if (pc->sides_option &&
3479 !ippFindAttribute(job->attrs, pc->sides_option, IPP_TAG_ZERO) &&
0268488e
MS
3480 (attr = ippFindAttribute(job->attrs, "sides", IPP_TAG_KEYWORD)) != NULL)
3481 {
3482 /*
3483 * Map sides to duplex option...
3484 */
3485
3486 if (!strcmp(attr->values[0].string.text, "one-sided"))
f14324a7 3487 num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_1sided,
0268488e
MS
3488 num_pwgppds, &pwgppds);
3489 else if (!strcmp(attr->values[0].string.text, "two-sided-long-edge"))
f14324a7 3490 num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_long,
0268488e
MS
3491 num_pwgppds, &pwgppds);
3492 else if (!strcmp(attr->values[0].string.text, "two-sided-short-edge"))
f14324a7 3493 num_pwgppds = cupsAddOption(pc->sides_option, pc->sides_2sided_short,
0268488e
MS
3494 num_pwgppds, &pwgppds);
3495 }
dcb445bc
MS
3496
3497 /*
3498 * Map finishings values...
3499 */
3500
3501 num_pwgppds = _ppdCacheGetFinishingOptions(pc, job->attrs,
3502 IPP_FINISHINGS_NONE, num_pwgppds,
3503 &pwgppds);
4220952d 3504 }
c7017ecc
MS
3505
3506 /*
3507 * Figure out how much room we need...
3508 */
3509
3510 newlength = ipp_length(job->attrs);
3511
3512 for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
3513 newlength += 1 + strlen(pwgppd->name) + 1 + strlen(pwgppd->value);
3514
3515 /*
3516 * Then allocate/reallocate the option buffer as needed...
3517 */
3518
85dda01c
MS
3519 if (newlength == 0) /* This can never happen, but Clang */
3520 newlength = 1; /* thinks it can... */
3521
c7017ecc 3522 if (newlength > optlength || !options)
e1d6a774 3523 {
b9faaae1 3524 if (!options)
c7017ecc 3525 optptr = malloc(newlength);
e1d6a774 3526 else
c7017ecc 3527 optptr = realloc(options, newlength);
ef416fc2 3528
b9faaae1 3529 if (!optptr)
e1d6a774 3530 {
75bd9771 3531 cupsdLogJob(job, CUPSD_LOG_CRIT,
12f89d24 3532 "Unable to allocate " CUPS_LLFMT " bytes for option buffer.",
c7017ecc 3533 CUPS_LLCAST newlength);
b9faaae1 3534 return (NULL);
e1d6a774 3535 }
ef416fc2 3536
e1d6a774 3537 options = optptr;
c7017ecc 3538 optlength = newlength;
e1d6a774 3539 }
ef416fc2 3540
e1d6a774 3541 /*
3542 * Now loop through the attributes and convert them to the textual
3543 * representation used by the filters...
3544 */
ef416fc2 3545
e1d6a774 3546 optptr = options;
3547 *optptr = '\0';
bd7854cb 3548
1340db2d
MS
3549 snprintf(title, title_size, "%s-%d", job->printer->name, job->id);
3550 strlcpy(copies, "1", copies_size);
bd7854cb 3551
e1d6a774 3552 for (attr = job->attrs->attrs; attr != NULL; attr = attr->next)
bd7854cb 3553 {
e1d6a774 3554 if (!strcmp(attr->name, "copies") &&
3555 attr->value_tag == IPP_TAG_INTEGER)
3556 {
3557 /*
3558 * Don't use the # copies attribute if we are printing the job sheets...
3559 */
bd7854cb 3560
e1d6a774 3561 if (!banner_page)
1340db2d 3562 snprintf(copies, copies_size, "%d", attr->values[0].integer);
e1d6a774 3563 }
3564 else if (!strcmp(attr->name, "job-name") &&
3565 (attr->value_tag == IPP_TAG_NAME ||
3566 attr->value_tag == IPP_TAG_NAMELANG))
1340db2d 3567 strlcpy(title, attr->values[0].string.text, title_size);
e1d6a774 3568 else if (attr->group_tag == IPP_TAG_JOB)
3569 {
3570 /*
3571 * Filter out other unwanted attributes...
3572 */
bd7854cb 3573
c7017ecc
MS
3574 if (attr->value_tag == IPP_TAG_NOVALUE ||
3575 attr->value_tag == IPP_TAG_MIMETYPE ||
e1d6a774 3576 attr->value_tag == IPP_TAG_NAMELANG ||
3577 attr->value_tag == IPP_TAG_TEXTLANG ||
3578 (attr->value_tag == IPP_TAG_URI && strcmp(attr->name, "job-uuid")) ||
3579 attr->value_tag == IPP_TAG_URISCHEME ||
3580 attr->value_tag == IPP_TAG_BEGIN_COLLECTION) /* Not yet supported */
3581 continue;
bd7854cb 3582
c7017ecc 3583 if (!strcmp(attr->name, "job-hold-until"))
e1d6a774 3584 continue;
bd7854cb 3585
e1d6a774 3586 if (!strncmp(attr->name, "job-", 4) &&
e4572d57 3587 strcmp(attr->name, "job-billing") &&
5d6412a9 3588 strcmp(attr->name, "job-impressions") &&
c5571a1d 3589 strcmp(attr->name, "job-originating-host-name") &&
e4572d57 3590 strcmp(attr->name, "job-uuid") &&
b9faaae1 3591 !(job->printer->type & CUPS_PRINTER_REMOTE))
e1d6a774 3592 continue;
ef416fc2 3593
5d6412a9
MS
3594 if ((!strcmp(attr->name, "job-impressions") ||
3595 !strcmp(attr->name, "page-label") ||
e1d6a774 3596 !strcmp(attr->name, "page-border") ||
3597 !strncmp(attr->name, "number-up", 9) ||
b94498cf 3598 !strcmp(attr->name, "page-ranges") ||
e1d6a774 3599 !strcmp(attr->name, "page-set") ||
88f9aafc
MS
3600 !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_InputSlot") ||
3601 !_cups_strcasecmp(attr->name, "AP_FIRSTPAGE_ManualFeed") ||
3602 !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
db1f069b 3603 "PMTotalSidesImaged..n.") ||
88f9aafc 3604 !_cups_strcasecmp(attr->name, "com.apple.print.PrintSettings."
db1f069b 3605 "PMTotalBeginPages..n.")) &&
e1d6a774 3606 banner_page)
3607 continue;
ef416fc2 3608
e1d6a774 3609 /*
3610 * Otherwise add them to the list...
3611 */
ef416fc2 3612
e1d6a774 3613 if (optptr > options)
3614 strlcat(optptr, " ", optlength - (optptr - options));
ef416fc2 3615
e1d6a774 3616 if (attr->value_tag != IPP_TAG_BOOLEAN)
3617 {
3618 strlcat(optptr, attr->name, optlength - (optptr - options));
3619 strlcat(optptr, "=", optlength - (optptr - options));
3620 }
ef416fc2 3621
e1d6a774 3622 for (i = 0; i < attr->num_values; i ++)
3623 {
3624 if (i)
3625 strlcat(optptr, ",", optlength - (optptr - options));
ef416fc2 3626
e1d6a774 3627 optptr += strlen(optptr);
ef416fc2 3628
e1d6a774 3629 switch (attr->value_tag)
3630 {
3631 case IPP_TAG_INTEGER :
3632 case IPP_TAG_ENUM :
3633 snprintf(optptr, optlength - (optptr - options),
3634 "%d", attr->values[i].integer);
3635 break;
ef416fc2 3636
e1d6a774 3637 case IPP_TAG_BOOLEAN :
3638 if (!attr->values[i].boolean)
3639 strlcat(optptr, "no", optlength - (optptr - options));
ef416fc2 3640
4220952d
MS
3641 strlcat(optptr, attr->name,
3642 optlength - (optptr - options));
3643 break;
3644
e1d6a774 3645 case IPP_TAG_RANGE :
3646 if (attr->values[i].range.lower == attr->values[i].range.upper)
3647 snprintf(optptr, optlength - (optptr - options) - 1,
3648 "%d", attr->values[i].range.lower);
3649 else
3650 snprintf(optptr, optlength - (optptr - options) - 1,
3651 "%d-%d", attr->values[i].range.lower,
3652 attr->values[i].range.upper);
3653 break;
ef416fc2 3654
e1d6a774 3655 case IPP_TAG_RESOLUTION :
3656 snprintf(optptr, optlength - (optptr - options) - 1,
3657 "%dx%d%s", attr->values[i].resolution.xres,
3658 attr->values[i].resolution.yres,
3659 attr->values[i].resolution.units == IPP_RES_PER_INCH ?
3660 "dpi" : "dpc");
3661 break;
ef416fc2 3662
e1d6a774 3663 case IPP_TAG_STRING :
3664 case IPP_TAG_TEXT :
3665 case IPP_TAG_NAME :
3666 case IPP_TAG_KEYWORD :
3667 case IPP_TAG_CHARSET :
3668 case IPP_TAG_LANGUAGE :
3669 case IPP_TAG_URI :
3670 for (valptr = attr->values[i].string.text; *valptr;)
3671 {
3672 if (strchr(" \t\n\\\'\"", *valptr))
3673 *optptr++ = '\\';
3674 *optptr++ = *valptr++;
3675 }
3676
3677 *optptr = '\0';
3678 break;
3679
3680 default :
3681 break; /* anti-compiler-warning-code */
3682 }
3683 }
3684
3685 optptr += strlen(optptr);
3686 }
3687 }
3688
c7017ecc
MS
3689 /*
3690 * Finally loop through the PWG->PPD mapped options and add them...
3691 */
3692
3693 for (i = num_pwgppds, pwgppd = pwgppds; i > 0; i --, pwgppd ++)
3694 {
3695 *optptr++ = ' ';
3696 strcpy(optptr, pwgppd->name);
3697 optptr += strlen(optptr);
3698 *optptr++ = '=';
3699 strcpy(optptr, pwgppd->value);
3700 optptr += strlen(optptr);
3701 }
3702
3703 cupsFreeOptions(num_pwgppds, pwgppds);
3704
3705 /*
3706 * Return the options string...
3707 */
b9faaae1
MS
3708
3709 return (options);
3710}
3711
3712
3713/*
3714 * 'ipp_length()' - Compute the size of the buffer needed to hold
3715 * the textual IPP attributes.
3716 */
3717
c7017ecc 3718static size_t /* O - Size of attribute buffer */
b9faaae1
MS
3719ipp_length(ipp_t *ipp) /* I - IPP request */
3720{
c7017ecc 3721 size_t bytes; /* Number of bytes */
b9faaae1
MS
3722 int i; /* Looping var */
3723 ipp_attribute_t *attr; /* Current attribute */
3724
3725
3726 /*
3727 * Loop through all attributes...
e1d6a774 3728 */
3729
b9faaae1 3730 bytes = 0;
e1d6a774 3731
b9faaae1 3732 for (attr = ipp->attrs; attr != NULL; attr = attr->next)
91c84a35 3733 {
b9faaae1
MS
3734 /*
3735 * Skip attributes that won't be sent to filters...
3736 */
91c84a35 3737
c7017ecc
MS
3738 if (attr->value_tag == IPP_TAG_NOVALUE ||
3739 attr->value_tag == IPP_TAG_MIMETYPE ||
b9faaae1
MS
3740 attr->value_tag == IPP_TAG_NAMELANG ||
3741 attr->value_tag == IPP_TAG_TEXTLANG ||
3742 attr->value_tag == IPP_TAG_URI ||
3743 attr->value_tag == IPP_TAG_URISCHEME)
3744 continue;
91c84a35 3745
b9faaae1
MS
3746 /*
3747 * Add space for a leading space and commas between each value.
3748 * For the first attribute, the leading space isn't used, so the
3749 * extra byte can be used as the nul terminator...
3750 */
e1d6a774 3751
b9faaae1
MS
3752 bytes ++; /* " " separator */
3753 bytes += attr->num_values; /* "," separators */
e1d6a774 3754
b9faaae1
MS
3755 /*
3756 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
3757 * other attributes appear as "foo=value1,value2,...,valueN".
3758 */
3759
3760 if (attr->value_tag != IPP_TAG_BOOLEAN)
3761 bytes += strlen(attr->name);
3762 else
3763 bytes += attr->num_values * strlen(attr->name);
3764
3765 /*
3766 * Now add the size required for each value in the attribute...
3767 */
3768
3769 switch (attr->value_tag)
ef416fc2 3770 {
b9faaae1
MS
3771 case IPP_TAG_INTEGER :
3772 case IPP_TAG_ENUM :
3773 /*
3774 * Minimum value of a signed integer is -2147483647, or 11 digits.
3775 */
3776
3777 bytes += attr->num_values * 11;
3778 break;
3779
3780 case IPP_TAG_BOOLEAN :
3781 /*
3782 * Add two bytes for each false ("no") value...
3783 */
3784
3785 for (i = 0; i < attr->num_values; i ++)
3786 if (!attr->values[i].boolean)
3787 bytes += 2;
3788 break;
3789
3790 case IPP_TAG_RANGE :
3791 /*
3792 * A range is two signed integers separated by a hyphen, or
3793 * 23 characters max.
3794 */
3795
3796 bytes += attr->num_values * 23;
3797 break;
3798
3799 case IPP_TAG_RESOLUTION :
3800 /*
3801 * A resolution is two signed integers separated by an "x" and
3802 * suffixed by the units, or 26 characters max.
3803 */
3804
3805 bytes += attr->num_values * 26;
3806 break;
3807
3808 case IPP_TAG_STRING :
3809 case IPP_TAG_TEXT :
3810 case IPP_TAG_NAME :
3811 case IPP_TAG_KEYWORD :
3812 case IPP_TAG_CHARSET :
3813 case IPP_TAG_LANGUAGE :
3814 case IPP_TAG_URI :
3815 /*
3816 * Strings can contain characters that need quoting. We need
3817 * at least 2 * len + 2 characters to cover the quotes and
3818 * any backslashes in the string.
3819 */
3820
3821 for (i = 0; i < attr->num_values; i ++)
3822 bytes += 2 * strlen(attr->values[i].string.text) + 2;
3823 break;
3824
3825 default :
3826 break; /* anti-compiler-warning-code */
e1d6a774 3827 }
3828 }
b9faaae1
MS
3829
3830 return (bytes);
3831}
3832
3833
3834/*
3835 * 'load_job_cache()' - Load jobs from the job.cache file.
3836 */
3837
3838static void
3839load_job_cache(const char *filename) /* I - job.cache filename */
3840{
3841 cups_file_t *fp; /* job.cache file */
3842 char line[1024], /* Line buffer */
3843 *value; /* Value on line */
3844 int linenum; /* Line number in file */
3845 cupsd_job_t *job; /* Current job */
3846 int jobid; /* Job ID */
3847 char jobfile[1024]; /* Job filename */
3848
3849
3850 /*
3851 * Open the job.cache file...
3852 */
3853
321d8d57 3854 if ((fp = cupsdOpenConfFile(filename)) == NULL)
e1d6a774 3855 {
b9faaae1 3856 load_request_root();
b9faaae1
MS
3857 return;
3858 }
ef416fc2 3859
e1d6a774 3860 /*
b9faaae1 3861 * Read entries from the job cache file and create jobs as needed.
e1d6a774 3862 */
ef416fc2 3863
b9faaae1
MS
3864 cupsdLogMessage(CUPSD_LOG_INFO, "Loading job cache file \"%s\"...",
3865 filename);
ef416fc2 3866
b9faaae1
MS
3867 linenum = 0;
3868 job = NULL;
3869
3870 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
3871 {
88f9aafc 3872 if (!_cups_strcasecmp(line, "NextJobId"))
b9faaae1
MS
3873 {
3874 if (value)
3875 NextJobId = atoi(value);
3876 }
88f9aafc 3877 else if (!_cups_strcasecmp(line, "<Job"))
b9faaae1
MS
3878 {
3879 if (job)
3880 {
12f89d24 3881 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing </Job> directive on line %d.",
b9faaae1
MS
3882 linenum);
3883 continue;
3884 }
3885
3886 if (!value)
3887 {
12f89d24 3888 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing job ID on line %d.", linenum);
b9faaae1
MS
3889 continue;
3890 }
3891
3892 jobid = atoi(value);
3893
3894 if (jobid < 1)
3895 {
12f89d24 3896 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad job ID %d on line %d.", jobid,
b9faaae1
MS
3897 linenum);
3898 continue;
3899 }
3900
3901 snprintf(jobfile, sizeof(jobfile), "%s/c%05d", RequestRoot, jobid);
3902 if (access(jobfile, 0))
3903 {
321d8d57
MS
3904 snprintf(jobfile, sizeof(jobfile), "%s/c%05d.N", RequestRoot, jobid);
3905 if (access(jobfile, 0))
3906 {
12f89d24 3907 cupsdLogMessage(CUPSD_LOG_ERROR, "[Job %d] Files have gone away.",
321d8d57
MS
3908 jobid);
3909 continue;
3910 }
b9faaae1
MS
3911 }
3912
3913 job = calloc(1, sizeof(cupsd_job_t));
3914 if (!job)
3915 {
3916 cupsdLogMessage(CUPSD_LOG_EMERG,
12f89d24 3917 "[Job %d] Unable to allocate memory for job.", jobid);
b9faaae1
MS
3918 break;
3919 }
3920
3921 job->id = jobid;
3922 job->back_pipes[0] = -1;
3923 job->back_pipes[1] = -1;
3924 job->print_pipes[0] = -1;
3925 job->print_pipes[1] = -1;
3926 job->side_pipes[0] = -1;
3927 job->side_pipes[1] = -1;
3928 job->status_pipes[0] = -1;
3929 job->status_pipes[1] = -1;
3930
3931 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Loading from cache...",
3932 job->id);
3933 }
3934 else if (!job)
3935 {
3936 cupsdLogMessage(CUPSD_LOG_ERROR,
12f89d24 3937 "Missing <Job #> directive on line %d.", linenum);
b9faaae1
MS
3938 continue;
3939 }
88f9aafc 3940 else if (!_cups_strcasecmp(line, "</Job>"))
b9faaae1
MS
3941 {
3942 cupsArrayAdd(Jobs, job);
3943
f701418f
MS
3944 if (job->state_value <= IPP_JOB_STOPPED && cupsdLoadJob(job))
3945 cupsArrayAdd(ActiveJobs, job);
b9faaae1
MS
3946
3947 job = NULL;
3948 }
3949 else if (!value)
3950 {
12f89d24 3951 cupsdLogMessage(CUPSD_LOG_ERROR, "Missing value on line %d.", linenum);
b9faaae1
MS
3952 continue;
3953 }
88f9aafc 3954 else if (!_cups_strcasecmp(line, "State"))
b9faaae1
MS
3955 {
3956 job->state_value = (ipp_jstate_t)atoi(value);
3957
3958 if (job->state_value < IPP_JOB_PENDING)
3959 job->state_value = IPP_JOB_PENDING;
3960 else if (job->state_value > IPP_JOB_COMPLETED)
3961 job->state_value = IPP_JOB_COMPLETED;
3962 }
88f9aafc 3963 else if (!_cups_strcasecmp(line, "HoldUntil"))
8b116e60
MS
3964 {
3965 job->hold_until = atoi(value);
3966 }
88f9aafc 3967 else if (!_cups_strcasecmp(line, "Priority"))
b9faaae1
MS
3968 {
3969 job->priority = atoi(value);
3970 }
88f9aafc 3971 else if (!_cups_strcasecmp(line, "Username"))
b9faaae1
MS
3972 {
3973 cupsdSetString(&job->username, value);
3974 }
88f9aafc 3975 else if (!_cups_strcasecmp(line, "Destination"))
b9faaae1
MS
3976 {
3977 cupsdSetString(&job->dest, value);
3978 }
88f9aafc 3979 else if (!_cups_strcasecmp(line, "DestType"))
b9faaae1
MS
3980 {
3981 job->dtype = (cups_ptype_t)atoi(value);
3982 }
88f9aafc 3983 else if (!_cups_strcasecmp(line, "NumFiles"))
b9faaae1
MS
3984 {
3985 job->num_files = atoi(value);
3986
3987 if (job->num_files < 0)
3988 {
12f89d24 3989 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad NumFiles value %d on line %d.",
b9faaae1
MS
3990 job->num_files, linenum);
3991 job->num_files = 0;
3992 continue;
3993 }
3994
3995 if (job->num_files > 0)
3996 {
3997 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-001", RequestRoot,
3998 job->id);
3999 if (access(jobfile, 0))
4000 {
12f89d24 4001 cupsdLogMessage(CUPSD_LOG_INFO, "[Job %d] Data files have gone away.",
b9faaae1
MS
4002 job->id);
4003 job->num_files = 0;
4004 continue;
4005 }
4006
4007 job->filetypes = calloc(job->num_files, sizeof(mime_type_t *));
4008 job->compressions = calloc(job->num_files, sizeof(int));
4009
4010 if (!job->filetypes || !job->compressions)
4011 {
4012 cupsdLogMessage(CUPSD_LOG_EMERG,
12f89d24 4013 "[Job %d] Unable to allocate memory for %d files.",
b9faaae1
MS
4014 job->id, job->num_files);
4015 break;
4016 }
4017 }
4018 }
88f9aafc 4019 else if (!_cups_strcasecmp(line, "File"))
b9faaae1
MS
4020 {
4021 int number, /* File number */
4022 compression; /* Compression value */
4023 char super[MIME_MAX_SUPER], /* MIME super type */
4024 type[MIME_MAX_TYPE]; /* MIME type */
4025
4026
4027 if (sscanf(value, "%d%*[ \t]%15[^/]/%255s%d", &number, super, type,
4028 &compression) != 4)
4029 {
12f89d24 4030 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File on line %d.", linenum);
b9faaae1
MS
4031 continue;
4032 }
4033
4034 if (number < 1 || number > job->num_files)
4035 {
12f89d24 4036 cupsdLogMessage(CUPSD_LOG_ERROR, "Bad File number %d on line %d.",
b9faaae1
MS
4037 number, linenum);
4038 continue;
4039 }
0a682745 4040
b9faaae1 4041 number --;
ef416fc2 4042
b9faaae1
MS
4043 job->compressions[number] = compression;
4044 job->filetypes[number] = mimeType(MimeDatabase, super, type);
ef416fc2 4045
b9faaae1
MS
4046 if (!job->filetypes[number])
4047 {
e1d6a774 4048 /*
b9faaae1 4049 * If the original MIME type is unknown, auto-type it!
e1d6a774 4050 */
ef416fc2 4051
b9faaae1 4052 cupsdLogMessage(CUPSD_LOG_ERROR,
12f89d24 4053 "[Job %d] Unknown MIME type %s/%s for file %d.",
b9faaae1
MS
4054 job->id, super, type, number + 1);
4055
4056 snprintf(jobfile, sizeof(jobfile), "%s/d%05d-%03d", RequestRoot,
4057 job->id, number + 1);
4058 job->filetypes[number] = mimeFileType(MimeDatabase, jobfile, NULL,
4059 job->compressions + number);
ef416fc2 4060
e1d6a774 4061 /*
b9faaae1 4062 * If that didn't work, assume it is raw...
e1d6a774 4063 */
ef416fc2 4064
b9faaae1
MS
4065 if (!job->filetypes[number])
4066 job->filetypes[number] = mimeType(MimeDatabase, "application",
4067 "vnd.cups-raw");
4068 }
4069 }
4070 else
12f89d24 4071 cupsdLogMessage(CUPSD_LOG_ERROR, "Unknown %s directive on line %d.",
b9faaae1 4072 line, linenum);
ef416fc2 4073 }
4074
b9faaae1
MS
4075 cupsFileClose(fp);
4076}
ef416fc2 4077
4078
b9faaae1
MS
4079/*
4080 * 'load_next_job_id()' - Load the NextJobId value from the job.cache file.
4081 */
bd7854cb 4082
b9faaae1
MS
4083static void
4084load_next_job_id(const char *filename) /* I - job.cache filename */
4085{
4086 cups_file_t *fp; /* job.cache file */
4087 char line[1024], /* Line buffer */
4088 *value; /* Value on line */
4089 int linenum; /* Line number in file */
4090 int next_job_id; /* NextJobId value from line */
db0bd74a 4091
db0bd74a 4092
b9faaae1
MS
4093 /*
4094 * Read the NextJobId directive from the job.cache file and use
4095 * the value (if any).
4096 */
bd7854cb 4097
b9faaae1 4098 if ((fp = cupsFileOpen(filename, "r")) == NULL)
e1d6a774 4099 {
b9faaae1
MS
4100 if (errno != ENOENT)
4101 cupsdLogMessage(CUPSD_LOG_ERROR,
4102 "Unable to open job cache file \"%s\": %s",
4103 filename, strerror(errno));
bd7854cb 4104
b9faaae1 4105 return;
e1d6a774 4106 }
bd7854cb 4107
b9faaae1
MS
4108 cupsdLogMessage(CUPSD_LOG_INFO,
4109 "Loading NextJobId from job cache file \"%s\"...", filename);
4110
4111 linenum = 0;
4112
4113 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
bd7854cb 4114 {
88f9aafc 4115 if (!_cups_strcasecmp(line, "NextJobId"))
b9faaae1
MS
4116 {
4117 if (value)
4118 {
4119 next_job_id = atoi(value);
4120
4121 if (next_job_id > NextJobId)
4122 NextJobId = next_job_id;
4123 }
4124 break;
4125 }
e1d6a774 4126 }
bd7854cb 4127
b9faaae1
MS
4128 cupsFileClose(fp);
4129}
09a101d6 4130
f7deaa1a 4131
b9faaae1
MS
4132/*
4133 * 'load_request_root()' - Load jobs from the RequestRoot directory.
4134 */
bd7854cb 4135
b9faaae1
MS
4136static void
4137load_request_root(void)
4138{
4139 cups_dir_t *dir; /* Directory */
4140 cups_dentry_t *dent; /* Directory entry */
4141 cupsd_job_t *job; /* New job */
e1d6a774 4142
bd7854cb 4143
4144 /*
b9faaae1 4145 * Open the requests directory...
bd7854cb 4146 */
4147
b9faaae1 4148 cupsdLogMessage(CUPSD_LOG_DEBUG, "Scanning %s for jobs...", RequestRoot);
bd7854cb 4149
b9faaae1 4150 if ((dir = cupsDirOpen(RequestRoot)) == NULL)
bd7854cb 4151 {
b9faaae1
MS
4152 cupsdLogMessage(CUPSD_LOG_ERROR,
4153 "Unable to open spool directory \"%s\": %s",
4154 RequestRoot, strerror(errno));
4155 return;
e1d6a774 4156 }
bd7854cb 4157
b9faaae1
MS
4158 /*
4159 * Read all the c##### files...
4160 */
bd7854cb 4161
b9faaae1
MS
4162 while ((dent = cupsDirRead(dir)) != NULL)
4163 if (strlen(dent->filename) >= 6 && dent->filename[0] == 'c')
bd7854cb 4164 {
b9faaae1
MS
4165 /*
4166 * Allocate memory for the job...
4167 */
bd7854cb 4168
b9faaae1 4169 if ((job = calloc(sizeof(cupsd_job_t), 1)) == NULL)
bd7854cb 4170 {
12f89d24 4171 cupsdLogMessage(CUPSD_LOG_ERROR, "Ran out of memory for jobs.");
b9faaae1
MS
4172 cupsDirClose(dir);
4173 return;
4174 }
bd7854cb 4175
b9faaae1
MS
4176 /*
4177 * Assign the job ID...
4178 */
bd7854cb 4179
b9faaae1
MS
4180 job->id = atoi(dent->filename + 1);
4181 job->back_pipes[0] = -1;
4182 job->back_pipes[1] = -1;
4183 job->print_pipes[0] = -1;
4184 job->print_pipes[1] = -1;
4185 job->side_pipes[0] = -1;
4186 job->side_pipes[1] = -1;
4187 job->status_pipes[0] = -1;
4188 job->status_pipes[1] = -1;
bd7854cb 4189
b9faaae1
MS
4190 if (job->id >= NextJobId)
4191 NextJobId = job->id + 1;
ed486911 4192
b9faaae1
MS
4193 /*
4194 * Load the job...
4195 */
ed486911 4196
f701418f
MS
4197 if (cupsdLoadJob(job))
4198 {
4199 /*
4200 * Insert the job into the array, sorting by job priority and ID...
4201 */
bd7854cb 4202
f701418f 4203 cupsArrayAdd(Jobs, job);
e1d6a774 4204
f701418f
MS
4205 if (job->state_value <= IPP_JOB_STOPPED)
4206 cupsArrayAdd(ActiveJobs, job);
4207 else
4208 unload_job(job);
4209 }
bd7854cb 4210 }
bd7854cb 4211
b9faaae1
MS
4212 cupsDirClose(dir);
4213}
bd7854cb 4214
4215
b9faaae1
MS
4216/*
4217 * 'set_time()' - Set one of the "time-at-xyz" attributes.
4218 */
bd7854cb 4219
b9faaae1
MS
4220static void
4221set_time(cupsd_job_t *job, /* I - Job to update */
4222 const char *name) /* I - Name of attribute */
4223{
4224 ipp_attribute_t *attr; /* Time attribute */
bd7854cb 4225
bd7854cb 4226
b9faaae1
MS
4227 if ((attr = ippFindAttribute(job->attrs, name, IPP_TAG_ZERO)) != NULL)
4228 {
4229 attr->value_tag = IPP_TAG_INTEGER;
4230 attr->values[0].integer = time(NULL);
4231 }
4232}
bd7854cb 4233
e1d6a774 4234
b9faaae1
MS
4235/*
4236 * 'start_job()' - Start a print job.
4237 */
e1d6a774 4238
b9faaae1
MS
4239static void
4240start_job(cupsd_job_t *job, /* I - Job ID */
4241 cupsd_printer_t *printer) /* I - Printer to print job */
4242{
4243 cupsdLogMessage(CUPSD_LOG_DEBUG2, "start_job(job=%p(%d), printer=%p(%s))",
4244 job, job->id, printer, printer->name);
bd7854cb 4245
b9faaae1
MS
4246 /*
4247 * Make sure we have some files around before we try to print...
4248 */
bd7854cb 4249
b9faaae1
MS
4250 if (job->num_files == 0)
4251 {
12f89d24 4252 ippSetString(job->attrs, &job->reasons, 0, "aborted-by-system");
b9faaae1
MS
4253 cupsdSetJobState(job, IPP_JOB_ABORTED, CUPSD_JOB_DEFAULT,
4254 "Aborting job because it has no files.");
4255 return;
4256 }
bd7854cb 4257
b9faaae1
MS
4258 /*
4259 * Update the printer and job state to "processing"...
4260 */
bd7854cb 4261
b9faaae1
MS
4262 if (!cupsdLoadJob(job))
4263 return;
89d46774 4264
88f9aafc
MS
4265 if (job->printer_message)
4266 cupsdSetString(&(job->printer_message->values[0].string.text), "");
4267
12f89d24 4268 ippSetString(job->attrs, &job->reasons, 0, "job-printing");
b9faaae1
MS
4269 cupsdSetJobState(job, IPP_JOB_PROCESSING, CUPSD_JOB_DEFAULT, NULL);
4270 cupsdSetPrinterState(printer, IPP_PRINTER_PROCESSING, 0);
07ed0e9a
MS
4271 cupsdSetPrinterReasons(printer, "-cups-remote-pending,"
4272 "cups-remote-pending-held,"
4273 "cups-remote-processing,"
4274 "cups-remote-stopped,"
4275 "cups-remote-canceled,"
4276 "cups-remote-aborted,"
4277 "cups-remote-completed");
f7deaa1a 4278
1340db2d
MS
4279 job->cost = 0;
4280 job->current_file = 0;
4281 job->progress = 0;
4282 job->printer = printer;
4283 printer->job = job;
f7deaa1a 4284
dcb445bc
MS
4285 if (MaxJobTime > 0)
4286 job->cancel_time = time(NULL) + MaxJobTime;
4287 else
4288 job->cancel_time = 0;
4289
b9faaae1
MS
4290 /*
4291 * Setup the last exit status and security profiles...
4292 */
89d46774 4293
b9faaae1
MS
4294 job->status = 0;
4295 job->profile = cupsdCreateProfile(job->id);
bd7854cb 4296
b9faaae1
MS
4297 /*
4298 * Create the status pipes and buffer...
4299 */
bd7854cb 4300
b9faaae1
MS
4301 if (cupsdOpenPipe(job->status_pipes))
4302 {
4303 cupsdLogJob(job, CUPSD_LOG_DEBUG,
4304 "Unable to create job status pipes - %s.", strerror(errno));
89d46774 4305
b9faaae1
MS
4306 cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
4307 "Job stopped because the scheduler could not create the "
4308 "job status pipes.");
89d46774 4309
b9faaae1
MS
4310 cupsdDestroyProfile(job->profile);
4311 job->profile = NULL;
4312 return;
e1d6a774 4313 }
bd7854cb 4314
b9faaae1
MS
4315 job->status_buffer = cupsdStatBufNew(job->status_pipes[0], NULL);
4316 job->status_level = CUPSD_LOG_INFO;
4317
4318 /*
4319 * Create the backchannel pipes and make them non-blocking...
4320 */
bd7854cb 4321
b9faaae1 4322 if (cupsdOpenPipe(job->back_pipes))
e1d6a774 4323 {
b9faaae1
MS
4324 cupsdLogJob(job, CUPSD_LOG_DEBUG,
4325 "Unable to create back-channel pipes - %s.", strerror(errno));
4326
4327 cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
4328 "Job stopped because the scheduler could not create the "
4329 "back-channel pipes.");
4330
4331 cupsdClosePipe(job->status_pipes);
4332 cupsdStatBufDelete(job->status_buffer);
4333 job->status_buffer = NULL;
4334
4335 cupsdDestroyProfile(job->profile);
4336 job->profile = NULL;
4337 return;
e1d6a774 4338 }
bd7854cb 4339
b9faaae1
MS
4340 fcntl(job->back_pipes[0], F_SETFL,
4341 fcntl(job->back_pipes[0], F_GETFL) | O_NONBLOCK);
4342 fcntl(job->back_pipes[1], F_SETFL,
4343 fcntl(job->back_pipes[1], F_GETFL) | O_NONBLOCK);
ef416fc2 4344
b9faaae1
MS
4345 /*
4346 * Create the side-channel pipes and make them non-blocking...
4347 */
ef416fc2 4348
b9faaae1
MS
4349 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, job->side_pipes))
4350 {
4351 cupsdLogJob(job, CUPSD_LOG_DEBUG,
4352 "Unable to create side-channel pipes - %s.", strerror(errno));
ef416fc2 4353
b9faaae1
MS
4354 cupsdSetJobState(job, IPP_JOB_STOPPED, CUPSD_JOB_DEFAULT,
4355 "Job stopped because the scheduler could not create the "
4356 "side-channel pipes.");
ef416fc2 4357
b9faaae1
MS
4358 cupsdClosePipe(job->back_pipes);
4359
4360 cupsdClosePipe(job->status_pipes);
4361 cupsdStatBufDelete(job->status_buffer);
4362 job->status_buffer = NULL;
4363
4364 cupsdDestroyProfile(job->profile);
4365 job->profile = NULL;
4366 return;
4367 }
4368
4369 fcntl(job->side_pipes[0], F_SETFL,
4370 fcntl(job->side_pipes[0], F_GETFL) | O_NONBLOCK);
4371 fcntl(job->side_pipes[1], F_SETFL,
4372 fcntl(job->side_pipes[1], F_GETFL) | O_NONBLOCK);
ef416fc2 4373
7a0cbd5e
MS
4374 fcntl(job->side_pipes[0], F_SETFD,
4375 fcntl(job->side_pipes[0], F_GETFD) | FD_CLOEXEC);
4376 fcntl(job->side_pipes[1], F_SETFD,
4377 fcntl(job->side_pipes[1], F_GETFD) | FD_CLOEXEC);
4378
ef416fc2 4379 /*
b9faaae1 4380 * Now start the first file in the job...
ef416fc2 4381 */
4382
b9faaae1
MS
4383 cupsdContinueJob(job);
4384}
ef416fc2 4385
ef416fc2 4386
b9faaae1
MS
4387/*
4388 * 'stop_job()' - Stop a print job.
4389 */
ef416fc2 4390
b9faaae1
MS
4391static void
4392stop_job(cupsd_job_t *job, /* I - Job */
4393 cupsd_jobaction_t action) /* I - Action */
4394{
4395 int i; /* Looping var */
b94498cf 4396
ef416fc2 4397
b9faaae1
MS
4398 cupsdLogMessage(CUPSD_LOG_DEBUG2, "stop_job(job=%p(%d), action=%d)", job,
4399 job->id, action);
ef416fc2 4400
b9faaae1
MS
4401 FilterLevel -= job->cost;
4402 job->cost = 0;
ef416fc2 4403
238c3832
MS
4404 if (action == CUPSD_JOB_DEFAULT && !job->kill_time)
4405 job->kill_time = time(NULL) + JobKillDelay;
ef55b745 4406 else if (action >= CUPSD_JOB_FORCE)
238c3832
MS
4407 job->kill_time = 0;
4408
b9faaae1
MS
4409 for (i = 0; job->filters[i]; i ++)
4410 if (job->filters[i] > 0)
ef55b745
MS
4411 {
4412 cupsdEndProcess(job->filters[i], action >= CUPSD_JOB_FORCE);
4413
4414 if (action >= CUPSD_JOB_FORCE)
4415 job->filters[i] = -job->filters[i];
4416 }
b9faaae1
MS
4417
4418 if (job->backend > 0)
ef55b745
MS
4419 {
4420 cupsdEndProcess(job->backend, action >= CUPSD_JOB_FORCE);
4421
4422 if (action >= CUPSD_JOB_FORCE)
4423 job->backend = -job->backend;
4424 }
4425
4426 if (action >= CUPSD_JOB_FORCE)
4427 {
4428 /*
4429 * Clear job status...
4430 */
4431
4432 job->status = 0;
4433 }
e1d6a774 4434}
ef416fc2 4435
e1d6a774 4436
4437/*
4438 * 'unload_job()' - Unload a job from memory.
4439 */
4440
4441static void
4442unload_job(cupsd_job_t *job) /* I - Job */
4443{
4444 if (!job->attrs)
4445 return;
4446
ed6e7faf 4447 cupsdLogMessage(CUPSD_LOG_DEBUG, "[Job %d] Unloading...", job->id);
e1d6a774 4448
4449 ippDelete(job->attrs);
4450
cc0d019f
MS
4451 job->attrs = NULL;
4452 job->state = NULL;
12f89d24 4453 job->reasons = NULL;
cc0d019f
MS
4454 job->sheets = NULL;
4455 job->job_sheets = NULL;
4456 job->printer_message = NULL;
4457 job->printer_reasons = NULL;
ef416fc2 4458}
4459
4460
4461/*
f899b121 4462 * 'update_job()' - Read a status update from a job's filters.
4463 */
4464
4465void
09a101d6 4466update_job(cupsd_job_t *job) /* I - Job to check */
f899b121 4467{
4468 int i; /* Looping var */
4469 int copies; /* Number of copies printed */
178cb736
MS
4470 char message[CUPSD_SB_BUFFER_SIZE],
4471 /* Message text */
f899b121 4472 *ptr; /* Pointer update... */
4473 int loglevel, /* Log level for message */
4474 event = 0; /* Events? */
f0ab5bff
MS
4475 static const char * const levels[] = /* Log levels */
4476 {
4477 "NONE",
4478 "EMERG",
4479 "ALERT",
4480 "CRIT",
4481 "ERROR",
4482 "WARN",
4483 "NOTICE",
4484 "INFO",
4485 "DEBUG",
4486 "DEBUG2"
4487 };
f899b121 4488
4489
f0ab5bff
MS
4490 /*
4491 * Get the printer associated with this job; if the printer is stopped for
4492 * any reason then job->printer will be reset to NULL, so make sure we have
4493 * a valid pointer...
4494 */
4495
f899b121 4496 while ((ptr = cupsdStatBufUpdate(job->status_buffer, &loglevel,
4497 message, sizeof(message))) != NULL)
4498 {
4499 /*
4500 * Process page and printer state messages as needed...
4501 */
4502
4503 if (loglevel == CUPSD_LOG_PAGE)
4504 {
4505 /*
4506 * Page message; send the message to the page_log file and update the
4507 * job sheet count...
4508 */
4509
75bd9771 4510 cupsdLogJob(job, CUPSD_LOG_DEBUG, "PAGE: %s", message);
dd1abb6b 4511
91c84a35 4512 if (job->sheets)
f899b121 4513 {
88f9aafc 4514 if (!_cups_strncasecmp(message, "total ", 6))
f899b121 4515 {
4516 /*
4517 * Got a total count of pages from a backend or filter...
4518 */
4519
4520 copies = atoi(message + 6);
4521 copies -= job->sheets->values[0].integer; /* Just track the delta */
4522 }
4523 else if (!sscanf(message, "%*d%d", &copies))
4524 copies = 1;
4525
4526 job->sheets->values[0].integer += copies;
4527
b9faaae1 4528 if (job->printer->page_limit)
ba55dc12 4529 cupsdUpdateQuota(job->printer, job->username, copies, 0);
f899b121 4530 }
4531
4532 cupsdLogPage(job, message);
4533
91c84a35 4534 if (job->sheets)
b9faaae1 4535 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
91c84a35 4536 "Printed %d page(s).", job->sheets->values[0].integer);
f899b121 4537 }
4538 else if (loglevel == CUPSD_LOG_STATE)
4539 {
75bd9771 4540 cupsdLogJob(job, CUPSD_LOG_DEBUG, "STATE: %s", message);
dd1abb6b 4541
09a101d6 4542 if (!strcmp(message, "paused"))
c24d2134 4543 {
b9faaae1 4544 cupsdStopPrinter(job->printer, 1);
c24d2134
MS
4545 return;
4546 }
acb056cb 4547 else if (cupsdSetPrinterReasons(job->printer, message))
dcb445bc 4548 {
d9bca400 4549 event |= CUPSD_EVENT_PRINTER_STATE;
bc44d920 4550
dcb445bc
MS
4551 if (MaxJobTime > 0 && strstr(message, "connecting-to-device") != NULL)
4552 {
4553 /*
4554 * Reset cancel time after connecting to the device...
4555 */
4556
4557 for (i = 0; i < job->printer->num_reasons; i ++)
4558 if (!strcmp(job->printer->reasons[i], "connecting-to-device"))
4559 break;
4560
4561 if (i >= job->printer->num_reasons)
4562 job->cancel_time = time(NULL) + MaxJobTime;
4563 }
4564 }
4565
4509bb49 4566 update_job_attrs(job, 0);
f899b121 4567 }
4568 else if (loglevel == CUPSD_LOG_ATTR)
4569 {
4570 /*
4571 * Set attribute(s)...
4572 */
4573
4574 int num_attrs; /* Number of attributes */
4575 cups_option_t *attrs; /* Attributes */
4576 const char *attr; /* Attribute */
4577
4578
75bd9771 4579 cupsdLogJob(job, CUPSD_LOG_DEBUG, "ATTR: %s", message);
dd1abb6b 4580
f899b121 4581 num_attrs = cupsParseOptions(message, 0, &attrs);
4582
4583 if ((attr = cupsGetOption("auth-info-required", num_attrs,
4584 attrs)) != NULL)
7ff4fea9 4585 {
b9faaae1
MS
4586 cupsdSetAuthInfoRequired(job->printer, attr, NULL);
4587 cupsdSetPrinterAttrs(job->printer);
3dfe78b3 4588
a2326b5b 4589 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
7ff4fea9 4590 }
f899b121 4591
9a4f8274
MS
4592 if ((attr = cupsGetOption("job-media-progress", num_attrs,
4593 attrs)) != NULL)
4594 {
4595 int progress = atoi(attr);
4596
4597
4598 if (progress >= 0 && progress <= 100)
4599 {
4600 job->progress = progress;
4601
4602 if (job->sheets)
b9faaae1 4603 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
9a4f8274
MS
4604 "Printing page %d, %d%%",
4605 job->sheets->values[0].integer, job->progress);
4606 }
4607 }
4608
323c5de1 4609 if ((attr = cupsGetOption("printer-alert", num_attrs, attrs)) != NULL)
4610 {
b9faaae1 4611 cupsdSetString(&job->printer->alert, attr);
d9bca400 4612 event |= CUPSD_EVENT_PRINTER_STATE;
323c5de1 4613 }
4614
4615 if ((attr = cupsGetOption("printer-alert-description", num_attrs,
4616 attrs)) != NULL)
4617 {
b9faaae1 4618 cupsdSetString(&job->printer->alert_description, attr);
d9bca400 4619 event |= CUPSD_EVENT_PRINTER_STATE;
323c5de1 4620 }
4621
5a738aea
MS
4622 if ((attr = cupsGetOption("marker-colors", num_attrs, attrs)) != NULL)
4623 {
b9faaae1
MS
4624 cupsdSetPrinterAttr(job->printer, "marker-colors", (char *)attr);
4625 job->printer->marker_time = time(NULL);
5a738aea 4626 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4627 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5a738aea
MS
4628 }
4629
4630 if ((attr = cupsGetOption("marker-levels", num_attrs, attrs)) != NULL)
4631 {
b9faaae1
MS
4632 cupsdSetPrinterAttr(job->printer, "marker-levels", (char *)attr);
4633 job->printer->marker_time = time(NULL);
5a738aea 4634 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4635 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5a738aea
MS
4636 }
4637
ed6e7faf
MS
4638 if ((attr = cupsGetOption("marker-low-levels", num_attrs, attrs)) != NULL)
4639 {
b9faaae1
MS
4640 cupsdSetPrinterAttr(job->printer, "marker-low-levels", (char *)attr);
4641 job->printer->marker_time = time(NULL);
ed6e7faf
MS
4642 event |= CUPSD_EVENT_PRINTER_STATE;
4643 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4644 }
4645
4646 if ((attr = cupsGetOption("marker-high-levels", num_attrs, attrs)) != NULL)
4647 {
b9faaae1
MS
4648 cupsdSetPrinterAttr(job->printer, "marker-high-levels", (char *)attr);
4649 job->printer->marker_time = time(NULL);
ed6e7faf
MS
4650 event |= CUPSD_EVENT_PRINTER_STATE;
4651 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
4652 }
4653
75bd9771
MS
4654 if ((attr = cupsGetOption("marker-message", num_attrs, attrs)) != NULL)
4655 {
b9faaae1
MS
4656 cupsdSetPrinterAttr(job->printer, "marker-message", (char *)attr);
4657 job->printer->marker_time = time(NULL);
75bd9771 4658 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4659 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
75bd9771
MS
4660 }
4661
5a738aea
MS
4662 if ((attr = cupsGetOption("marker-names", num_attrs, attrs)) != NULL)
4663 {
b9faaae1
MS
4664 cupsdSetPrinterAttr(job->printer, "marker-names", (char *)attr);
4665 job->printer->marker_time = time(NULL);
5a738aea 4666 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4667 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5a738aea
MS
4668 }
4669
4670 if ((attr = cupsGetOption("marker-types", num_attrs, attrs)) != NULL)
4671 {
b9faaae1
MS
4672 cupsdSetPrinterAttr(job->printer, "marker-types", (char *)attr);
4673 job->printer->marker_time = time(NULL);
5a738aea 4674 event |= CUPSD_EVENT_PRINTER_STATE;
52f6f666 4675 cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
5a738aea
MS
4676 }
4677
f899b121 4678 cupsFreeOptions(num_attrs, attrs);
4679 }
c9fc04c6
MS
4680 else if (loglevel == CUPSD_LOG_PPD)
4681 {
4682 /*
4683 * Set attribute(s)...
4684 */
4685
4686 int num_keywords; /* Number of keywords */
4687 cups_option_t *keywords; /* Keywords */
4688
4689
75bd9771 4690 cupsdLogJob(job, CUPSD_LOG_DEBUG, "PPD: %s", message);
dd1abb6b 4691
c9fc04c6
MS
4692 num_keywords = cupsParseOptions(message, 0, &keywords);
4693
b9faaae1
MS
4694 if (cupsdUpdatePrinterPPD(job->printer, num_keywords, keywords))
4695 cupsdSetPrinterAttrs(job->printer);
c9fc04c6
MS
4696
4697 cupsFreeOptions(num_keywords, keywords);
4698 }
4d301e69 4699 else
f899b121 4700 {
4d301e69
MS
4701 /*
4702 * Strip legacy message prefix...
4703 */
f899b121 4704
4d301e69 4705 if (!strncmp(message, "recoverable:", 12))
d2354e63 4706 {
4d301e69
MS
4707 ptr = message + 12;
4708 while (isspace(*ptr & 255))
4709 ptr ++;
d2354e63 4710 }
4d301e69 4711 else if (!strncmp(message, "recovered:", 10))
acb056cb 4712 {
4d301e69
MS
4713 ptr = message + 10;
4714 while (isspace(*ptr & 255))
4715 ptr ++;
acb056cb 4716 }
4d301e69
MS
4717 else
4718 ptr = message;
4719
dcb445bc
MS
4720 if (*ptr)
4721 cupsdLogJob(job, loglevel, "%s", ptr);
f899b121 4722
5180a04c
MS
4723 if (loglevel < CUPSD_LOG_DEBUG &&
4724 strcmp(job->printer->state_message, ptr))
1f0275e3 4725 {
4d301e69 4726 strlcpy(job->printer->state_message, ptr,
b9faaae1 4727 sizeof(job->printer->state_message));
4509bb49 4728
8b116e60 4729 event |= CUPSD_EVENT_PRINTER_STATE | CUPSD_EVENT_JOB_PROGRESS;
4509bb49 4730
229681c1 4731 if (loglevel <= job->status_level && job->status_level > CUPSD_LOG_ERROR)
1f0275e3
MS
4732 {
4733 /*
b9faaae1 4734 * Some messages show in the job-printer-state-message attribute...
1f0275e3 4735 */
09a101d6 4736
1f0275e3
MS
4737 if (loglevel != CUPSD_LOG_NOTICE)
4738 job->status_level = loglevel;
01ce6322 4739
1f0275e3 4740 update_job_attrs(job, 1);
f0ab5bff
MS
4741
4742 cupsdLogJob(job, CUPSD_LOG_DEBUG,
4743 "Set job-printer-state-message to \"%s\", "
4744 "current level=%s",
4745 job->printer_message->values[0].string.text,
4746 levels[job->status_level]);
1f0275e3 4747 }
75bd9771 4748 }
f899b121 4749 }
4750
4751 if (!strchr(job->status_buffer->buffer, '\n'))
4752 break;
4753 }
4754
f8b3a85b
MS
4755 if (event & CUPSD_EVENT_JOB_PROGRESS)
4756 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS, job->printer, job,
4757 "%s", job->printer->state_message);
5180a04c 4758 if (event & CUPSD_EVENT_PRINTER_STATE)
b9faaae1
MS
4759 cupsdAddEvent(CUPSD_EVENT_PRINTER_STATE, job->printer, NULL,
4760 (job->printer->type & CUPS_PRINTER_CLASS) ?
f899b121 4761 "Class \"%s\" state changed." :
4762 "Printer \"%s\" state changed.",
b9faaae1 4763 job->printer->name);
f899b121 4764
8b116e60 4765
f899b121 4766 if (ptr == NULL && !job->status_buffer->bufused)
4767 {
4768 /*
4769 * See if all of the filters and the backend have returned their
4770 * exit statuses.
4771 */
4772
4773 for (i = 0; job->filters[i] < 0; i ++);
4774
4775 if (job->filters[i])
d7871c8c
MS
4776 {
4777 /*
4778 * EOF but we haven't collected the exit status of all filters...
4779 */
4780
4781 cupsdCheckProcess();
f899b121 4782 return;
d7871c8c 4783 }
f899b121 4784
4785 if (job->current_file >= job->num_files && job->backend > 0)
d7871c8c
MS
4786 {
4787 /*
4788 * EOF but we haven't collected the exit status of the backend...
4789 */
4790
4791 cupsdCheckProcess();
f899b121 4792 return;
d7871c8c 4793 }
f899b121 4794
4795 /*
4796 * Handle the end of job stuff...
4797 */
4798
ef55b745 4799 finalize_job(job, 1);
b9faaae1
MS
4800
4801 /*
4802 * Check for new jobs...
4803 */
4804
4805 cupsdCheckJobs();
f899b121 4806 }
4807}
4808
4809
4810/*
bc44d920 4811 * 'update_job_attrs()' - Update the job-printer-* attributes.
4812 */
4813
4814void
4509bb49 4815update_job_attrs(cupsd_job_t *job, /* I - Job to update */
b9faaae1 4816 int do_message)/* I - 1 = copy job-printer-state message */
bc44d920 4817{
4818 int i; /* Looping var */
4819 int num_reasons; /* Actual number of reasons */
4820 const char * const *reasons; /* Reasons */
f0ab5bff 4821 static const char *none = "none"; /* "none" reason */
bc44d920 4822
4823
4824 /*
4825 * Get/create the job-printer-state-* attributes...
4826 */
4827
4828 if (!job->printer_message)
4829 {
4830 if ((job->printer_message = ippFindAttribute(job->attrs,
4831 "job-printer-state-message",
4832 IPP_TAG_TEXT)) == NULL)
4833 job->printer_message = ippAddString(job->attrs, IPP_TAG_JOB, IPP_TAG_TEXT,
4834 "job-printer-state-message",
4835 NULL, "");
4836 }
4837
4838 if (!job->printer_reasons)
4839 job->printer_reasons = ippFindAttribute(job->attrs,
4840 "job-printer-state-reasons",
4841 IPP_TAG_KEYWORD);
4842
bc44d920 4843 /*
b9faaae1 4844 * Copy or clear the printer-state-message value as needed...
bc44d920 4845 */
4846
b9faaae1
MS
4847 if (job->state_value != IPP_JOB_PROCESSING &&
4848 job->status_level == CUPSD_LOG_INFO)
dcb445bc 4849 {
b9faaae1 4850 cupsdSetString(&(job->printer_message->values[0].string.text), "");
dcb445bc
MS
4851
4852 job->dirty = 1;
4853 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
4854 }
b9faaae1 4855 else if (job->printer->state_message[0] && do_message)
dcb445bc 4856 {
bc44d920 4857 cupsdSetString(&(job->printer_message->values[0].string.text),
b9faaae1 4858 job->printer->state_message);
ef55b745 4859
dcb445bc
MS
4860 job->dirty = 1;
4861 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
4862 }
4863
bc44d920 4864 /*
4865 * ... and the printer-state-reasons value...
4866 */
4867
b9faaae1 4868 if (job->printer->num_reasons == 0)
bc44d920 4869 {
4870 num_reasons = 1;
f0ab5bff 4871 reasons = &none;
bc44d920 4872 }
4873 else
4874 {
b9faaae1
MS
4875 num_reasons = job->printer->num_reasons;
4876 reasons = (const char * const *)job->printer->reasons;
bc44d920 4877 }
4878
4879 if (!job->printer_reasons || job->printer_reasons->num_values != num_reasons)
4880 {
b9faaae1
MS
4881 /*
4882 * Replace/create a job-printer-state-reasons attribute...
4883 */
4884
bc44d920 4885 ippDeleteAttribute(job->attrs, job->printer_reasons);
4886
4887 job->printer_reasons = ippAddStrings(job->attrs,
4888 IPP_TAG_JOB, IPP_TAG_KEYWORD,
4889 "job-printer-state-reasons",
4890 num_reasons, NULL, NULL);
4891 }
b9faaae1 4892 else
d2354e63 4893 {
b9faaae1
MS
4894 /*
4895 * Don't bother clearing the reason strings if they are the same...
4896 */
4897
4898 for (i = 0; i < num_reasons; i ++)
4899 if (strcmp(job->printer_reasons->values[i].string.text, reasons[i]))
4900 break;
4901
4902 if (i >= num_reasons)
4903 return;
4904
4905 /*
4906 * Not the same, so free the current strings...
4907 */
4908
4909 for (i = 0; i < num_reasons; i ++)
d2354e63
MS
4910 _cupsStrFree(job->printer_reasons->values[i].string.text);
4911 }
bc44d920 4912
b9faaae1
MS
4913 /*
4914 * Copy the reasons...
4915 */
4916
bc44d920 4917 for (i = 0; i < num_reasons; i ++)
d2354e63 4918 job->printer_reasons->values[i].string.text = _cupsStrAlloc(reasons[i]);
dcb445bc
MS
4919
4920 job->dirty = 1;
4921 cupsdMarkDirty(CUPSD_DIRTY_JOBS);
bc44d920 4922}
4923
4924
4925/*
b19ccc9e 4926 * End of "$Id: job.c 7902 2008-09-03 14:20:17Z mike $".
ef416fc2 4927 */