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