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