2 * "$Id: ipp.c 5724 2006-07-12 19:42:35Z mike $"
4 * IPP backend 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" 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
24 * This file is subject to the Apple OS-Developed Software exception.
28 * main() - Send a file to the printer or server.
29 * cancel_job() - Cancel a print job.
30 * check_printer_state() - Check the printer state...
31 * compress_files() - Compress print files...
32 * password_cb() - Disable the password prompt for
33 * cupsDoFileRequest().
34 * report_printer_state() - Report the printer state.
35 * run_pictwps_filter() - Convert PICT files to PostScript when printing
37 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
41 * Include necessary headers.
44 #include <cups/http-private.h>
48 #include <sys/types.h>
50 #include <cups/backend.h>
51 #include <cups/cups.h>
52 #include <cups/language.h>
53 #include <cups/string.h>
62 static char *password
= NULL
; /* Password for device URI */
64 static char pstmpname
[1024] = ""; /* Temporary PostScript file name */
65 #endif /* __APPLE__ */
66 static char tmpfilename
[1024] = ""; /* Temporary spool file name */
67 static int job_cancelled
= 0; /* Job cancelled? */
74 static void cancel_job(http_t
*http
, const char *uri
, int id
,
75 const char *resource
, const char *user
, int version
);
76 static void check_printer_state(http_t
*http
, const char *uri
,
77 const char *resource
, const char *user
,
80 static void compress_files(int num_files
, char **files
);
81 #endif /* HAVE_LIBZ */
82 static const char *password_cb(const char *);
83 static int report_printer_state(ipp_t
*ipp
);
86 static int run_pictwps_filter(char **argv
, const char *filename
);
87 #endif /* __APPLE__ */
88 static void sigterm_handler(int sig
);
92 * 'main()' - Send a file to the printer or server.
96 * printer-uri job-id user title copies options [file]
99 int /* O - Exit status */
100 main(int argc
, /* I - Number of command-line args */
101 char *argv
[]) /* I - Command-line arguments */
103 int i
; /* Looping var */
104 int num_options
; /* Number of printer options */
105 cups_option_t
*options
; /* Printer options */
106 char method
[255], /* Method in URI */
107 hostname
[1024], /* Hostname */
108 username
[255], /* Username info */
109 resource
[1024], /* Resource info (printer name) */
110 *optptr
, /* Pointer to URI options */
111 name
[255], /* Name of option */
112 value
[255], /* Value of option */
113 *ptr
; /* Pointer into name or value */
114 int num_files
; /* Number of files to print */
115 char **files
, /* Files to print */
116 *filename
; /* Pointer to single filename */
117 int port
; /* Port number (not used) */
118 char uri
[HTTP_MAX_URI
]; /* Updated URI without user/pass */
119 ipp_status_t ipp_status
; /* Status of IPP request */
120 http_t
*http
; /* HTTP connection */
121 ipp_t
*request
, /* IPP request */
122 *response
, /* IPP response */
123 *supported
; /* get-printer-attributes response */
124 int compression
, /* Do compression of the job data? */
125 waitjob
, /* Wait for job complete? */
126 waitprinter
; /* Wait for printer ready? */
127 ipp_attribute_t
*job_id_attr
; /* job-id attribute */
128 int job_id
; /* job-id value */
129 ipp_attribute_t
*job_sheets
; /* job-media-sheets-completed */
130 ipp_attribute_t
*job_state
; /* job-state */
131 ipp_attribute_t
*copies_sup
; /* copies-supported */
132 ipp_attribute_t
*format_sup
; /* document-format-supported */
133 ipp_attribute_t
*printer_state
; /* printer-state attribute */
134 ipp_attribute_t
*printer_accepting
; /* printer-is-accepting-jobs */
135 int copies
; /* Number of copies remaining */
136 const char *content_type
; /* CONTENT_TYPE environment variable */
137 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
138 struct sigaction action
; /* Actions for POSIX signals */
139 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
140 int version
; /* IPP version */
141 int reasons
; /* Number of printer-state-reasons */
142 static const char * const pattrs
[] =
143 { /* Printer attributes we want */
145 "document-format-supported",
146 "printer-is-accepting-jobs",
148 "printer-state-reasons",
150 static const char * const jattrs
[] =
151 { /* Job attributes we want */
152 "job-media-sheets-completed",
158 * Make sure status messages are not buffered...
161 setbuf(stderr
, NULL
);
164 * Ignore SIGPIPE and catch SIGTERM signals...
168 sigset(SIGPIPE
, SIG_IGN
);
169 sigset(SIGTERM
, sigterm_handler
);
170 #elif defined(HAVE_SIGACTION)
171 memset(&action
, 0, sizeof(action
));
172 action
.sa_handler
= SIG_IGN
;
173 sigaction(SIGPIPE
, &action
, NULL
);
175 sigemptyset(&action
.sa_mask
);
176 sigaddset(&action
.sa_mask
, SIGTERM
);
177 action
.sa_handler
= sigterm_handler
;
178 sigaction(SIGTERM
, &action
, NULL
);
180 signal(SIGPIPE
, SIG_IGN
);
181 signal(SIGTERM
, sigterm_handler
);
182 #endif /* HAVE_SIGSET */
185 * Check command-line...
192 if ((s
= strrchr(argv
[0], '/')) != NULL
)
197 printf("network %s \"Unknown\" \"Internet Printing Protocol (%s)\"\n",
199 return (CUPS_BACKEND_OK
);
204 "Usage: %s job-id user title copies options [file ... fileN]\n",
206 return (CUPS_BACKEND_STOP
);
210 * Get the (final) content type...
213 if ((content_type
= getenv("FINAL_CONTENT_TYPE")) == NULL
)
214 if ((content_type
= getenv("CONTENT_TYPE")) == NULL
)
215 content_type
= "application/octet-stream";
218 * Extract the hostname and printer name from the URI...
221 if (httpSeparateURI(HTTP_URI_CODING_ALL
, cupsBackendDeviceURI(argv
),
222 method
, sizeof(method
), username
, sizeof(username
),
223 hostname
, sizeof(hostname
), &port
,
224 resource
, sizeof(resource
)) < HTTP_URI_OK
)
226 fputs("ERROR: Missing device URI on command-line and no DEVICE_URI "
227 "environment variable!\n", stderr
);
228 return (CUPS_BACKEND_STOP
);
231 if (!strcmp(method
, "https"))
232 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
234 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
237 * See if there are any options...
245 if ((optptr
= strchr(resource
, '?')) != NULL
)
248 * Yup, terminate the device name string and move to the first
249 * character of the optptr...
255 * Then parse the optptr...
264 for (ptr
= name
; *optptr
&& *optptr
!= '=';)
265 if (ptr
< (name
+ sizeof(name
) - 1))
277 for (ptr
= value
; *optptr
&& *optptr
!= '+' && *optptr
!= '&';)
278 if (ptr
< (value
+ sizeof(value
) - 1))
282 if (*optptr
== '+' || *optptr
== '&')
289 * Process the option...
292 if (!strcasecmp(name
, "waitjob"))
295 * Wait for job completion?
298 waitjob
= !strcasecmp(value
, "on") ||
299 !strcasecmp(value
, "yes") ||
300 !strcasecmp(value
, "true");
302 else if (!strcasecmp(name
, "waitprinter"))
305 * Wait for printer idle?
308 waitprinter
= !strcasecmp(value
, "on") ||
309 !strcasecmp(value
, "yes") ||
310 !strcasecmp(value
, "true");
312 else if (!strcasecmp(name
, "encryption"))
315 * Enable/disable encryption?
318 if (!strcasecmp(value
, "always"))
319 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
320 else if (!strcasecmp(value
, "required"))
321 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
322 else if (!strcasecmp(value
, "never"))
323 cupsSetEncryption(HTTP_ENCRYPT_NEVER
);
324 else if (!strcasecmp(value
, "ifrequested"))
325 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
328 fprintf(stderr
, "ERROR: Unknown encryption option value \"%s\"!\n",
332 else if (!strcasecmp(name
, "version"))
334 if (!strcmp(value
, "1.0"))
336 else if (!strcmp(value
, "1.1"))
340 fprintf(stderr
, "ERROR: Unknown version option value \"%s\"!\n",
345 else if (!strcasecmp(name
, "compression"))
347 compression
= !strcasecmp(value
, "true") ||
348 !strcasecmp(value
, "yes") ||
349 !strcasecmp(value
, "on") ||
350 !strcasecmp(value
, "gzip");
352 #endif /* HAVE_LIBZ */
359 fprintf(stderr
, "ERROR: Unknown option \"%s\" with value \"%s\"!\n",
366 * If we have 7 arguments, print the file named on the command-line.
367 * Otherwise, copy stdin to a temporary file and print the temporary
374 * Copy stdin to a temporary file...
377 int fd
; /* File descriptor */
378 cups_file_t
*fp
; /* Temporary file */
379 char buffer
[8192]; /* Buffer for copying */
380 int bytes
; /* Number of bytes read */
383 if ((fd
= cupsTempFd(tmpfilename
, sizeof(tmpfilename
))) < 0)
385 perror("ERROR: unable to create temporary file");
386 return (CUPS_BACKEND_FAILED
);
389 if ((fp
= cupsFileOpenFd(fd
, compression
? "w9" : "w")) == NULL
)
391 perror("ERROR: unable to open temporary file");
394 return (CUPS_BACKEND_FAILED
);
397 while ((bytes
= fread(buffer
, 1, sizeof(buffer
), stdin
)) > 0)
398 if (cupsFileWrite(fp
, buffer
, bytes
) < bytes
)
400 perror("ERROR: unable to write to temporary file");
403 return (CUPS_BACKEND_FAILED
);
409 * Point to the single file from stdin...
412 filename
= tmpfilename
;
419 * Point to the files on the command-line...
422 num_files
= argc
- 6;
427 compress_files(num_files
, files
);
428 #endif /* HAVE_LIBZ */
431 fprintf(stderr
, "DEBUG: %d files to send in job...\n", num_files
);
434 * Set the authentication info, if any...
437 cupsSetPasswordCB(password_cb
);
442 * Use authenticaion information in the device URI...
445 if ((password
= strchr(username
, ':')) != NULL
)
448 cupsSetUser(username
);
453 * Try loading authentication information from the a##### file.
456 const char *request_root
; /* CUPS_REQUESTROOT env var */
457 char afilename
[1024], /* a##### filename */
458 aline
[1024]; /* Line from file */
459 FILE *fp
; /* File pointer */
462 if ((request_root
= getenv("CUPS_REQUESTROOT")) != NULL
)
465 * Try opening authentication cache file...
468 snprintf(afilename
, sizeof(afilename
), "%s/a%05d", request_root
,
470 if ((fp
= fopen(afilename
, "r")) != NULL
)
476 if (fgets(aline
, sizeof(aline
), fp
))
482 i
= sizeof(username
);
483 httpDecode64_2(username
, &i
, aline
);
489 if (fgets(aline
, sizeof(aline
), fp
))
495 i
= sizeof(password
);
496 httpDecode64_2(password
, &i
, aline
);
510 * Try connecting to the remote server...
513 fputs("STATE: +connecting-to-device\n", stderr
);
517 fprintf(stderr
, "INFO: Connecting to %s on port %d...\n", hostname
, port
);
519 if ((http
= httpConnectEncrypt(hostname
, port
, cupsEncryption())) == NULL
)
521 if (getenv("CLASS") != NULL
)
524 * If the CLASS environment variable is set, the job was submitted
525 * to a class and not to a specific queue. In this case, we want
526 * to abort immediately so that the job can be requeued on the next
527 * available printer in the class.
531 "INFO: Unable to connect to %s, queuing on next printer in "
535 if (argc
== 6 || strcmp(filename
, argv
[6]))
539 * Sleep 5 seconds to keep the job from requeuing too rapidly...
544 return (CUPS_BACKEND_FAILED
);
547 if (errno
== ECONNREFUSED
|| errno
== EHOSTDOWN
||
548 errno
== EHOSTUNREACH
)
551 "INFO: Network host \'%s\' is busy; will retry in 30 "
558 fprintf(stderr
, "INFO: Unable to lookup host \'%s\' - %s\n",
559 hostname
, hstrerror(h_errno
));
564 perror("ERROR: Unable to connect to IPP host");
569 while (http
== NULL
);
571 fputs("STATE: -connecting-to-device\n", stderr
);
572 fprintf(stderr
, "INFO: Connected to %s...\n", hostname
);
575 * Build a URI for the printer and fill the standard IPP attributes for
576 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
577 * might contain username:password information...
580 snprintf(uri
, sizeof(uri
), "%s://%s:%d%s", method
, hostname
, port
, resource
);
583 * First validate the destination and see if the device supports multiple
584 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
585 * don't support the copies attribute...
595 * Build the IPP request...
598 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
599 request
->request
.op
.version
[1] = version
;
601 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
604 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
605 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
612 fputs("DEBUG: Getting supported attributes...\n", stderr
);
614 if ((supported
= cupsDoRequest(http
, request
, resource
)) == NULL
)
615 ipp_status
= cupsLastError();
617 ipp_status
= supported
->request
.status
.status_code
;
619 if (ipp_status
> IPP_OK_CONFLICT
)
621 if (ipp_status
== IPP_PRINTER_BUSY
||
622 ipp_status
== IPP_SERVICE_UNAVAILABLE
)
624 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr
);
625 report_printer_state(supported
);
628 else if ((ipp_status
== IPP_BAD_REQUEST
||
629 ipp_status
== IPP_VERSION_NOT_SUPPORTED
) && version
== 1)
632 * Switch to IPP/1.0...
635 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n",
640 else if (ipp_status
== IPP_NOT_FOUND
)
642 fputs("ERROR: Destination printer does not exist!\n", stderr
);
645 ippDelete(supported
);
647 return (CUPS_BACKEND_STOP
);
651 fprintf(stderr
, "ERROR: Unable to get printer status (%s)!\n",
652 ippErrorString(ipp_status
));
657 ippDelete(supported
);
661 else if ((copies_sup
= ippFindAttribute(supported
, "copies-supported",
662 IPP_TAG_RANGE
)) != NULL
)
665 * Has the "copies-supported" attribute - does it have an upper
669 if (copies_sup
->values
[0].range
.upper
<= 1)
670 copies_sup
= NULL
; /* No */
673 format_sup
= ippFindAttribute(supported
, "document-format-supported",
678 fprintf(stderr
, "DEBUG: document-format-supported (%d values)\n",
679 format_sup
->num_values
);
680 for (i
= 0; i
< format_sup
->num_values
; i
++)
681 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
682 format_sup
->values
[i
].string
.text
);
685 report_printer_state(supported
);
687 while (ipp_status
> IPP_OK_CONFLICT
);
690 * See if the printer is accepting jobs and is not stopped; if either
691 * condition is true and we are printing to a class, requeue the job...
694 if (getenv("CLASS") != NULL
)
696 printer_state
= ippFindAttribute(supported
, "printer-state",
698 printer_accepting
= ippFindAttribute(supported
, "printer-is-accepting-jobs",
701 if (printer_state
== NULL
||
702 (printer_state
->values
[0].integer
> IPP_PRINTER_PROCESSING
&&
704 printer_accepting
== NULL
||
705 !printer_accepting
->values
[0].boolean
)
708 * If the CLASS environment variable is set, the job was submitted
709 * to a class and not to a specific queue. In this case, we want
710 * to abort immediately so that the job can be requeued on the next
711 * available printer in the class.
715 "INFO: Unable to queue job on %s, queuing on next printer in "
719 ippDelete(supported
);
722 if (argc
== 6 || strcmp(filename
, argv
[6]))
726 * Sleep 5 seconds to keep the job from requeuing too rapidly...
731 return (CUPS_BACKEND_FAILED
);
736 * See if the printer supports multiple copies...
739 if (copies_sup
|| argc
< 7)
742 copies
= atoi(argv
[4]);
745 * Then issue the print-job request...
754 * Build the IPP request...
761 request
= ippNewRequest(IPP_CREATE_JOB
);
763 request
= ippNewRequest(IPP_PRINT_JOB
);
765 request
->request
.op
.version
[1] = version
;
767 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
770 fprintf(stderr
, "DEBUG: printer-uri = \"%s\"\n", uri
);
773 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
774 "requesting-user-name", NULL
, argv
[2]);
776 fprintf(stderr
, "DEBUG: requesting-user-name = \"%s\"\n", argv
[2]);
779 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
782 fprintf(stderr
, "DEBUG: job-name = \"%s\"\n", argv
[3]);
786 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
787 "compression", NULL
, "gzip");
788 #endif /* HAVE_LIBZ */
791 * Handle options on the command-line...
795 num_options
= cupsParseOptions(argv
[5], 0, &options
);
798 if (content_type
!= NULL
&&
799 !strcasecmp(content_type
, "application/pictwps") && num_files
== 1)
801 if (format_sup
!= NULL
)
803 for (i
= 0; i
< format_sup
->num_values
; i
++)
804 if (!strcasecmp(content_type
, format_sup
->values
[i
].string
.text
))
808 if (format_sup
== NULL
|| i
>= format_sup
->num_values
)
811 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
812 * so convert the document to PostScript...
815 if (run_pictwps_filter(argv
, filename
))
816 return (CUPS_BACKEND_FAILED
);
818 filename
= pstmpname
;
821 * Change the MIME type to application/postscript and change the
822 * number of copies to 1...
825 content_type
= "application/postscript";
829 #endif /* __APPLE__ */
831 if (content_type
!= NULL
&& format_sup
!= NULL
)
833 for (i
= 0; i
< format_sup
->num_values
; i
++)
834 if (!strcasecmp(content_type
, format_sup
->values
[i
].string
.text
))
837 if (i
< format_sup
->num_values
)
838 num_options
= cupsAddOption("document-format", content_type
,
839 num_options
, &options
);
845 * Only send options if the destination printer supports the copies
846 * attribute. This is a hack for the HP JetDirect implementation of
847 * IPP, which does not accept extension attributes and incorrectly
848 * reports a client-error-bad-request error instead of the
849 * successful-ok-unsupported-attributes status. In short, at least
850 * some HP implementations of IPP are non-compliant.
853 cupsEncodeOptions(request
, num_options
, options
);
854 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies",
858 cupsFreeOptions(num_options
, options
);
861 * If copies aren't supported, then we are likely dealing with an HP
862 * JetDirect. The HP IPP implementation seems to close the connection
863 * after every request (that is, it does *not* implement HTTP Keep-
864 * Alive, which is REQUIRED by HTTP/1.1...
875 response
= cupsDoRequest(http
, request
, resource
);
877 response
= cupsDoFileRequest(http
, request
, resource
, files
[0]);
879 ipp_status
= cupsLastError();
881 if (ipp_status
> IPP_OK_CONFLICT
)
888 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
889 ipp_status
== IPP_PRINTER_BUSY
)
891 fputs("INFO: Printer is busy; retrying print job...\n", stderr
);
895 fprintf(stderr
, "ERROR: Print file was not accepted (%s)!\n",
896 cupsLastErrorString());
898 else if ((job_id_attr
= ippFindAttribute(response
, "job-id",
899 IPP_TAG_INTEGER
)) == NULL
)
901 fputs("NOTICE: Print file accepted - job ID unknown.\n", stderr
);
906 job_id
= job_id_attr
->values
[0].integer
;
907 fprintf(stderr
, "NOTICE: Print file accepted - job ID %d.\n", job_id
);
915 if (job_id
&& num_files
> 1)
917 for (i
= 0; i
< num_files
; i
++)
919 request
= ippNewRequest(IPP_SEND_DOCUMENT
);
921 request
->request
.op
.version
[1] = version
;
923 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
926 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
930 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
931 "requesting-user-name", NULL
, argv
[2]);
933 if ((i
+ 1) == num_files
)
934 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
936 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
937 "document-format", NULL
, content_type
);
939 ippDelete(cupsDoFileRequest(http
, request
, resource
, files
[i
]));
941 if (cupsLastError() > IPP_OK_CONFLICT
)
943 ipp_status
= cupsLastError();
945 fprintf(stderr
, "ERROR: Unable to add file %d to job: %s\n",
946 job_id
, cupsLastErrorString());
952 if (ipp_status
<= IPP_OK_CONFLICT
&& argc
> 6)
954 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
957 else if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
958 ipp_status
== IPP_PRINTER_BUSY
)
964 * Wait for the job to complete...
967 if (!job_id
|| !waitjob
)
970 fputs("INFO: Waiting for job to complete...\n", stderr
);
972 for (; !job_cancelled
;)
975 * Build an IPP_GET_JOB_ATTRIBUTES request...
978 request
= ippNewRequest(IPP_GET_JOB_ATTRIBUTES
);
979 request
->request
.op
.version
[1] = version
;
981 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
984 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
988 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
989 "requesting-user-name", NULL
, argv
[2]);
991 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
992 "requested-attributes", sizeof(jattrs
) / sizeof(jattrs
[0]),
1000 httpReconnect(http
);
1002 response
= cupsDoRequest(http
, request
, resource
);
1003 ipp_status
= cupsLastError();
1005 if (ipp_status
== IPP_NOT_FOUND
)
1008 * Job has gone away and/or the server has no job history...
1011 ippDelete(response
);
1013 ipp_status
= IPP_OK
;
1017 if (ipp_status
> IPP_OK_CONFLICT
)
1019 if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
1020 ipp_status
!= IPP_PRINTER_BUSY
)
1022 ippDelete(response
);
1024 fprintf(stderr
, "ERROR: Unable to get job %d attributes (%s)!\n",
1025 job_id
, ippErrorString(ipp_status
));
1032 if ((job_state
= ippFindAttribute(response
, "job-state",
1033 IPP_TAG_ENUM
)) != NULL
)
1036 * Stop polling if the job is finished or pending-held...
1039 if (job_state
->values
[0].integer
> IPP_JOB_PROCESSING
||
1040 job_state
->values
[0].integer
== IPP_JOB_HELD
)
1042 if ((job_sheets
= ippFindAttribute(response
,
1043 "job-media-sheets-completed",
1044 IPP_TAG_INTEGER
)) != NULL
)
1045 fprintf(stderr
, "PAGE: total %d\n",
1046 job_sheets
->values
[0].integer
);
1048 ippDelete(response
);
1054 ippDelete(response
);
1057 * Check the printer state and report it if necessary...
1060 check_printer_state(http
, uri
, resource
, argv
[2], version
);
1063 * Wait 10 seconds before polling again...
1071 * Cancel the job as needed...
1074 if (job_cancelled
&& job_id
)
1075 cancel_job(http
, uri
, job_id
, resource
, argv
[2], version
);
1078 * Check the printer state and report it if necessary...
1081 check_printer_state(http
, uri
, resource
, argv
[2], version
);
1089 ippDelete(supported
);
1092 * Remove the temporary file(s) if necessary...
1096 unlink(tmpfilename
);
1101 for (i
= 0; i
< num_files
; i
++)
1104 #endif /* HAVE_LIBZ */
1109 #endif /* __APPLE__ */
1112 * Return the queue status...
1115 return (ipp_status
> IPP_OK_CONFLICT
? CUPS_BACKEND_FAILED
: CUPS_BACKEND_OK
);
1120 * 'cancel_job()' - Cancel a print job.
1124 cancel_job(http_t
*http
, /* I - HTTP connection */
1125 const char *uri
, /* I - printer-uri */
1126 int id
, /* I - job-id */
1127 const char *resource
, /* I - Resource path */
1128 const char *user
, /* I - requesting-user-name */
1129 int version
) /* I - IPP version */
1131 ipp_t
*request
; /* Cancel-Job request */
1134 fputs("INFO: Cancelling print job...\n", stderr
);
1136 request
= ippNewRequest(IPP_CANCEL_JOB
);
1137 request
->request
.op
.version
[1] = version
;
1139 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1141 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1143 if (user
&& user
[0])
1144 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1145 "requesting-user-name", NULL
, user
);
1151 ippDelete(cupsDoRequest(http
, request
, resource
));
1153 if (cupsLastError() > IPP_OK_CONFLICT
)
1154 fprintf(stderr
, "ERROR: Unable to cancel job %d: %s\n", id
,
1155 cupsLastErrorString());
1160 * 'check_printer_state()' - Check the printer state...
1164 check_printer_state(
1165 http_t
*http
, /* I - HTTP connection */
1166 const char *uri
, /* I - Printer URI */
1167 const char *resource
, /* I - Resource path */
1168 const char *user
, /* I - Username, if any */
1169 int version
) /* I - IPP version */
1171 ipp_t
*request
, /* IPP request */
1172 *response
; /* IPP response */
1176 * Check on the printer state...
1179 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1180 request
->request
.op
.version
[1] = version
;
1182 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1185 if (user
&& user
[0])
1186 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1187 "requesting-user-name", NULL
, user
);
1189 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1190 "requested-attributes", NULL
, "printer-state-reasons");
1196 if ((response
= cupsDoRequest(http
, request
, resource
)) != NULL
)
1198 report_printer_state(response
);
1199 ippDelete(response
);
1206 * 'compress_files()' - Compress print files...
1210 compress_files(int num_files
, /* I - Number of files */
1211 char **files
) /* I - Files */
1213 int i
, /* Looping var */
1214 fd
; /* Temporary file descriptor */
1215 ssize_t bytes
; /* Bytes read/written */
1216 size_t total
; /* Total bytes read */
1217 cups_file_t
*in
, /* Input file */
1218 *out
; /* Output file */
1219 struct stat outinfo
; /* Output file information */
1220 char filename
[1024], /* Temporary filename */
1221 buffer
[32768]; /* Copy buffer */
1224 fprintf(stderr
, "DEBUG: Compressing %d job files...\n", num_files
);
1225 for (i
= 0; i
< num_files
; i
++)
1227 if ((fd
= cupsTempFd(filename
, sizeof(filename
))) < 0)
1229 perror("ERROR: Unable to create temporary compressed print file");
1230 exit(CUPS_BACKEND_FAILED
);
1233 if ((out
= cupsFileOpenFd(fd
, "w9")) == NULL
)
1235 perror("ERROR: Unable to open temporary compressed print file");
1236 exit(CUPS_BACKEND_FAILED
);
1239 if ((in
= cupsFileOpen(files
[i
], "r")) == NULL
)
1241 fprintf(stderr
, "ERROR: Unable to open print file \"%s\": %s\n",
1242 files
[i
], strerror(errno
));
1244 exit(CUPS_BACKEND_FAILED
);
1248 while ((bytes
= cupsFileRead(in
, buffer
, sizeof(buffer
))) > 0)
1249 if (cupsFileWrite(out
, buffer
, bytes
) < bytes
)
1251 fprintf(stderr
, "ERROR: Unable to write " CUPS_LLFMT
" bytes to \"%s\": %s\n",
1252 CUPS_LLCAST bytes
, filename
, strerror(errno
));
1255 exit(CUPS_BACKEND_FAILED
);
1263 files
[i
] = strdup(filename
);
1265 if (!stat(filename
, &outinfo
))
1267 "DEBUG: File %d compressed to %.1f%% of original size, "
1268 CUPS_LLFMT
" bytes...\n",
1269 i
+ 1, 100.0 * outinfo
.st_size
/ total
,
1270 CUPS_LLCAST outinfo
.st_size
);
1273 #endif /* HAVE_LIBZ */
1277 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1280 static const char * /* O - Password */
1281 password_cb(const char *prompt
) /* I - Prompt (not used) */
1290 * If there is no password set in the device URI, return the
1291 * "authentication required" exit code...
1295 unlink(tmpfilename
);
1300 #endif /* __APPLE__ */
1302 exit(CUPS_BACKEND_AUTH_REQUIRED
);
1308 * 'report_printer_state()' - Report the printer state.
1311 static int /* O - Number of reasons shown */
1312 report_printer_state(ipp_t
*ipp
) /* I - IPP response */
1314 int i
; /* Looping var */
1315 int count
; /* Count of reasons shown... */
1316 ipp_attribute_t
*reasons
; /* printer-state-reasons */
1317 const char *reason
; /* Current reason */
1318 const char *message
; /* Message to show */
1319 char unknown
[1024]; /* Unknown message string */
1320 const char *prefix
; /* Prefix for STATE: line */
1321 char state
[1024]; /* State string */
1324 if ((reasons
= ippFindAttribute(ipp
, "printer-state-reasons",
1325 IPP_TAG_KEYWORD
)) == NULL
)
1331 for (i
= 0, count
= 0; i
< reasons
->num_values
; i
++)
1333 reason
= reasons
->values
[i
].string
.text
;
1335 strlcat(state
, prefix
, sizeof(state
));
1336 strlcat(state
, reason
, sizeof(state
));
1341 if (!strncmp(reason
, "media-needed", 12))
1342 message
= "Media tray needs to be filled.";
1343 else if (!strncmp(reason
, "media-jam", 9))
1344 message
= "Media jam!";
1345 else if (!strncmp(reason
, "moving-to-paused", 16) ||
1346 !strncmp(reason
, "paused", 6) ||
1347 !strncmp(reason
, "shutdown", 8))
1348 message
= "Printer off-line.";
1349 else if (!strncmp(reason
, "toner-low", 9))
1350 message
= "Toner low.";
1351 else if (!strncmp(reason
, "toner-empty", 11))
1352 message
= "Out of toner!";
1353 else if (!strncmp(reason
, "cover-open", 10))
1354 message
= "Cover open.";
1355 else if (!strncmp(reason
, "interlock-open", 14))
1356 message
= "Interlock open.";
1357 else if (!strncmp(reason
, "door-open", 9))
1358 message
= "Door open.";
1359 else if (!strncmp(reason
, "input-tray-missing", 18))
1360 message
= "Media tray missing!";
1361 else if (!strncmp(reason
, "media-low", 9))
1362 message
= "Media tray almost empty.";
1363 else if (!strncmp(reason
, "media-empty", 11))
1364 message
= "Media tray empty!";
1365 else if (!strncmp(reason
, "output-tray-missing", 19))
1366 message
= "Output tray missing!";
1367 else if (!strncmp(reason
, "output-area-almost-full", 23))
1368 message
= "Output bin almost full.";
1369 else if (!strncmp(reason
, "output-area-full", 16))
1370 message
= "Output bin full!";
1371 else if (!strncmp(reason
, "marker-supply-low", 17))
1372 message
= "Ink/toner almost empty.";
1373 else if (!strncmp(reason
, "marker-supply-empty", 19))
1374 message
= "Ink/toner empty!";
1375 else if (!strncmp(reason
, "marker-waste-almost-full", 24))
1376 message
= "Ink/toner waste bin almost full.";
1377 else if (!strncmp(reason
, "marker-waste-full", 17))
1378 message
= "Ink/toner waste bin full!";
1379 else if (!strncmp(reason
, "fuser-over-temp", 15))
1380 message
= "Fuser temperature high!";
1381 else if (!strncmp(reason
, "fuser-under-temp", 16))
1382 message
= "Fuser temperature low!";
1383 else if (!strncmp(reason
, "opc-near-eol", 12))
1384 message
= "OPC almost at end-of-life.";
1385 else if (!strncmp(reason
, "opc-life-over", 13))
1386 message
= "OPC at end-of-life!";
1387 else if (!strncmp(reason
, "developer-low", 13))
1388 message
= "Developer almost empty.";
1389 else if (!strncmp(reason
, "developer-empty", 15))
1390 message
= "Developer empty!";
1391 else if (strstr(reason
, "error") != NULL
)
1395 snprintf(unknown
, sizeof(unknown
), "Unknown printer error (%s)!",
1402 if (strstr(reasons
->values
[i
].string
.text
, "error"))
1403 fprintf(stderr
, "ERROR: %s\n", message
);
1404 else if (strstr(reasons
->values
[i
].string
.text
, "warning"))
1405 fprintf(stderr
, "WARNING: %s\n", message
);
1407 fprintf(stderr
, "INFO: %s\n", message
);
1411 fprintf(stderr
, "%s\n", state
);
1419 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1422 * This step is required because the PICT format is not documented and
1423 * subject to change, so developing a filter for other OS's is infeasible.
1424 * Also, fonts required by the PICT file need to be embedded on the
1425 * client side (which has the fonts), so we run the filter to get a
1426 * PostScript file for printing...
1429 static int /* O - Exit status of filter */
1430 run_pictwps_filter(char **argv
, /* I - Command-line arguments */
1431 const char *filename
)/* I - Filename */
1433 struct stat fileinfo
; /* Print file information */
1434 const char *ppdfile
; /* PPD file for destination printer */
1435 int pid
; /* Child process ID */
1436 int fd
; /* Temporary file descriptor */
1437 int status
; /* Exit status of filter */
1438 const char *printer
; /* PRINTER env var */
1439 static char ppdenv
[1024]; /* PPD environment variable */
1443 * First get the PPD file for the printer...
1446 printer
= getenv("PRINTER");
1449 fputs("ERROR: PRINTER environment variable not defined!\n", stderr
);
1453 if ((ppdfile
= cupsGetPPD(printer
)) == NULL
)
1455 fprintf(stderr
, "ERROR: Unable to get PPD file for printer \"%s\" - %s.\n",
1456 printer
, ippErrorString(cupsLastError()));
1461 snprintf(ppdenv
, sizeof(ppdenv
), "PPD=%s", ppdfile
);
1466 * Then create a temporary file for printing...
1469 if ((fd
= cupsTempFd(pstmpname
, sizeof(pstmpname
))) < 0)
1471 fprintf(stderr
, "ERROR: Unable to create temporary file - %s.\n",
1479 * Get the owner of the spool file - it is owned by the user we want to run
1484 stat(argv
[6], &fileinfo
);
1488 * Use the OSX defaults, as an up-stream filter created the PICT
1492 fileinfo
.st_uid
= 1;
1493 fileinfo
.st_gid
= 80;
1497 chown(ppdfile
, fileinfo
.st_uid
, fileinfo
.st_gid
);
1499 fchown(fd
, fileinfo
.st_uid
, fileinfo
.st_gid
);
1502 * Finally, run the filter to convert the file...
1505 if ((pid
= fork()) == 0)
1508 * Child process for pictwpstops... Redirect output of pictwpstops to a
1519 * Change to an unpriviledged user...
1522 setgid(fileinfo
.st_gid
);
1523 setuid(fileinfo
.st_uid
);
1526 execlp("pictwpstops", printer
, argv
[1], argv
[2], argv
[3], argv
[4], argv
[5],
1528 perror("ERROR: Unable to exec pictwpstops");
1540 perror("ERROR: Unable to fork pictwpstops");
1548 * Now wait for the filter to complete...
1551 if (wait(&status
) < 0)
1553 perror("ERROR: Unable to wait for pictwpstops");
1569 fprintf(stderr
, "ERROR: pictwpstops exited with status %d!\n",
1572 fprintf(stderr
, "ERROR: pictwpstops exited on signal %d!\n",
1580 * Return with no errors..
1585 #endif /* __APPLE__ */
1589 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1593 sigterm_handler(int sig
) /* I - Signal */
1595 (void)sig
; /* remove compiler warnings... */
1600 * Flag that the job should be cancelled...
1608 * The scheduler already tried to cancel us once, now just terminate
1609 * after removing our temp files!
1613 unlink(tmpfilename
);
1618 #endif /* __APPLE__ */
1625 * End of "$Id: ipp.c 5724 2006-07-12 19:42:35Z mike $".