4 * Job management routines for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * cupsdAddJob() - Add a new job to the job queue...
27 * cupsdCancelJob() - Cancel the specified print job.
28 * cupsdCancelJobs() - Cancel all jobs for the given destination/user...
29 * cupsdCheckJobs() - Check the pending jobs and start any if the
30 * destination is available.
31 * cupsdCleanJobs() - Clean out old jobs.
32 * cupsdFreeAllJobs() - Free all jobs from memory.
33 * cupsdFindJob() - Find the specified job.
34 * cupsdGetPrinterJobCount() - Get the number of pending, processing,
35 * or held jobs in a printer or class.
36 * cupsdGetUserJobCount() - Get the number of pending, processing,
37 * or held jobs for a user.
38 * cupsdHoldJob() - Hold the specified job.
39 * cupsdLoadAllJobs() - Load all jobs from disk.
40 * cupsdMoveJob() - Move the specified job to a different
42 * cupsdReleaseJob() - Release the specified job.
43 * cupsdRestartJob() - Restart the specified job.
44 * cupsdSaveJob() - Save a job to disk.
45 * cupsdSetJobHoldUntil() - Set the hold time for a job...
46 * cupsdSetJobPriority() - Set the priority of a job, moving it up/down
47 * in the list as needed.
48 * cupsdStartJob() - Start a print job.
49 * cupsdStopAllJobs() - Stop all print jobs.
50 * cupsdStopJob() - Stop a print job.
51 * cupsdUpdateJob() - Read a status update from a job's filters.
52 * compare_active_jobs() - Compare the job IDs and priorities of two jobs.
53 * compare_jobs() - Compare the job IDs of two jobs.
54 * ipp_length() - Compute the size of the buffer needed to hold
55 * the textual IPP attributes.
56 * set_hold_until() - Set the hold time and update job-hold-until attribute.
60 * Include necessary headers...
65 #include <cups/backend.h>
73 static mime_filter_t gziptoany_filter
=
75 NULL
, /* Source type */
76 NULL
, /* Destination type */
78 "gziptoany" /* Filter program to run */
86 static int compare_active_jobs(void *first
, void *second
, void *data
);
87 static int compare_jobs(void *first
, void *second
, void *data
);
88 static int ipp_length(ipp_t
*ipp
);
89 static void set_time(cupsd_job_t
*job
, const char *name
);
90 static void set_hold_until(cupsd_job_t
*job
, time_t holdtime
);
94 * 'cupsdAddJob()' - Add a new job to the job queue...
97 cupsd_job_t
* /* O - New job record */
98 cupsdAddJob(int priority
, /* I - Job priority */
99 const char *dest
) /* I - Job destination */
101 cupsd_job_t
*job
; /* New job record */
104 job
= calloc(sizeof(cupsd_job_t
), 1);
106 job
->id
= NextJobId
++;
107 job
->priority
= priority
;
108 job
->back_pipes
[0] = -1;
109 job
->back_pipes
[1] = -1;
110 job
->print_pipes
[0] = -1;
111 job
->print_pipes
[1] = -1;
113 cupsdSetString(&job
->dest
, dest
);
116 * Add the new job to the "all jobs" and "active jobs" lists...
119 cupsArrayAdd(Jobs
, job
);
120 cupsArrayAdd(ActiveJobs
, job
);
127 * 'cupsdCancelJob()' - Cancel the specified print job.
131 cupsdCancelJob(cupsd_job_t
*job
, /* I - Job to cancel */
132 int purge
) /* I - Purge jobs? */
134 int i
; /* Looping var */
135 char filename
[1024]; /* Job filename */
138 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdCancelJob: id = %d", job
->id
);
141 * Remove the job from the active list...
144 cupsArrayRemove(ActiveJobs
, job
);
147 * Stop any processes that are working on the current job...
150 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
151 cupsdStopJob(job
, 0);
153 cupsArrayRemove(ActiveJobs
, job
);
155 job
->state
->values
[0].integer
= IPP_JOB_CANCELLED
;
157 set_time(job
, "time-at-completed");
159 cupsdExpireSubscriptions(NULL
, job
);
162 * Remove any authentication data...
165 snprintf(filename
, sizeof(filename
), "%s/a%05d", RequestRoot
,
170 * Remove the print file for good if we aren't preserving jobs or
174 job
->current_file
= 0;
176 if (!JobHistory
|| !JobFiles
|| purge
||
177 (job
->dtype
& CUPS_PRINTER_REMOTE
))
178 for (i
= 1; i
<= job
->num_files
; i
++)
180 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
185 if (JobHistory
&& !purge
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
))
188 * Save job state info...
196 * Remove the job info file...
199 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
,
204 * Remove the job from the "all jobs" list...
207 cupsArrayRemove(Jobs
, job
);
210 * Free all memory used...
213 if (job
->attrs
!= NULL
)
214 ippDelete(job
->attrs
);
216 if (job
->num_files
> 0)
218 free(job
->compressions
);
219 free(job
->filetypes
);
222 cupsdClearString(&job
->username
);
223 cupsdClearString(&job
->dest
);
231 * 'cupsdCancelJobs()' - Cancel all jobs for the given destination/user...
235 cupsdCancelJobs(const char *dest
, /* I - Destination to cancel */
236 const char *username
, /* I - Username or NULL */
237 int purge
) /* I - Purge jobs? */
239 cupsd_job_t
*job
; /* Current job */
242 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
244 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
245 if ((dest
== NULL
|| !strcmp(job
->dest
, dest
)) &&
246 (username
== NULL
|| !strcmp(job
->username
, username
)))
249 * Cancel all jobs matching this destination/user...
252 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
253 purge
? "Job purged." : "Job canceled.");
255 cupsdCancelJob(job
, purge
);
263 * 'cupsdCheckJobs()' - Check the pending jobs and start any if the destination
270 cupsd_job_t
*job
; /* Current job in queue */
271 cupsd_printer_t
*printer
, /* Printer destination */
272 *pclass
; /* Printer class destination */
275 DEBUG_puts("cupsdCheckJobs()");
277 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
279 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
282 * Start held jobs if they are ready...
285 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
&&
287 job
->hold_until
< time(NULL
))
288 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
291 * Start pending jobs if the destination is available...
294 if (job
->state
->values
[0].integer
== IPP_JOB_PENDING
&& !NeedReload
&&
297 printer
= cupsdFindDest(job
->dest
);
301 (printer
->type
& (CUPS_PRINTER_IMPLICIT
| CUPS_PRINTER_CLASS
)))
304 * If the class is remote, just pass it to the remote server...
309 if (!(pclass
->type
& CUPS_PRINTER_REMOTE
))
311 if (pclass
->state
!= IPP_PRINTER_STOPPED
)
312 printer
= cupsdFindAvailablePrinter(job
->dest
);
318 if (!printer
&& !pclass
)
321 * Whoa, the printer and/or class for this destination went away;
325 cupsdLogMessage(CUPSD_LOG_WARN
,
326 "Printer/class %s has gone away; cancelling job %d!",
329 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
330 "Job canceled because the destination printer/class has gone away.");
332 cupsdCancelJob(job
, 1);
337 * See if the printer is available or remote and not printing a job;
338 * if so, start the job...
344 * Add/update a job-actual-printer-uri attribute for this job
345 * so that we know which printer actually printed the job...
348 ipp_attribute_t
*attr
; /* job-actual-printer-uri attribute */
351 if ((attr
= ippFindAttribute(job
->attrs
, "job-actual-printer-uri",
352 IPP_TAG_URI
)) != NULL
)
353 cupsdSetString(&attr
->values
[0].string
.text
, printer
->uri
);
355 ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_URI
,
356 "job-actual-printer-uri", NULL
, printer
->uri
);
359 if (printer
->state
== IPP_PRINTER_IDLE
|| /* Printer is idle */
360 ((printer
->type
& CUPS_PRINTER_REMOTE
) && /* Printer is remote */
361 !printer
->job
)) /* and not printing a job */
362 cupsdStartJob(job
, printer
);
370 * 'cupsdCleanJobs()' - Clean out old jobs.
376 cupsd_job_t
*job
; /* Current job */
382 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
383 job
&& cupsArrayCount(Jobs
) >= MaxJobs
;
384 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
385 if (job
->state
->values
[0].integer
>= IPP_JOB_CANCELLED
)
386 cupsdCancelJob(job
, 1);
391 * 'cupsdFinishJob()' - Finish a job.
395 cupsdFinishJob(cupsd_job_t
*job
) /* I - Job */
397 int job_history
; /* Did cupsdCancelJob() keep the job? */
398 cupsd_printer_t
*printer
; /* Current printer */
401 cupsdLogMessage(CUPSD_LOG_DEBUG
,
402 "cupsdFinishJob: job %d, file %d is complete.",
403 job
->id
, job
->current_file
- 1);
405 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdFinishJob: job->status is %d",
408 if (job
->status_buffer
&& job
->current_file
>= job
->num_files
)
411 * Close the pipe and clear the input bit.
414 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
415 "cupsdFinishJob: Removing fd %d from InputSet...",
416 job
->status_buffer
->fd
);
418 FD_CLR(job
->status_buffer
->fd
, InputSet
);
420 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
421 "cupsdFinishJob: Closing status input pipe %d...",
422 job
->status_buffer
->fd
);
424 cupsdStatBufDelete(job
->status_buffer
);
426 job
->status_buffer
= NULL
;
429 printer
= job
->printer
;
434 * Backend had errors; stop it...
437 switch (-job
->status
)
440 case CUPS_BACKEND_FAILED
:
442 * Backend failure, use the error-policy to determine how to
446 cupsdStopJob(job
, 0);
447 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
451 * If the job was queued to a class, try requeuing it... For
452 * faxes and retry-job queues, hold the current job for 5 minutes.
455 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
457 else if ((printer
->type
& CUPS_PRINTER_FAX
) ||
458 !strcmp(printer
->error_policy
, "retry-job"))
461 * See how many times we've tried to send the job; if more than
462 * the limit, cancel the job.
467 if (job
->tries
>= JobRetryLimit
)
473 cupsdLogMessage(CUPSD_LOG_ERROR
,
474 "Canceling job %d since it could not be sent after %d tries.",
475 job
->id
, JobRetryLimit
);
477 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
478 "Job canceled since it could not be sent after %d tries.",
481 cupsdCancelJob(job
, 0);
486 * Try again in N seconds...
489 set_hold_until(job
, time(NULL
) + JobRetryInterval
);
492 else if (!strcmp(printer
->error_policy
, "abort-job"))
493 cupsdCancelJob(job
, 0);
496 case CUPS_BACKEND_CANCEL
:
501 cupsdCancelJob(job
, 0);
504 case CUPS_BACKEND_HOLD
:
509 cupsdStopJob(job
, 0);
510 cupsdSetJobHoldUntil(job
, "indefinite");
514 case CUPS_BACKEND_STOP
:
516 * Stop the printer...
519 cupsdStopJob(job
, 0);
521 cupsdSetPrinterState(printer
, IPP_PRINTER_STOPPED
, 1);
524 case CUPS_BACKEND_AUTH_REQUIRED
:
525 cupsdStopJob(job
, 0);
526 cupsdSetJobHoldUntil(job
, "authenticated");
529 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, printer
, job
,
530 "Authentication is required for job %d.", job
->id
);
535 * Try printing another job...
540 else if (job
->status
> 0)
543 * Filter had errors; stop job...
546 cupsdStopJob(job
, 1);
548 cupsdAddEvent(CUPSD_EVENT_JOB_STOPPED
, job
->printer
, job
,
549 "Job stopped due to filter errors; please consult the "
550 "error_log file for details.");
556 * Job printed successfully; cancel it...
559 if (job
->current_file
< job
->num_files
)
562 * Start the next file in the job...
565 FilterLevel
-= job
->cost
;
566 cupsdStartJob(job
, job
->printer
);
571 * Close out this job...
574 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
575 "Job completed successfully.");
577 job_history
= JobHistory
&& !(job
->dtype
& CUPS_PRINTER_REMOTE
);
579 cupsdCancelJob(job
, 0);
583 job
->state
->values
[0].integer
= IPP_JOB_COMPLETED
;
588 * Clear the printer's state_message and move on...
591 printer
->state_message
[0] = '\0';
600 * 'cupsdFreeAllJobs()' - Free all jobs from memory.
604 cupsdFreeAllJobs(void)
606 cupsd_job_t
*job
; /* Current job */
613 for (job
= (cupsd_job_t
*)cupsArrayFirst(Jobs
);
615 job
= (cupsd_job_t
*)cupsArrayNext(Jobs
))
617 cupsArrayRemove(Jobs
, job
);
618 cupsArrayRemove(ActiveJobs
, job
);
620 ippDelete(job
->attrs
);
622 if (job
->num_files
> 0)
624 free(job
->compressions
);
625 free(job
->filetypes
);
631 cupsdReleaseSignals();
636 * 'cupsdFindJob()' - Find the specified job.
639 cupsd_job_t
* /* O - Job data */
640 cupsdFindJob(int id
) /* I - Job ID */
642 cupsd_job_t key
; /* Search key */
647 return ((cupsd_job_t
*)cupsArrayFind(Jobs
, &key
));
652 * 'cupsdGetPrinterJobCount()' - Get the number of pending, processing,
653 * or held jobs in a printer or class.
656 int /* O - Job count */
657 cupsdGetPrinterJobCount(
658 const char *dest
) /* I - Printer or class name */
660 int count
; /* Job count */
661 cupsd_job_t
*job
; /* Current job */
664 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
666 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
667 if (!strcasecmp(job
->dest
, dest
))
675 * 'cupsdGetUserJobCount()' - Get the number of pending, processing,
676 * or held jobs for a user.
679 int /* O - Job count */
680 cupsdGetUserJobCount(
681 const char *username
) /* I - Username */
683 int count
; /* Job count */
684 cupsd_job_t
*job
; /* Current job */
687 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
), count
= 0;
689 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
690 if (!strcasecmp(job
->username
, username
))
698 * 'cupsdHoldJob()' - Hold the specified job.
702 cupsdHoldJob(cupsd_job_t
*job
) /* I - Job data */
704 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdHoldJob: id = %d", job
->id
);
706 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
707 cupsdStopJob(job
, 0);
709 DEBUG_puts("cupsdHoldJob: setting state to held...");
711 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
720 * 'cupsdLoadAllJobs()' - Load all jobs from disk.
724 cupsdLoadAllJobs(void)
726 cups_dir_t
*dir
; /* Directory */
727 cups_dentry_t
*dent
; /* Directory entry */
728 char filename
[1024]; /* Full filename of job file */
729 cups_file_t
*fp
; /* Job file */
730 cupsd_job_t
*job
; /* New job */
731 int jobid
, /* Current job ID */
732 fileid
; /* Current file ID */
733 ipp_attribute_t
*attr
; /* Job attribute */
734 char method
[HTTP_MAX_URI
],
735 /* Method portion of URI */
736 username
[HTTP_MAX_URI
],
737 /* Username portion of URI */
739 /* Host portion of URI */
740 resource
[HTTP_MAX_URI
];
741 /* Resource portion of URI */
742 int port
; /* Port portion of URI */
743 const char *dest
; /* Destination */
744 mime_type_t
**filetypes
; /* New filetypes array */
745 int *compressions
; /* New compressions array */
749 * First create the job lists...
753 Jobs
= cupsArrayNew(compare_jobs
, NULL
);
756 ActiveJobs
= cupsArrayNew(compare_active_jobs
, NULL
);
759 * Then open the requests directory...
762 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdLoadAllJobs: Scanning %s...",
765 if ((dir
= cupsDirOpen(RequestRoot
)) == NULL
)
767 cupsdLogMessage(CUPSD_LOG_ERROR
,
768 "cupsdLoadAllJobs: Unable to open spool directory %s: %s",
769 RequestRoot
, strerror(errno
));
774 * Read all the c##### files...
777 while ((dent
= cupsDirRead(dir
)) != NULL
)
778 if (strlen(dent
->filename
) >= 6 && dent
->filename
[0] == 'c')
781 * Allocate memory for the job...
784 if ((job
= calloc(sizeof(cupsd_job_t
), 1)) == NULL
)
786 cupsdLogMessage(CUPSD_LOG_ERROR
,
787 "cupsdLoadAllJobs: Ran out of memory for jobs!");
792 if ((job
->attrs
= ippNew()) == NULL
)
795 cupsdLogMessage(CUPSD_LOG_ERROR
,
796 "cupsdLoadAllJobs: Ran out of memory for job attributes!");
802 * Assign the job ID...
805 job
->id
= atoi(dent
->filename
+ 1);
806 job
->back_pipes
[0] = -1;
807 job
->back_pipes
[1] = -1;
808 job
->print_pipes
[0] = -1;
809 job
->print_pipes
[1] = -1;
811 cupsdLogMessage(CUPSD_LOG_DEBUG
,
812 "cupsdLoadAllJobs: Loading attributes for job %d...",
815 if (job
->id
>= NextJobId
)
816 NextJobId
= job
->id
+ 1;
819 * Load the job control file...
822 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->filename
);
823 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
825 cupsdLogMessage(CUPSD_LOG_ERROR
,
826 "cupsdLoadAllJobs: Unable to open job control file "
828 filename
, strerror(errno
));
829 ippDelete(job
->attrs
);
836 if (ippReadIO(fp
, (ipp_iocb_t
)cupsFileRead
, 1, NULL
,
837 job
->attrs
) != IPP_DATA
)
839 cupsdLogMessage(CUPSD_LOG_ERROR
,
840 "cupsdLoadAllJobs: Unable to read job control file "
844 ippDelete(job
->attrs
);
853 if ((job
->state
= ippFindAttribute(job
->attrs
, "job-state", IPP_TAG_ENUM
)) == NULL
)
855 cupsdLogMessage(CUPSD_LOG_ERROR
,
856 "cupsdLoadAllJobs: Missing or bad job-state attribute "
857 "in control file \"%s\"!",
859 ippDelete(job
->attrs
);
865 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) == NULL
)
867 cupsdLogMessage(CUPSD_LOG_ERROR
,
868 "cupsdLoadAllJobs: No job-printer-uri attribute in "
869 "control file \"%s\"!",
871 ippDelete(job
->attrs
);
877 httpSeparateURI(HTTP_URI_CODING_ALL
, attr
->values
[0].string
.text
, method
,
878 sizeof(method
), username
, sizeof(username
), host
,
879 sizeof(host
), &port
, resource
, sizeof(resource
));
881 if ((dest
= cupsdValidateDest(host
, resource
, &(job
->dtype
),
884 cupsdLogMessage(CUPSD_LOG_ERROR
,
885 "cupsdLoadAllJobs: Unable to queue job for destination "
887 attr
->values
[0].string
.text
);
888 ippDelete(job
->attrs
);
894 cupsdSetString(&job
->dest
, dest
);
896 job
->sheets
= ippFindAttribute(job
->attrs
, "job-media-sheets-completed",
898 job
->job_sheets
= ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_NAME
);
900 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority",
901 IPP_TAG_INTEGER
)) == NULL
)
903 cupsdLogMessage(CUPSD_LOG_ERROR
,
904 "cupsdLoadAllJobs: Missing or bad job-priority "
905 "attribute in control file \"%s\"!",
907 ippDelete(job
->attrs
);
912 job
->priority
= attr
->values
[0].integer
;
914 if ((attr
= ippFindAttribute(job
->attrs
, "job-originating-user-name",
915 IPP_TAG_NAME
)) == NULL
)
917 cupsdLogMessage(CUPSD_LOG_ERROR
,
918 "cupsdLoadAllJobs: Missing or bad "
919 "job-originating-user-name attribute in control file "
922 ippDelete(job
->attrs
);
927 cupsdSetString(&job
->username
, attr
->values
[0].string
.text
);
930 * Insert the job into the array, sorting by job priority and ID...
933 cupsArrayAdd(Jobs
, job
);
934 if (job
->state
->values
[0].integer
< IPP_JOB_STOPPED
)
935 cupsArrayAdd(ActiveJobs
,job
);
938 * Set the job hold-until time and state...
941 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
943 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until",
944 IPP_TAG_KEYWORD
)) == NULL
)
945 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
948 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
950 cupsdSetJobHoldUntil(job
, attr
->values
[0].string
.text
);
952 else if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
953 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
957 * Read all the d##### files...
962 while ((dent
= cupsDirRead(dir
)) != NULL
)
963 if (strlen(dent
->filename
) > 7 && dent
->filename
[0] == 'd' &&
964 strchr(dent
->filename
, '-'))
970 jobid
= atoi(dent
->filename
+ 1);
971 fileid
= atoi(strchr(dent
->filename
, '-') + 1);
973 cupsdLogMessage(CUPSD_LOG_DEBUG
,
974 "cupsdLoadAllJobs: Auto-typing document file %s...",
977 snprintf(filename
, sizeof(filename
), "%s/%s", RequestRoot
, dent
->filename
);
979 if ((job
= cupsdFindJob(jobid
)) == NULL
)
981 cupsdLogMessage(CUPSD_LOG_ERROR
,
982 "cupsdLoadAllJobs: Orphaned print file \"%s\"!",
988 if (fileid
> job
->num_files
)
990 if (job
->num_files
== 0)
992 compressions
= (int *)calloc(fileid
, sizeof(int));
993 filetypes
= (mime_type_t
**)calloc(fileid
, sizeof(mime_type_t
*));
997 compressions
= (int *)realloc(job
->compressions
,
998 sizeof(int) * fileid
);
999 filetypes
= (mime_type_t
**)realloc(job
->filetypes
,
1000 sizeof(mime_type_t
*) * fileid
);
1003 if (compressions
== NULL
|| filetypes
== NULL
)
1005 cupsdLogMessage(CUPSD_LOG_ERROR
,
1006 "cupsdLoadAllJobs: Ran out of memory for job file "
1011 job
->compressions
= compressions
;
1012 job
->filetypes
= filetypes
;
1013 job
->num_files
= fileid
;
1016 job
->filetypes
[fileid
- 1] = mimeFileType(MimeDatabase
, filename
,
1017 job
->compressions
+ fileid
- 1);
1019 if (job
->filetypes
[fileid
- 1] == NULL
)
1020 job
->filetypes
[fileid
- 1] = mimeType(MimeDatabase
, "application",
1027 * Clean out old jobs as needed...
1035 * 'cupsdMoveJob()' - Move the specified job to a different destination.
1039 cupsdMoveJob(cupsd_job_t
*job
, /* I - Job */
1040 const char *dest
) /* I - Destination */
1042 ipp_attribute_t
*attr
; /* job-printer-uri attribute */
1043 cupsd_printer_t
*p
; /* Destination printer or class */
1047 * Find the printer...
1050 if ((p
= cupsdFindDest(dest
)) == NULL
)
1054 * Don't move completed jobs...
1057 if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
1061 * Change the destination information...
1064 cupsdSetString(&job
->dest
, dest
);
1065 job
->dtype
= p
->type
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_REMOTE
|
1066 CUPS_PRINTER_IMPLICIT
);
1068 if ((attr
= ippFindAttribute(job
->attrs
, "job-printer-uri", IPP_TAG_URI
)) != NULL
)
1069 cupsdSetString(&(attr
->values
[0].string
.text
), p
->uri
);
1076 * 'cupsdReleaseJob()' - Release the specified job.
1080 cupsdReleaseJob(cupsd_job_t
*job
) /* I - Job */
1082 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdReleaseJob: id = %d", job
->id
);
1084 if (job
->state
->values
[0].integer
== IPP_JOB_HELD
)
1086 DEBUG_puts("cupsdReleaseJob: setting state to pending...");
1088 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1096 * 'cupsdRestartJob()' - Restart the specified job.
1100 cupsdRestartJob(cupsd_job_t
*job
) /* I - Job */
1102 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdRestartJob: id = %d", job
->id
);
1104 if (job
->state
->values
[0].integer
== IPP_JOB_STOPPED
|| JobFiles
)
1107 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
1115 * 'cupsdSaveJob()' - Save a job to disk.
1119 cupsdSaveJob(cupsd_job_t
*job
) /* I - Job */
1121 char filename
[1024]; /* Job control filename */
1122 cups_file_t
*fp
; /* Job file */
1125 snprintf(filename
, sizeof(filename
), "%s/c%05d", RequestRoot
, job
->id
);
1127 if ((fp
= cupsFileOpen(filename
, "w")) == NULL
)
1129 cupsdLogMessage(CUPSD_LOG_ERROR
,
1130 "cupsdSaveJob: Unable to create job control file "
1132 filename
, strerror(errno
));
1136 fchmod(cupsFileNumber(fp
), 0600);
1137 fchown(cupsFileNumber(fp
), RunUser
, Group
);
1139 ippWriteIO(fp
, (ipp_iocb_t
)cupsFileWrite
, 1, NULL
, job
->attrs
);
1146 * 'cupsdSetJobHoldUntil()' - Set the hold time for a job...
1150 cupsdSetJobHoldUntil(cupsd_job_t
*job
, /* I - Job */
1151 const char *when
) /* I - When to resume */
1153 time_t curtime
; /* Current time */
1154 struct tm
*curdate
; /* Current date */
1155 int hour
; /* Hold hour */
1156 int minute
; /* Hold minute */
1157 int second
; /* Hold second */
1160 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdSetJobHoldUntil(%d, \"%s\")",
1165 if (!strcmp(when
, "indefinite") || !strcmp(when
, "authenticated"))
1168 * Hold indefinitely...
1171 job
->hold_until
= 0;
1173 else if (!strcmp(when
, "day-time"))
1176 * Hold to 6am the next morning unless local time is < 6pm.
1179 curtime
= time(NULL
);
1180 curdate
= localtime(&curtime
);
1182 if (curdate
->tm_hour
< 18)
1183 job
->hold_until
= curtime
;
1185 job
->hold_until
= curtime
+
1186 ((29 - curdate
->tm_hour
) * 60 + 59 -
1187 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1189 else if (!strcmp(when
, "evening") || strcmp(when
, "night"))
1192 * Hold to 6pm unless local time is > 6pm or < 6am.
1195 curtime
= time(NULL
);
1196 curdate
= localtime(&curtime
);
1198 if (curdate
->tm_hour
< 6 || curdate
->tm_hour
>= 18)
1199 job
->hold_until
= curtime
;
1201 job
->hold_until
= curtime
+
1202 ((17 - curdate
->tm_hour
) * 60 + 59 -
1203 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1205 else if (!strcmp(when
, "second-shift"))
1208 * Hold to 4pm unless local time is > 4pm.
1211 curtime
= time(NULL
);
1212 curdate
= localtime(&curtime
);
1214 if (curdate
->tm_hour
>= 16)
1215 job
->hold_until
= curtime
;
1217 job
->hold_until
= curtime
+
1218 ((15 - curdate
->tm_hour
) * 60 + 59 -
1219 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1221 else if (!strcmp(when
, "third-shift"))
1224 * Hold to 12am unless local time is < 8am.
1227 curtime
= time(NULL
);
1228 curdate
= localtime(&curtime
);
1230 if (curdate
->tm_hour
< 8)
1231 job
->hold_until
= curtime
;
1233 job
->hold_until
= curtime
+
1234 ((23 - curdate
->tm_hour
) * 60 + 59 -
1235 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1237 else if (!strcmp(when
, "weekend"))
1240 * Hold to weekend unless we are in the weekend.
1243 curtime
= time(NULL
);
1244 curdate
= localtime(&curtime
);
1246 if (curdate
->tm_wday
|| curdate
->tm_wday
== 6)
1247 job
->hold_until
= curtime
;
1249 job
->hold_until
= curtime
+
1250 (((5 - curdate
->tm_wday
) * 24 +
1251 (17 - curdate
->tm_hour
)) * 60 + 59 -
1252 curdate
->tm_min
) * 60 + 60 - curdate
->tm_sec
;
1254 else if (sscanf(when
, "%d:%d:%d", &hour
, &minute
, &second
) >= 2)
1257 * Hold to specified GMT time (HH:MM or HH:MM:SS)...
1260 curtime
= time(NULL
);
1261 curdate
= gmtime(&curtime
);
1263 job
->hold_until
= curtime
+
1264 ((hour
- curdate
->tm_hour
) * 60 + minute
-
1265 curdate
->tm_min
) * 60 + second
- curdate
->tm_sec
;
1268 * Hold until next day as needed...
1271 if (job
->hold_until
< curtime
)
1272 job
->hold_until
+= 24 * 60 * 60 * 60;
1275 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdSetJobHoldUntil: hold_until = %d",
1276 (int)job
->hold_until
);
1281 * 'cupsdSetJobPriority()' - Set the priority of a job, moving it up/down in
1282 * the list as needed.
1286 cupsdSetJobPriority(
1287 cupsd_job_t
*job
, /* I - Job ID */
1288 int priority
) /* I - New priority (0 to 100) */
1290 ipp_attribute_t
*attr
; /* Job attribute */
1294 * Don't change completed jobs...
1297 if (job
->state
->values
[0].integer
>= IPP_JOB_PROCESSING
)
1301 * Set the new priority and re-add the job into the active list...
1304 cupsArrayRemove(ActiveJobs
, job
);
1306 job
->priority
= priority
;
1308 if ((attr
= ippFindAttribute(job
->attrs
, "job-priority", IPP_TAG_INTEGER
)) != NULL
)
1309 attr
->values
[0].integer
= priority
;
1311 ippAddInteger(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "job-priority",
1314 cupsArrayAdd(ActiveJobs
, job
);
1321 * 'cupsdStartJob()' - Start a print job.
1325 cupsdStartJob(cupsd_job_t
*job
, /* I - Job ID */
1326 cupsd_printer_t
*printer
) /* I - Printer to print job */
1328 int i
; /* Looping var */
1329 int slot
; /* Pipe slot */
1330 cups_array_t
*filters
; /* Filters for job */
1331 mime_filter_t
*filter
, /* Current filter */
1332 port_monitor
; /* Port monitor filter */
1333 char method
[255], /* Method for output */
1334 *optptr
, /* Pointer to options */
1335 *valptr
; /* Pointer in value string */
1336 ipp_attribute_t
*attr
; /* Current attribute */
1337 int pid
; /* Process ID of new filter process */
1338 int banner_page
; /* 1 if banner page, 0 otherwise */
1339 int statusfds
[2], /* Pipes used between the filters and scheduler */
1340 filterfds
[2][2];/* Pipes used between the filters */
1341 int envc
; /* Number of environment variables */
1342 char *argv
[8], /* Filter command-line arguments */
1343 sani_uri
[1024], /* Sanitized DEVICE_URI env var */
1344 filename
[1024], /* Job filename */
1345 command
[1024], /* Full path to filter/backend command */
1346 jobid
[255], /* Job ID string */
1347 title
[IPP_MAX_NAME
],
1348 /* Job title string */
1349 copies
[255], /* # copies string */
1350 *envp
[100], /* Environment variables */
1351 charset
[255], /* CHARSET environment variable */
1352 class_name
[255],/* CLASS environment variable */
1353 classification
[1024],
1354 /* CLASSIFICATION environment variable */
1356 /* CONTENT_TYPE environment variable */
1358 /* DEVICE_URI environment variable */
1359 lang
[255], /* LANG environment variable */
1360 ppd
[1024], /* PPD environment variable */
1362 /* PRINTER environment variable */
1364 /* RIP_MAX_CACHE environment variable */
1365 static char *options
= NULL
;/* Full list of options */
1366 static int optlength
= 0; /* Length of option buffer */
1369 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob() id = %d, file = %d/%d",
1370 job
->id
, job
->current_file
, job
->num_files
);
1372 if (job
->num_files
== 0)
1374 cupsdLogMessage(CUPSD_LOG_ERROR
, "Job ID %d has no files! Cancelling it!",
1377 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1378 "Job canceled because it has no files.");
1380 cupsdCancelJob(job
, 0);
1385 * Figure out what filters are required to convert from
1386 * the source to the destination type...
1395 * Remote jobs and raw queues go directly to the printer without
1399 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1400 "cupsdStartJob: Sending job to queue tagged as raw...");
1407 * Local jobs get filtered...
1410 filters
= mimeFilter(MimeDatabase
, job
->filetypes
[job
->current_file
],
1411 printer
->filetype
, &(job
->cost
), MAX_FILTERS
- 1);
1415 cupsdLogMessage(CUPSD_LOG_ERROR
,
1416 "Unable to convert file %d to printable format for job %d!",
1417 job
->current_file
, job
->id
);
1418 cupsdLogMessage(CUPSD_LOG_INFO
,
1419 "Hint: Do you have ESP Ghostscript installed?");
1421 if (LogLevel
< CUPSD_LOG_DEBUG
)
1422 cupsdLogMessage(CUPSD_LOG_INFO
,
1423 "Hint: Try setting the LogLevel to \"debug\".");
1425 job
->current_file
++;
1427 if (job
->current_file
== job
->num_files
)
1429 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1430 "Job canceled because it has no files that can be printed.");
1432 cupsdCancelJob(job
, 0);
1439 * Remove NULL ("-") filters...
1442 for (filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
1444 filter
= (mime_filter_t
*)cupsArrayNext(filters
))
1445 if (!strcmp(filter
->filter
, "-"))
1446 cupsArrayRemove(filters
, filter
);
1448 if (cupsArrayCount(filters
) == 0)
1450 cupsArrayDelete(filters
);
1456 * See if the filter cost is too high...
1459 if ((FilterLevel
+ job
->cost
) > FilterLimit
&& FilterLevel
> 0 &&
1463 * Don't print this job quite yet...
1466 cupsArrayDelete(filters
);
1468 cupsdLogMessage(CUPSD_LOG_INFO
,
1469 "Holding job %d because filter limit has been reached.",
1471 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1472 "cupsdStartJob: id=%d, file=%d, cost=%d, level=%d, limit=%d",
1473 job
->id
, job
->current_file
, job
->cost
, FilterLevel
,
1478 FilterLevel
+= job
->cost
;
1481 * Add decompression filters, if any...
1484 if (job
->compressions
[job
->current_file
])
1487 * Add gziptoany filter to the front of the list...
1490 if (!cupsArrayInsert(filters
, &gziptoany_filter
))
1492 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add decompression filter - %s",
1495 if (filters
!= NULL
)
1498 job
->current_file
++;
1500 if (job
->current_file
== job
->num_files
)
1502 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1503 "Job canceled because the print file could not be "
1506 cupsdCancelJob(job
, 0);
1514 * Add port monitor, if any...
1517 if (printer
->port_monitor
)
1520 * Add port monitor to the end of the list...
1523 if (!cupsArrayAdd(filters
, &port_monitor
))
1525 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to add port monitor - %s",
1528 if (filters
!= NULL
)
1531 job
->current_file
++;
1533 if (job
->current_file
== job
->num_files
)
1535 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1536 "Job canceled because the port monitor could not be "
1539 cupsdCancelJob(job
, 0);
1545 snprintf(port_monitor
.filter
, sizeof(port_monitor
.filter
),
1546 "%s/monitor/%s", ServerBin
, printer
->port_monitor
);
1550 * Update the printer and job state to "processing"...
1553 job
->state
->values
[0].integer
= IPP_JOB_PROCESSING
;
1555 job
->printer
= printer
;
1557 cupsdSetPrinterState(printer
, IPP_PRINTER_PROCESSING
, 0);
1559 if (job
->current_file
== 0)
1561 set_time(job
, "time-at-processing");
1562 cupsdOpenPipe(job
->back_pipes
);
1566 * Determine if we are printing a banner page or not...
1569 if (job
->job_sheets
== NULL
)
1571 cupsdLogMessage(CUPSD_LOG_DEBUG
, "No job-sheets attribute.");
1572 if ((job
->job_sheets
=
1573 ippFindAttribute(job
->attrs
, "job-sheets", IPP_TAG_ZERO
)) != NULL
)
1574 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1575 "... but someone added one without setting job_sheets!");
1577 else if (job
->job_sheets
->num_values
== 1)
1578 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s",
1579 job
->job_sheets
->values
[0].string
.text
);
1581 cupsdLogMessage(CUPSD_LOG_DEBUG
, "job-sheets=%s,%s",
1582 job
->job_sheets
->values
[0].string
.text
,
1583 job
->job_sheets
->values
[1].string
.text
);
1585 if (printer
->type
& (CUPS_PRINTER_REMOTE
| CUPS_PRINTER_IMPLICIT
))
1587 else if (job
->job_sheets
== NULL
)
1589 else if (strcasecmp(job
->job_sheets
->values
[0].string
.text
, "none") != 0 &&
1590 job
->current_file
== 0)
1592 else if (job
->job_sheets
->num_values
> 1 &&
1593 strcasecmp(job
->job_sheets
->values
[1].string
.text
, "none") != 0 &&
1594 job
->current_file
== (job
->num_files
- 1))
1599 cupsdLogMessage(CUPSD_LOG_DEBUG
, "banner_page = %d", banner_page
);
1602 * Building the options string is harder than it needs to be, but
1603 * for the moment we need to pass strings for command-line args and
1604 * not IPP attribute pointers... :)
1606 * First allocate/reallocate the option buffer as needed...
1609 i
= ipp_length(job
->attrs
);
1616 optptr
= realloc(options
, i
);
1620 cupsdLogMessage(CUPSD_LOG_CRIT
,
1621 "cupsdStartJob: Unable to allocate %d bytes for option buffer for job %d!",
1624 if (filters
!= NULL
)
1627 FilterLevel
-= job
->cost
;
1629 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1630 "Job canceled because the server ran out of memory.");
1632 cupsdCancelJob(job
, 0);
1641 * Now loop through the attributes and convert them to the textual
1642 * representation used by the filters...
1648 snprintf(title
, sizeof(title
), "%s-%d", printer
->name
, job
->id
);
1649 strcpy(copies
, "1");
1651 for (attr
= job
->attrs
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1653 if (strcmp(attr
->name
, "copies") == 0 &&
1654 attr
->value_tag
== IPP_TAG_INTEGER
)
1657 * Don't use the # copies attribute if we are printing the job sheets...
1661 sprintf(copies
, "%d", attr
->values
[0].integer
);
1663 else if (strcmp(attr
->name
, "job-name") == 0 &&
1664 (attr
->value_tag
== IPP_TAG_NAME
||
1665 attr
->value_tag
== IPP_TAG_NAMELANG
))
1666 strlcpy(title
, attr
->values
[0].string
.text
, sizeof(title
));
1667 else if (attr
->group_tag
== IPP_TAG_JOB
)
1670 * Filter out other unwanted attributes...
1673 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
1674 attr
->value_tag
== IPP_TAG_NAMELANG
||
1675 attr
->value_tag
== IPP_TAG_TEXTLANG
||
1676 attr
->value_tag
== IPP_TAG_URI
||
1677 attr
->value_tag
== IPP_TAG_URISCHEME
||
1678 attr
->value_tag
== IPP_TAG_BEGIN_COLLECTION
) /* Not yet supported */
1681 if (strncmp(attr
->name
, "time-", 5) == 0)
1684 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1685 !(printer
->type
& CUPS_PRINTER_REMOTE
))
1688 if (strncmp(attr
->name
, "job-", 4) == 0 &&
1689 strcmp(attr
->name
, "job-billing") != 0 &&
1690 strcmp(attr
->name
, "job-sheets") != 0 &&
1691 strcmp(attr
->name
, "job-hold-until") != 0 &&
1692 strcmp(attr
->name
, "job-priority") != 0)
1695 if ((strcmp(attr
->name
, "page-label") == 0 ||
1696 strcmp(attr
->name
, "page-border") == 0 ||
1697 strncmp(attr
->name
, "number-up", 9) == 0 ||
1698 strcmp(attr
->name
, "page-set") == 0) &&
1703 * Otherwise add them to the list...
1706 if (optptr
> options
)
1707 strlcat(optptr
, " ", optlength
- (optptr
- options
));
1709 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
1711 strlcat(optptr
, attr
->name
, optlength
- (optptr
- options
));
1712 strlcat(optptr
, "=", optlength
- (optptr
- options
));
1715 for (i
= 0; i
< attr
->num_values
; i
++)
1718 strlcat(optptr
, ",", optlength
- (optptr
- options
));
1720 optptr
+= strlen(optptr
);
1722 switch (attr
->value_tag
)
1724 case IPP_TAG_INTEGER
:
1726 snprintf(optptr
, optlength
- (optptr
- options
),
1727 "%d", attr
->values
[i
].integer
);
1730 case IPP_TAG_BOOLEAN
:
1731 if (!attr
->values
[i
].boolean
)
1732 strlcat(optptr
, "no", optlength
- (optptr
- options
));
1734 case IPP_TAG_NOVALUE
:
1735 strlcat(optptr
, attr
->name
,
1736 optlength
- (optptr
- options
));
1739 case IPP_TAG_RANGE
:
1740 if (attr
->values
[i
].range
.lower
== attr
->values
[i
].range
.upper
)
1741 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1742 "%d", attr
->values
[i
].range
.lower
);
1744 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1745 "%d-%d", attr
->values
[i
].range
.lower
,
1746 attr
->values
[i
].range
.upper
);
1749 case IPP_TAG_RESOLUTION
:
1750 snprintf(optptr
, optlength
- (optptr
- options
) - 1,
1751 "%dx%d%s", attr
->values
[i
].resolution
.xres
,
1752 attr
->values
[i
].resolution
.yres
,
1753 attr
->values
[i
].resolution
.units
== IPP_RES_PER_INCH
?
1757 case IPP_TAG_STRING
:
1760 case IPP_TAG_KEYWORD
:
1761 case IPP_TAG_CHARSET
:
1762 case IPP_TAG_LANGUAGE
:
1763 for (valptr
= attr
->values
[i
].string
.text
; *valptr
;)
1765 if (strchr(" \t\n\\\'\"", *valptr
))
1767 *optptr
++ = *valptr
++;
1774 break; /* anti-compiler-warning-code */
1778 optptr
+= strlen(optptr
);
1783 * Build the command-line arguments for the filters. Each filter
1784 * has 6 or 7 arguments:
1788 * argv[2] = username
1790 * argv[4] = # copies
1792 * argv[6] = filename (optional; normally stdin)
1794 * This allows legacy printer drivers that use the old System V
1795 * printing interface to be used by CUPS.
1798 sprintf(jobid
, "%d", job
->id
);
1799 snprintf(filename
, sizeof(filename
), "%s/d%05d-%03d", RequestRoot
,
1800 job
->id
, job
->current_file
+ 1);
1802 argv
[0] = printer
->name
;
1804 argv
[2] = job
->username
;
1811 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1812 "cupsdStartJob: argv = \"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"",
1813 argv
[0], argv
[1], argv
[2], argv
[3], argv
[4], argv
[5], argv
[6]);
1816 * Create environment variable strings for the filters...
1819 attr
= ippFindAttribute(job
->attrs
, "attributes-natural-language",
1822 switch (strlen(attr
->values
[0].string
.text
))
1826 * This is an unknown or badly formatted language code; use
1827 * the POSIX locale...
1830 strcpy(lang
, "LANG=C");
1835 * Just the language code (ll)...
1838 snprintf(lang
, sizeof(lang
), "LANG=%s",
1839 attr
->values
[0].string
.text
);
1844 * Language and country code (ll-cc)...
1847 snprintf(lang
, sizeof(lang
), "LANG=%c%c_%c%c",
1848 attr
->values
[0].string
.text
[0],
1849 attr
->values
[0].string
.text
[1],
1850 toupper(attr
->values
[0].string
.text
[3] & 255),
1851 toupper(attr
->values
[0].string
.text
[4] & 255));
1855 attr
= ippFindAttribute(job
->attrs
, "document-format",
1858 (optptr
= strstr(attr
->values
[0].string
.text
, "charset=")) != NULL
)
1859 snprintf(charset
, sizeof(charset
), "CHARSET=%s", optptr
+ 8);
1862 attr
= ippFindAttribute(job
->attrs
, "attributes-charset",
1864 snprintf(charset
, sizeof(charset
), "CHARSET=%s",
1865 attr
->values
[0].string
.text
);
1868 snprintf(content_type
, sizeof(content_type
), "CONTENT_TYPE=%s/%s",
1869 job
->filetypes
[job
->current_file
]->super
,
1870 job
->filetypes
[job
->current_file
]->type
);
1871 snprintf(device_uri
, sizeof(device_uri
), "DEVICE_URI=%s", printer
->device_uri
);
1872 cupsdSanitizeURI(printer
->device_uri
, sani_uri
, sizeof(sani_uri
));
1873 snprintf(ppd
, sizeof(ppd
), "PPD=%s/ppd/%s.ppd", ServerRoot
, printer
->name
);
1874 snprintf(printer_name
, sizeof(printer_name
), "PRINTER=%s", printer
->name
);
1875 snprintf(rip_max_cache
, sizeof(rip_max_cache
), "RIP_MAX_CACHE=%s", RIPCache
);
1877 envc
= cupsdLoadEnv(envp
, (int)(sizeof(envp
) / sizeof(envp
[0])));
1879 envp
[envc
++] = charset
;
1880 envp
[envc
++] = lang
;
1881 envp
[envc
++] = ppd
;
1882 envp
[envc
++] = rip_max_cache
;
1883 envp
[envc
++] = content_type
;
1884 envp
[envc
++] = device_uri
;
1885 envp
[envc
++] = printer_name
;
1887 if (Classification
&& !banner_page
)
1889 if ((attr
= ippFindAttribute(job
->attrs
, "job-sheets",
1890 IPP_TAG_NAME
)) == NULL
)
1891 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1893 else if (attr
->num_values
> 1 &&
1894 strcmp(attr
->values
[1].string
.text
, "none") != 0)
1895 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1896 attr
->values
[1].string
.text
);
1898 snprintf(classification
, sizeof(classification
), "CLASSIFICATION=%s",
1899 attr
->values
[0].string
.text
);
1901 envp
[envc
++] = classification
;
1904 if (job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
))
1906 snprintf(class_name
, sizeof(class_name
), "CLASS=%s", job
->dest
);
1907 envp
[envc
++] = class_name
;
1912 for (i
= 0; i
< envc
; i
++)
1913 if (strncmp(envp
[i
], "DEVICE_URI=", 11))
1914 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: envp[%d]=\"%s\"",
1917 cupsdLogMessage(CUPSD_LOG_DEBUG
,
1918 "cupsdStartJob: envp[%d]=\"DEVICE_URI=%s\"", i
, sani_uri
);
1920 job
->current_file
++;
1923 * Now create processes for all of the filters...
1926 if (cupsdOpenPipe(statusfds
))
1928 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to create job status pipes - %s.",
1930 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1931 "Unable to create status pipes - %s.", strerror(errno
));
1933 cupsdAddPrinterHistory(printer
);
1935 if (filters
!= NULL
)
1938 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
1939 "Job canceled because the server could not create the job status pipes.");
1941 cupsdCancelJob(job
, 0);
1945 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: statusfds = [ %d %d ]",
1946 statusfds
[0], statusfds
[1]);
1949 fcntl(statusfds
[0], F_SETFD
, FD_CLOEXEC
);
1950 fcntl(statusfds
[1], F_SETFD
, FD_CLOEXEC
);
1951 #endif /* FD_CLOEXEC */
1953 job
->status_buffer
= cupsdStatBufNew(statusfds
[0], "[Job %d]",
1956 memset(job
->filters
, 0, sizeof(job
->filters
));
1958 filterfds
[1][0] = open("/dev/null", O_RDONLY
);
1959 filterfds
[1][1] = -1;
1961 if (filterfds
[1][0] < 0)
1963 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
1965 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
1966 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
1968 cupsdAddPrinterHistory(printer
);
1970 if (filters
!= NULL
)
1973 cupsdClosePipe(statusfds
);
1974 cupsdCancelJob(job
, 0);
1978 fcntl(filterfds
[1][0], F_SETFD
, fcntl(filterfds
[1][0], F_GETFD
) | FD_CLOEXEC
);
1980 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
1981 1, filterfds
[1][0], filterfds
[1][1]);
1983 for (i
= 0, slot
= 0, filter
= (mime_filter_t
*)cupsArrayFirst(filters
);
1985 i
++, filter
= (mime_filter_t
*)cupsArrayNext(filters
))
1987 if (filter
->filter
[0] != '/')
1988 snprintf(command
, sizeof(command
), "%s/filter/%s", ServerBin
,
1991 strlcpy(command
, filter
->filter
, sizeof(command
));
1993 if (i
< (cupsArrayCount(filters
) - 1))
1995 if (cupsdOpenPipe(filterfds
[slot
]))
1997 cupsdLogMessage(CUPSD_LOG_ERROR
,
1998 "Unable to create job filter pipes - %s.",
2000 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2001 "Unable to create filter pipes - %s.", strerror(errno
));
2002 cupsdAddPrinterHistory(printer
);
2004 cupsArrayDelete(filters
);
2006 cupsdClosePipe(statusfds
);
2007 cupsdClosePipe(filterfds
[!slot
]);
2009 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2010 "Job canceled because the server could not create the filter pipes.");
2012 cupsdCancelJob(job
, 0);
2018 if (job
->current_file
== 1)
2020 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
2022 if (cupsdOpenPipe(job
->print_pipes
))
2024 cupsdLogMessage(CUPSD_LOG_ERROR
,
2025 "Unable to create job backend pipes - %s.",
2027 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2028 "Unable to create backend pipes - %s.", strerror(errno
));
2029 cupsdAddPrinterHistory(printer
);
2031 cupsArrayDelete(filters
);
2033 cupsdClosePipe(statusfds
);
2034 cupsdClosePipe(filterfds
[!slot
]);
2036 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2037 "Job canceled because the server could not create the backend pipes.");
2039 cupsdCancelJob(job
, 0);
2045 job
->print_pipes
[0] = -1;
2046 if (!strncmp(printer
->device_uri
, "file:/dev/", 10) &&
2047 strcmp(printer
->device_uri
, "file:/dev/null"))
2048 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
2050 else if (!strncmp(printer
->device_uri
, "file:///dev/", 12) &&
2051 strcmp(printer
->device_uri
, "file:///dev/null"))
2052 job
->print_pipes
[1] = open(printer
->device_uri
+ 7,
2055 job
->print_pipes
[1] = open(printer
->device_uri
+ 5,
2056 O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
2058 if (job
->print_pipes
[1] < 0)
2060 cupsdLogMessage(CUPSD_LOG_ERROR
,
2061 "Unable to open output file \"%s\" - %s.",
2062 printer
->device_uri
, strerror(errno
));
2063 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2064 "Unable to open output file \"%s\" - %s.",
2065 printer
->device_uri
, strerror(errno
));
2067 cupsdAddPrinterHistory(printer
);
2069 cupsArrayDelete(filters
);
2071 cupsdClosePipe(statusfds
);
2072 cupsdClosePipe(filterfds
[!slot
]);
2074 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2075 "Job canceled because the server could not open the output file.");
2077 cupsdCancelJob(job
, 0);
2081 fcntl(job
->print_pipes
[1], F_SETFD
,
2082 fcntl(job
->print_pipes
[1], F_GETFD
) | FD_CLOEXEC
);
2085 cupsdLogMessage(CUPSD_LOG_DEBUG2
, "cupsdStartJob: print_pipes = [ %d %d ]",
2086 job
->print_pipes
[0], job
->print_pipes
[1]);
2089 filterfds
[slot
][0] = job
->print_pipes
[0];
2090 filterfds
[slot
][1] = job
->print_pipes
[1];
2093 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filter = \"%s\"", command
);
2094 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2095 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2097 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
2098 filterfds
[slot
][1], statusfds
[1],
2099 job
->back_pipes
[0], 0, job
->filters
+ i
);
2101 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2102 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2103 !slot
, filterfds
[!slot
][0], filterfds
[!slot
][1]);
2105 cupsdClosePipe(filterfds
[!slot
]);
2109 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start filter \"%s\" - %s.",
2110 filter
->filter
, strerror(errno
));
2111 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2112 "Unable to start filter \"%s\" - %s.",
2113 filter
->filter
, strerror(errno
));
2115 cupsdAddPrinterHistory(printer
);
2117 cupsArrayDelete(filters
);
2119 cupsdAddPrinterHistory(printer
);
2121 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2122 "Job canceled because the server could not execute a filter.");
2124 cupsdCancelJob(job
, 0);
2128 cupsdLogMessage(CUPSD_LOG_INFO
, "Started filter %s (PID %d) for job %d.",
2129 command
, pid
, job
->id
);
2135 cupsArrayDelete(filters
);
2138 * Finally, pipe the final output into a backend process if needed...
2141 if (strncmp(printer
->device_uri
, "file:", 5) != 0)
2143 if (job
->current_file
== 1)
2145 sscanf(printer
->device_uri
, "%254[^:]", method
);
2146 snprintf(command
, sizeof(command
), "%s/backend/%s", ServerBin
, method
);
2150 filterfds
[slot
][0] = -1;
2151 filterfds
[slot
][1] = open("/dev/null", O_WRONLY
);
2153 if (filterfds
[slot
][1] < 0)
2155 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to open \"/dev/null\" - %s.",
2157 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2158 "Unable to open \"/dev/null\" - %s.", strerror(errno
));
2160 cupsdAddPrinterHistory(printer
);
2162 cupsdClosePipe(statusfds
);
2164 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2165 "Job canceled because the server could not open a file.");
2167 cupsdCancelJob(job
, 0);
2171 fcntl(filterfds
[slot
][1], F_SETFD
,
2172 fcntl(filterfds
[slot
][1], F_GETFD
) | FD_CLOEXEC
);
2174 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStartJob: backend = \"%s\"",
2176 cupsdLogMessage(CUPSD_LOG_DEBUG
,
2177 "cupsdStartJob: filterfds[%d] = [ %d %d ]",
2178 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2180 pid
= cupsdStartProcess(command
, argv
, envp
, filterfds
[!slot
][0],
2181 filterfds
[slot
][1], statusfds
[1],
2182 job
->back_pipes
[1], 1,
2187 cupsdLogMessage(CUPSD_LOG_ERROR
, "Unable to start backend \"%s\" - %s.",
2188 method
, strerror(errno
));
2189 snprintf(printer
->state_message
, sizeof(printer
->state_message
),
2190 "Unable to start backend \"%s\" - %s.", method
, strerror(errno
));
2192 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2193 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2194 job
->print_pipes
[0], job
->print_pipes
[1]);
2196 cupsdClosePipe(job
->print_pipes
);
2198 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2199 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2200 job
->back_pipes
[0], job
->back_pipes
[1]);
2202 cupsdClosePipe(job
->back_pipes
);
2204 cupsdAddEvent(CUPSD_EVENT_JOB_COMPLETED
, job
->printer
, job
,
2205 "Job canceled because the server could not execute the backend.");
2207 cupsdCancelJob(job
, 0);
2212 cupsdLogMessage(CUPSD_LOG_INFO
,
2213 "Started backend %s (PID %d) for job %d.",
2214 command
, pid
, job
->id
);
2218 if (job
->current_file
== job
->num_files
)
2220 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2221 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2222 job
->print_pipes
[0], job
->print_pipes
[1]);
2224 cupsdClosePipe(job
->print_pipes
);
2226 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2227 "cupsdStartJob: Closing back pipes [ %d %d ]...",
2228 job
->back_pipes
[0], job
->back_pipes
[1]);
2230 cupsdClosePipe(job
->back_pipes
);
2235 filterfds
[slot
][0] = -1;
2236 filterfds
[slot
][1] = -1;
2238 if (job
->current_file
== job
->num_files
)
2240 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2241 "cupsdStartJob: Closing print pipes [ %d %d ]...",
2242 job
->print_pipes
[0], job
->print_pipes
[1]);
2244 cupsdClosePipe(job
->print_pipes
);
2248 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2249 "cupsdStartJob: Closing filter pipes for slot %d [ %d %d ]...",
2250 slot
, filterfds
[slot
][0], filterfds
[slot
][1]);
2252 cupsdClosePipe(filterfds
[slot
]);
2254 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2255 "cupsdStartJob: Closing status output pipe %d...",
2258 close(statusfds
[1]);
2260 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2261 "cupsdStartJob: Adding fd %d to InputSet...",
2262 job
->status_buffer
->fd
);
2264 FD_SET(job
->status_buffer
->fd
, InputSet
);
2266 cupsdAddEvent(CUPSD_EVENT_JOB_STATE
, job
->printer
, job
, "Job #%d started.",
2272 * 'cupsdStopAllJobs()' - Stop all print jobs.
2276 cupsdStopAllJobs(void)
2278 cupsd_job_t
*job
; /* Current job */
2281 DEBUG_puts("cupsdStopAllJobs()");
2283 for (job
= (cupsd_job_t
*)cupsArrayFirst(ActiveJobs
);
2285 job
= (cupsd_job_t
*)cupsArrayNext(ActiveJobs
))
2286 if (job
->state
->values
[0].integer
== IPP_JOB_PROCESSING
)
2288 cupsdStopJob(job
, 1);
2289 job
->state
->values
[0].integer
= IPP_JOB_PENDING
;
2295 * 'cupsdStopJob()' - Stop a print job.
2299 cupsdStopJob(cupsd_job_t
*job
, /* I - Job */
2300 int force
) /* I - 1 = Force all filters to stop */
2302 int i
; /* Looping var */
2305 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStopJob: id = %d, force = %d",
2308 if (job
->state
->values
[0].integer
!= IPP_JOB_PROCESSING
)
2311 FilterLevel
-= job
->cost
;
2313 if (job
->status
< 0 &&
2314 !(job
->dtype
& (CUPS_PRINTER_CLASS
| CUPS_PRINTER_IMPLICIT
)) &&
2315 !(job
->printer
->type
& CUPS_PRINTER_FAX
) &&
2316 !strcmp(job
->printer
->error_policy
, "stop-printer"))
2317 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_STOPPED
, 1);
2318 else if (job
->printer
->state
!= IPP_PRINTER_STOPPED
)
2319 cupsdSetPrinterState(job
->printer
, IPP_PRINTER_IDLE
, 0);
2321 cupsdLogMessage(CUPSD_LOG_DEBUG
, "cupsdStopJob: printer state is %d",
2322 job
->printer
->state
);
2324 job
->state
->values
[0].integer
= IPP_JOB_STOPPED
;
2325 job
->printer
->job
= NULL
;
2326 job
->printer
= NULL
;
2328 job
->current_file
--;
2330 for (i
= 0; job
->filters
[i
]; i
++)
2331 if (job
->filters
[i
] > 0)
2333 cupsdEndProcess(job
->filters
[i
], force
);
2334 job
->filters
[i
] = 0;
2337 if (job
->backend
> 0)
2339 cupsdEndProcess(job
->backend
, force
);
2343 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2344 "cupsdStopJob: Closing print pipes [ %d %d ]...",
2345 job
->print_pipes
[0], job
->print_pipes
[1]);
2347 cupsdClosePipe(job
->print_pipes
);
2349 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2350 "cupsdStopJob: Closing back pipes [ %d %d ]...",
2351 job
->back_pipes
[0], job
->back_pipes
[1]);
2353 cupsdClosePipe(job
->back_pipes
);
2355 if (job
->status_buffer
)
2358 * Close the pipe and clear the input bit.
2361 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2362 "cupsdStopJob: Removing fd %d from InputSet...",
2363 job
->status_buffer
->fd
);
2365 FD_CLR(job
->status_buffer
->fd
, InputSet
);
2367 cupsdLogMessage(CUPSD_LOG_DEBUG2
,
2368 "cupsdStopJob: Closing status input pipe %d...",
2369 job
->status_buffer
->fd
);
2371 cupsdStatBufDelete(job
->status_buffer
);
2373 job
->status_buffer
= NULL
;
2379 * 'cupsdUpdateJob()' - Read a status update from a job's filters.
2383 cupsdUpdateJob(cupsd_job_t
*job
) /* I - Job to check */
2385 int i
; /* Looping var */
2386 int copies
; /* Number of copies printed */
2387 char message
[1024], /* Message text */
2388 *ptr
; /* Pointer update... */
2389 int loglevel
; /* Log level for message */
2392 while ((ptr
= cupsdStatBufUpdate(job
->status_buffer
, &loglevel
,
2393 message
, sizeof(message
))) != NULL
)
2396 * Process page and printer state messages as needed...
2399 if (loglevel
== CUPSD_LOG_PAGE
)
2402 * Page message; send the message to the page_log file and update the
2403 * job sheet count...
2406 if (job
->sheets
!= NULL
)
2408 if (!strncasecmp(message
, "total ", 6))
2411 * Got a total count of pages from a backend or filter...
2414 copies
= atoi(message
+ 6);
2415 copies
-= job
->sheets
->values
[0].integer
; /* Just track the delta */
2417 else if (!sscanf(message
, "%*d%d", &copies
))
2420 job
->sheets
->values
[0].integer
+= copies
;
2422 if (job
->printer
->page_limit
)
2423 cupsdUpdateQuota(job
->printer
, job
->username
, copies
, 0);
2426 cupsdLogPage(job
, message
);
2428 cupsdAddEvent(CUPSD_EVENT_JOB_PROGRESS
, job
->printer
, job
,
2429 "Printed %d page(s).", job
->sheets
->values
[0].integer
);
2431 else if (loglevel
== CUPSD_LOG_STATE
)
2433 cupsdSetPrinterReasons(job
->printer
, message
);
2434 cupsdAddPrinterHistory(job
->printer
);
2436 else if (loglevel
== CUPSD_LOG_ATTR
)
2439 * Set attribute(s)...
2445 else if (!strncmp(message
, "recoverable:", 12))
2447 cupsdSetPrinterReasons(job
->printer
,
2448 "+com.apple.print.recoverable-warning");
2451 while (isspace(*ptr
& 255))
2454 cupsdSetString(&job
->printer
->recoverable
, ptr
);
2455 cupsdAddPrinterHistory(job
->printer
);
2457 else if (!strncmp(message
, "recovered:", 10))
2459 cupsdSetPrinterReasons(job
->printer
,
2460 "-com.apple.print.recoverable-warning");
2463 while (isspace(*ptr
& 255))
2466 cupsdSetString(&job
->printer
->recoverable
, ptr
);
2467 cupsdAddPrinterHistory(job
->printer
);
2469 #endif /* __APPLE__ */
2473 * Some message to show in the printer-state-message attribute...
2476 strlcpy(job
->printer
->state_message
, message
,
2477 sizeof(job
->printer
->state_message
));
2478 cupsdAddPrinterHistory(job
->printer
);
2481 if (!strchr(job
->status_buffer
->buffer
, '\n'))
2488 * See if all of the filters and the backend have returned their
2492 for (i
= 0; job
->filters
[i
] < 0; i
++);
2494 if (job
->filters
[i
])
2497 if (job
->current_file
>= job
->num_files
&& job
->backend
> 0)
2501 * Handle the end of job stuff...
2504 cupsdFinishJob(job
);
2510 * 'compare_active_jobs()' - Compare the job IDs and priorities of two jobs.
2513 static int /* O - Difference */
2514 compare_active_jobs(void *first
, /* I - First job */
2515 void *second
, /* I - Second job */
2516 void *data
) /* I - App data (not used) */
2518 int diff
; /* Difference */
2521 if ((diff
= ((cupsd_job_t
*)first
)->priority
- ((cupsd_job_t
*)second
)->priority
) != 0)
2524 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
2529 * 'compare_jobs()' - Compare the job IDs of two jobs.
2532 static int /* O - Difference */
2533 compare_jobs(void *first
, /* I - First job */
2534 void *second
, /* I - Second job */
2535 void *data
) /* I - App data (not used) */
2537 return (((cupsd_job_t
*)first
)->id
- ((cupsd_job_t
*)second
)->id
);
2542 * 'ipp_length()' - Compute the size of the buffer needed to hold
2543 * the textual IPP attributes.
2546 int /* O - Size of buffer to hold IPP attributes */
2547 ipp_length(ipp_t
*ipp
) /* I - IPP request */
2549 int bytes
; /* Number of bytes */
2550 int i
; /* Looping var */
2551 ipp_attribute_t
*attr
; /* Current attribute */
2555 * Loop through all attributes...
2560 for (attr
= ipp
->attrs
; attr
!= NULL
; attr
= attr
->next
)
2563 * Skip attributes that won't be sent to filters...
2566 if (attr
->value_tag
== IPP_TAG_MIMETYPE
||
2567 attr
->value_tag
== IPP_TAG_NAMELANG
||
2568 attr
->value_tag
== IPP_TAG_TEXTLANG
||
2569 attr
->value_tag
== IPP_TAG_URI
||
2570 attr
->value_tag
== IPP_TAG_URISCHEME
)
2573 if (strncmp(attr
->name
, "time-", 5) == 0)
2577 * Add space for a leading space and commas between each value.
2578 * For the first attribute, the leading space isn't used, so the
2579 * extra byte can be used as the nul terminator...
2582 bytes
++; /* " " separator */
2583 bytes
+= attr
->num_values
; /* "," separators */
2586 * Boolean attributes appear as "foo,nofoo,foo,nofoo", while
2587 * other attributes appear as "foo=value1,value2,...,valueN".
2590 if (attr
->value_tag
!= IPP_TAG_BOOLEAN
)
2591 bytes
+= strlen(attr
->name
);
2593 bytes
+= attr
->num_values
* strlen(attr
->name
);
2596 * Now add the size required for each value in the attribute...
2599 switch (attr
->value_tag
)
2601 case IPP_TAG_INTEGER
:
2604 * Minimum value of a signed integer is -2147483647, or 11 digits.
2607 bytes
+= attr
->num_values
* 11;
2610 case IPP_TAG_BOOLEAN
:
2612 * Add two bytes for each false ("no") value...
2615 for (i
= 0; i
< attr
->num_values
; i
++)
2616 if (!attr
->values
[i
].boolean
)
2620 case IPP_TAG_RANGE
:
2622 * A range is two signed integers separated by a hyphen, or
2623 * 23 characters max.
2626 bytes
+= attr
->num_values
* 23;
2629 case IPP_TAG_RESOLUTION
:
2631 * A resolution is two signed integers separated by an "x" and
2632 * suffixed by the units, or 26 characters max.
2635 bytes
+= attr
->num_values
* 26;
2638 case IPP_TAG_STRING
:
2641 case IPP_TAG_KEYWORD
:
2642 case IPP_TAG_CHARSET
:
2643 case IPP_TAG_LANGUAGE
:
2645 * Strings can contain characters that need quoting. We need
2646 * at least 2 * len + 2 characters to cover the quotes and
2647 * any backslashes in the string.
2650 for (i
= 0; i
< attr
->num_values
; i
++)
2651 bytes
+= 2 * strlen(attr
->values
[i
].string
.text
) + 2;
2655 break; /* anti-compiler-warning-code */
2664 * 'set_time()' - Set one of the "time-at-xyz" attributes...
2668 set_time(cupsd_job_t
*job
, /* I - Job to update */
2669 const char *name
) /* I - Name of attribute */
2671 ipp_attribute_t
*attr
; /* Time attribute */
2674 if ((attr
= ippFindAttribute(job
->attrs
, name
, IPP_TAG_ZERO
)) != NULL
)
2676 attr
->value_tag
= IPP_TAG_INTEGER
;
2677 attr
->values
[0].integer
= time(NULL
);
2683 * 'set_hold_until()' - Set the hold time and update job-hold-until attribute...
2687 set_hold_until(cupsd_job_t
*job
, /* I - Job to update */
2688 time_t holdtime
) /* I - Hold until time */
2690 ipp_attribute_t
*attr
; /* job-hold-until attribute */
2691 struct tm
*holddate
; /* Hold date */
2692 char holdstr
[64]; /* Hold time */
2696 * Set the hold_until value and hold the job...
2699 cupsdLogMessage(CUPSD_LOG_DEBUG
, "set_hold_until: hold_until = %d", (int)holdtime
);
2701 job
->state
->values
[0].integer
= IPP_JOB_HELD
;
2702 job
->hold_until
= holdtime
;
2705 * Update the job-hold-until attribute with a string representing GMT
2706 * time (HH:MM:SS)...
2709 holddate
= gmtime(&holdtime
);
2710 snprintf(holdstr
, sizeof(holdstr
), "%d:%d:%d", holddate
->tm_hour
,
2711 holddate
->tm_min
, holddate
->tm_sec
);
2713 if ((attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_KEYWORD
)) == NULL
)
2714 attr
= ippFindAttribute(job
->attrs
, "job-hold-until", IPP_TAG_NAME
);
2717 * Either add the attribute or update the value of the existing one
2721 attr
= ippAddString(job
->attrs
, IPP_TAG_JOB
, IPP_TAG_KEYWORD
,
2722 "job-hold-until", NULL
, holdstr
);
2724 cupsdSetString(&attr
->values
[0].string
.text
, holdstr
);