2 * "$Id: ipp.c 7583 2008-05-16 17:47:16Z mike $"
4 * IPP backend for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2008 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * "LICENSE" which should have been included with this file. If this
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * main() - Send a file to the printer or server.
20 * cancel_job() - Cancel a print job.
21 * check_printer_state() - Check the printer state...
22 * compress_files() - Compress print files...
23 * password_cb() - Disable the password prompt for
24 * cupsDoFileRequest().
25 * report_printer_state() - Report the printer state.
26 * run_pictwps_filter() - Convert PICT files to PostScript when printing
28 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
32 * Include necessary headers.
35 #include <cups/http-private.h>
36 #include "backend-private.h"
37 #include <sys/types.h>
45 static char *password
= NULL
; /* Password for device URI */
46 static int password_tries
= 0; /* Password tries */
48 static char pstmpname
[1024] = ""; /* Temporary PostScript file name */
49 #endif /* __APPLE__ */
50 static char tmpfilename
[1024] = ""; /* Temporary spool file name */
51 static int job_cancelled
= 0; /* Job cancelled? */
58 static void cancel_job(http_t
*http
, const char *uri
, int id
,
59 const char *resource
, const char *user
, int version
);
60 static void check_printer_state(http_t
*http
, const char *uri
,
61 const char *resource
, const char *user
,
62 int version
, int job_id
);
64 static void compress_files(int num_files
, char **files
);
65 #endif /* HAVE_LIBZ */
66 static const char *password_cb(const char *);
67 static int report_printer_state(ipp_t
*ipp
, int job_id
);
70 static int run_pictwps_filter(char **argv
, const char *filename
);
71 #endif /* __APPLE__ */
72 static void sigterm_handler(int sig
);
76 * 'main()' - Send a file to the printer or server.
80 * printer-uri job-id user title copies options [file]
83 int /* O - Exit status */
84 main(int argc
, /* I - Number of command-line args */
85 char *argv
[]) /* I - Command-line arguments */
87 int i
; /* Looping var */
88 int send_options
; /* Send job options? */
89 int num_options
; /* Number of printer options */
90 cups_option_t
*options
; /* Printer options */
91 char method
[255], /* Method in URI */
92 hostname
[1024], /* Hostname */
93 username
[255], /* Username info */
94 resource
[1024], /* Resource info (printer name) */
95 addrname
[256], /* Address name */
96 *optptr
, /* Pointer to URI options */
97 *name
, /* Name of option */
98 *value
, /* Value of option */
99 sep
; /* Separator character */
100 int snmp_fd
, /* SNMP socket */
101 start_count
, /* Page count via SNMP at start */
102 page_count
; /* Page count via SNMP */
103 int num_files
; /* Number of files to print */
104 char **files
, /* Files to print */
105 *filename
; /* Pointer to single filename */
106 int port
; /* Port number (not used) */
107 char uri
[HTTP_MAX_URI
]; /* Updated URI without user/pass */
108 ipp_status_t ipp_status
; /* Status of IPP request */
109 http_t
*http
; /* HTTP connection */
110 ipp_t
*request
, /* IPP request */
111 *response
, /* IPP response */
112 *supported
; /* get-printer-attributes response */
113 time_t start_time
; /* Time of first connect */
114 int recoverable
; /* Recoverable error shown? */
115 int contimeout
; /* Connection timeout */
116 int delay
; /* Delay for retries... */
117 int compression
, /* Do compression of the job data? */
118 waitjob
, /* Wait for job complete? */
119 waitprinter
; /* Wait for printer ready? */
120 ipp_attribute_t
*job_id_attr
; /* job-id attribute */
121 int job_id
; /* job-id value */
122 ipp_attribute_t
*job_sheets
; /* job-media-sheets-completed */
123 ipp_attribute_t
*job_state
; /* job-state */
124 ipp_attribute_t
*copies_sup
; /* copies-supported */
125 ipp_attribute_t
*format_sup
; /* document-format-supported */
126 ipp_attribute_t
*printer_state
; /* printer-state attribute */
127 ipp_attribute_t
*printer_accepting
; /* printer-is-accepting-jobs */
128 int copies
, /* Number of copies for job */
129 copies_remaining
; /* Number of copies remaining */
130 const char *content_type
, /* CONTENT_TYPE environment variable */
131 *final_content_type
; /* FINAL_CONTENT_TYPE environment var */
132 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
133 struct sigaction action
; /* Actions for POSIX signals */
134 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
135 int version
; /* IPP version */
136 static const char * const pattrs
[] =
137 { /* Printer attributes we want */
139 "document-format-supported",
140 "printer-is-accepting-jobs",
142 "printer-state-message",
143 "printer-state-reasons",
145 static const char * const jattrs
[] =
146 { /* Job attributes we want */
147 "job-media-sheets-completed",
153 * Make sure status messages are not buffered...
156 setbuf(stderr
, NULL
);
159 * Ignore SIGPIPE and catch SIGTERM signals...
163 sigset(SIGPIPE
, SIG_IGN
);
164 sigset(SIGTERM
, sigterm_handler
);
165 #elif defined(HAVE_SIGACTION)
166 memset(&action
, 0, sizeof(action
));
167 action
.sa_handler
= SIG_IGN
;
168 sigaction(SIGPIPE
, &action
, NULL
);
170 sigemptyset(&action
.sa_mask
);
171 sigaddset(&action
.sa_mask
, SIGTERM
);
172 action
.sa_handler
= sigterm_handler
;
173 sigaction(SIGTERM
, &action
, NULL
);
175 signal(SIGPIPE
, SIG_IGN
);
176 signal(SIGTERM
, sigterm_handler
);
177 #endif /* HAVE_SIGSET */
180 * Check command-line...
187 if ((s
= strrchr(argv
[0], '/')) != NULL
)
192 printf("network %s \"Unknown\" \"Internet Printing Protocol (%s)\"\n",
194 return (CUPS_BACKEND_OK
);
198 _cupsLangPrintf(stderr
,
199 _("Usage: %s job-id user title copies options [file]\n"),
201 return (CUPS_BACKEND_STOP
);
205 * Get the (final) content type...
208 if ((content_type
= getenv("CONTENT_TYPE")) == NULL
)
209 content_type
= "application/octet-stream";
211 if ((final_content_type
= getenv("FINAL_CONTENT_TYPE")) == NULL
)
213 final_content_type
= content_type
;
215 if (!strncmp(final_content_type
, "printer/", 8))
216 final_content_type
= "application/vnd.cups-raw";
220 * Extract the hostname and printer name from the URI...
223 if (httpSeparateURI(HTTP_URI_CODING_ALL
, cupsBackendDeviceURI(argv
),
224 method
, sizeof(method
), username
, sizeof(username
),
225 hostname
, sizeof(hostname
), &port
,
226 resource
, sizeof(resource
)) < HTTP_URI_OK
)
228 _cupsLangPuts(stderr
,
229 _("ERROR: Missing device URI on command-line and no "
230 "DEVICE_URI environment variable!\n"));
231 return (CUPS_BACKEND_STOP
);
235 port
= IPP_PORT
; /* Default to port 631 */
237 if (!strcmp(method
, "https"))
238 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
240 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
243 * See if there are any options...
250 contimeout
= 7 * 24 * 60 * 60;
252 if ((optptr
= strchr(resource
, '?')) != NULL
)
255 * Yup, terminate the device name string and move to the first
256 * character of the optptr...
262 * Then parse the optptr...
273 while (*optptr
&& *optptr
!= '=' && *optptr
!= '+' && *optptr
!= '&')
276 if ((sep
= *optptr
) != '\0')
287 while (*optptr
&& *optptr
!= '+' && *optptr
!= '&')
297 * Process the option...
300 if (!strcasecmp(name
, "waitjob"))
303 * Wait for job completion?
306 waitjob
= !strcasecmp(value
, "on") ||
307 !strcasecmp(value
, "yes") ||
308 !strcasecmp(value
, "true");
310 else if (!strcasecmp(name
, "waitprinter"))
313 * Wait for printer idle?
316 waitprinter
= !strcasecmp(value
, "on") ||
317 !strcasecmp(value
, "yes") ||
318 !strcasecmp(value
, "true");
320 else if (!strcasecmp(name
, "encryption"))
323 * Enable/disable encryption?
326 if (!strcasecmp(value
, "always"))
327 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS
);
328 else if (!strcasecmp(value
, "required"))
329 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED
);
330 else if (!strcasecmp(value
, "never"))
331 cupsSetEncryption(HTTP_ENCRYPT_NEVER
);
332 else if (!strcasecmp(value
, "ifrequested"))
333 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED
);
336 _cupsLangPrintf(stderr
,
337 _("ERROR: Unknown encryption option value \"%s\"!\n"),
341 else if (!strcasecmp(name
, "version"))
343 if (!strcmp(value
, "1.0"))
345 else if (!strcmp(value
, "1.1"))
349 _cupsLangPrintf(stderr
,
350 _("ERROR: Unknown version option value \"%s\"!\n"),
355 else if (!strcasecmp(name
, "compression"))
357 compression
= !strcasecmp(value
, "true") ||
358 !strcasecmp(value
, "yes") ||
359 !strcasecmp(value
, "on") ||
360 !strcasecmp(value
, "gzip");
362 #endif /* HAVE_LIBZ */
363 else if (!strcasecmp(name
, "contimeout"))
366 * Set the connection timeout...
370 contimeout
= atoi(value
);
378 _cupsLangPrintf(stderr
,
379 _("ERROR: Unknown option \"%s\" with value \"%s\"!\n"),
386 * If we have 7 arguments, print the file named on the command-line.
387 * Otherwise, copy stdin to a temporary file and print the temporary
394 * Copy stdin to a temporary file...
397 int fd
; /* File descriptor */
398 cups_file_t
*fp
; /* Temporary file */
399 char buffer
[8192]; /* Buffer for copying */
400 int bytes
; /* Number of bytes read */
403 if ((fd
= cupsTempFd(tmpfilename
, sizeof(tmpfilename
))) < 0)
405 _cupsLangPrintError(_("ERROR: Unable to create temporary file"));
406 return (CUPS_BACKEND_FAILED
);
409 if ((fp
= cupsFileOpenFd(fd
, compression
? "w9" : "w")) == NULL
)
411 _cupsLangPrintError(_("ERROR: Unable to open temporary file"));
414 return (CUPS_BACKEND_FAILED
);
417 while ((bytes
= fread(buffer
, 1, sizeof(buffer
), stdin
)) > 0)
418 if (cupsFileWrite(fp
, buffer
, bytes
) < bytes
)
420 _cupsLangPrintError(_("ERROR: Unable to write to temporary file"));
423 return (CUPS_BACKEND_FAILED
);
429 * Point to the single file from stdin...
432 filename
= tmpfilename
;
440 * Point to the files on the command-line...
443 num_files
= argc
- 6;
449 compress_files(num_files
, files
);
450 #endif /* HAVE_LIBZ */
453 fprintf(stderr
, "DEBUG: %d files to send in job...\n", num_files
);
456 * Set the authentication info, if any...
459 cupsSetPasswordCB(password_cb
);
464 * Use authenticaion information in the device URI...
467 if ((password
= strchr(username
, ':')) != NULL
)
470 cupsSetUser(username
);
475 * Try loading authentication information from the environment.
478 const char *ptr
= getenv("AUTH_USERNAME");
483 password
= getenv("AUTH_PASSWORD");
487 * Try connecting to the remote server...
492 start_time
= time(NULL
);
494 fputs("STATE: +connecting-to-device\n", stderr
);
498 _cupsLangPrintf(stderr
, _("INFO: Connecting to %s on port %d...\n"),
501 if ((http
= httpConnectEncrypt(hostname
, port
, cupsEncryption())) == NULL
)
506 if (getenv("CLASS") != NULL
)
509 * If the CLASS environment variable is set, the job was submitted
510 * to a class and not to a specific queue. In this case, we want
511 * to abort immediately so that the job can be requeued on the next
512 * available printer in the class.
515 _cupsLangPuts(stderr
,
516 _("INFO: Unable to contact printer, queuing on next "
517 "printer in class...\n"));
523 * Sleep 5 seconds to keep the job from requeuing too rapidly...
528 return (CUPS_BACKEND_FAILED
);
531 if (errno
== ECONNREFUSED
|| errno
== EHOSTDOWN
||
532 errno
== EHOSTUNREACH
)
534 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
536 _cupsLangPuts(stderr
, _("ERROR: Printer not responding!\n"));
537 return (CUPS_BACKEND_FAILED
);
542 _cupsLangPrintf(stderr
,
543 _("WARNING: recoverable: Network host \'%s\' is busy; "
544 "will retry in %d seconds...\n"),
554 _cupsLangPrintf(stderr
, _("ERROR: Unable to locate printer \'%s\'!\n"),
556 return (CUPS_BACKEND_STOP
);
562 fprintf(stderr
, "DEBUG: Connection error: %s\n", strerror(errno
));
563 _cupsLangPuts(stderr
,
564 _("ERROR: recoverable: Unable to connect to printer; will "
565 "retry in 30 seconds...\n"));
573 while (http
== NULL
);
580 return (CUPS_BACKEND_FAILED
);
583 fputs("STATE: -connecting-to-device\n", stderr
);
584 _cupsLangPrintf(stderr
, _("INFO: Connected to %s...\n"), hostname
);
587 if (http
->hostaddr
->addr
.sa_family
== AF_INET6
)
588 fprintf(stderr
, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
589 httpAddrString(http
->hostaddr
, addrname
, sizeof(addrname
)),
590 ntohs(http
->hostaddr
->ipv6
.sin6_port
));
592 #endif /* AF_INET6 */
593 if (http
->hostaddr
->addr
.sa_family
== AF_INET
)
594 fprintf(stderr
, "DEBUG: Connected to %s:%d (IPv4)...\n",
595 httpAddrString(http
->hostaddr
, addrname
, sizeof(addrname
)),
596 ntohs(http
->hostaddr
->ipv4
.sin_port
));
599 * See if the printer supports SNMP...
602 if ((snmp_fd
= _cupsSNMPOpen(http
->hostaddr
->addr
.sa_family
)) >= 0)
603 if (backendSNMPSupplies(snmp_fd
, http
->hostaddr
, &start_count
, NULL
))
609 _cupsSNMPClose(snmp_fd
);
614 * Build a URI for the printer and fill the standard IPP attributes for
615 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
616 * might contain username:password information...
619 snprintf(uri
, sizeof(uri
), "%s://%s:%d%s", method
, hostname
, port
, resource
);
622 * First validate the destination and see if the device supports multiple
623 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
624 * don't support the copies attribute...
634 * Check for side-channel requests...
637 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
640 * Build the IPP request...
643 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
644 request
->request
.op
.version
[1] = version
;
646 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
649 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
650 "requested-attributes", sizeof(pattrs
) / sizeof(pattrs
[0]),
657 fputs("DEBUG: Getting supported attributes...\n", stderr
);
659 if ((supported
= cupsDoRequest(http
, request
, resource
)) == NULL
)
660 ipp_status
= cupsLastError();
662 ipp_status
= supported
->request
.status
.status_code
;
664 if (ipp_status
> IPP_OK_CONFLICT
)
666 if (ipp_status
== IPP_PRINTER_BUSY
||
667 ipp_status
== IPP_SERVICE_UNAVAILABLE
)
669 if (contimeout
&& (time(NULL
) - start_time
) > contimeout
)
671 _cupsLangPuts(stderr
, _("ERROR: Printer not responding!\n"));
672 return (CUPS_BACKEND_FAILED
);
677 _cupsLangPrintf(stderr
,
678 _("WARNING: recoverable: Network host \'%s\' is busy; "
679 "will retry in %d seconds...\n"),
682 report_printer_state(supported
, 0);
689 else if ((ipp_status
== IPP_BAD_REQUEST
||
690 ipp_status
== IPP_VERSION_NOT_SUPPORTED
) && version
== 1)
693 * Switch to IPP/1.0...
696 _cupsLangPuts(stderr
,
697 _("INFO: Printer does not support IPP/1.1, trying "
702 else if (ipp_status
== IPP_NOT_FOUND
)
704 _cupsLangPuts(stderr
, _("ERROR: Destination printer does not exist!\n"));
707 ippDelete(supported
);
709 return (CUPS_BACKEND_STOP
);
713 _cupsLangPrintf(stderr
,
714 _("ERROR: Unable to get printer status (%s)!\n"),
715 cupsLastErrorString());
720 ippDelete(supported
);
724 else if ((copies_sup
= ippFindAttribute(supported
, "copies-supported",
725 IPP_TAG_RANGE
)) != NULL
)
728 * Has the "copies-supported" attribute - does it have an upper
732 if (copies_sup
->values
[0].range
.upper
<= 1)
733 copies_sup
= NULL
; /* No */
736 format_sup
= ippFindAttribute(supported
, "document-format-supported",
741 fprintf(stderr
, "DEBUG: document-format-supported (%d values)\n",
742 format_sup
->num_values
);
743 for (i
= 0; i
< format_sup
->num_values
; i
++)
744 fprintf(stderr
, "DEBUG: [%d] = \"%s\"\n", i
,
745 format_sup
->values
[i
].string
.text
);
748 report_printer_state(supported
, 0);
750 while (ipp_status
> IPP_OK_CONFLICT
);
753 * See if the printer is accepting jobs and is not stopped; if either
754 * condition is true and we are printing to a class, requeue the job...
757 if (getenv("CLASS") != NULL
)
759 printer_state
= ippFindAttribute(supported
, "printer-state",
761 printer_accepting
= ippFindAttribute(supported
, "printer-is-accepting-jobs",
764 if (printer_state
== NULL
||
765 (printer_state
->values
[0].integer
> IPP_PRINTER_PROCESSING
&&
767 printer_accepting
== NULL
||
768 !printer_accepting
->values
[0].boolean
)
771 * If the CLASS environment variable is set, the job was submitted
772 * to a class and not to a specific queue. In this case, we want
773 * to abort immediately so that the job can be requeued on the next
774 * available printer in the class.
777 _cupsLangPuts(stderr
,
778 _("INFO: Unable to contact printer, queuing on next "
779 "printer in class...\n"));
781 ippDelete(supported
);
788 * Sleep 5 seconds to keep the job from requeuing too rapidly...
793 return (CUPS_BACKEND_FAILED
);
800 * If we've shown a recoverable error make sure the printer proxies
801 * have a chance to see the recovered message. Not pretty but
802 * necessary for now...
805 fputs("INFO: recovered: \n", stderr
);
810 * See if the printer supports multiple copies...
813 copies
= atoi(argv
[4]);
815 if (copies_sup
|| argc
< 7)
817 copies_remaining
= 1;
823 copies_remaining
= copies
;
826 * Then issue the print-job request...
831 while (copies_remaining
> 0)
834 * Check for side-channel requests...
837 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
840 * Build the IPP request...
847 request
= ippNewRequest(IPP_CREATE_JOB
);
849 request
= ippNewRequest(IPP_PRINT_JOB
);
851 request
->request
.op
.version
[1] = version
;
853 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
856 fprintf(stderr
, "DEBUG: printer-uri = \"%s\"\n", uri
);
859 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
860 "requesting-user-name", NULL
, argv
[2]);
862 fprintf(stderr
, "DEBUG: requesting-user-name = \"%s\"\n", argv
[2]);
865 * Only add a "job-name" attribute if the remote server supports
866 * copy generation - some IPP implementations like HP's don't seem
867 * to like UTF-8 job names (STR #1837)...
870 if (argv
[3][0] && copies_sup
)
871 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
,
874 fprintf(stderr
, "DEBUG: job-name = \"%s\"\n", argv
[3]);
878 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
879 "compression", NULL
, "gzip");
880 #endif /* HAVE_LIBZ */
883 * Handle options on the command-line...
887 num_options
= cupsParseOptions(argv
[5], 0, &options
);
890 if (!strcasecmp(final_content_type
, "application/pictwps") &&
893 if (format_sup
!= NULL
)
895 for (i
= 0; i
< format_sup
->num_values
; i
++)
896 if (!strcasecmp(final_content_type
, format_sup
->values
[i
].string
.text
))
900 if (format_sup
== NULL
|| i
>= format_sup
->num_values
)
903 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
904 * so convert the document to PostScript...
907 if (run_pictwps_filter(argv
, files
[0]))
915 return (CUPS_BACKEND_FAILED
);
918 files
[0] = pstmpname
;
921 * Change the MIME type to application/postscript and change the
922 * number of copies to 1...
925 final_content_type
= "application/postscript";
927 copies_remaining
= 1;
931 #endif /* __APPLE__ */
933 if (format_sup
!= NULL
)
935 for (i
= 0; i
< format_sup
->num_values
; i
++)
936 if (!strcasecmp(final_content_type
, format_sup
->values
[i
].string
.text
))
939 if (i
< format_sup
->num_values
)
940 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
941 "document-format", NULL
, final_content_type
);
944 if (copies_sup
&& version
> 0 && send_options
)
947 * Only send options if the destination printer supports the copies
948 * attribute and IPP/1.1. This is a hack for the HP and Lexmark
949 * implementations of IPP, which do not accept extension attributes
950 * and incorrectly report a client-error-bad-request error instead of
951 * the successful-ok-unsupported-attributes status. In short, at least
952 * some HP and Lexmark implementations of IPP are non-compliant.
955 cupsEncodeOptions(request
, num_options
, options
);
957 ippAddInteger(request
, IPP_TAG_JOB
, IPP_TAG_INTEGER
, "copies",
961 cupsFreeOptions(num_options
, options
);
964 * If copies aren't supported, then we are likely dealing with an HP
965 * JetDirect. The HP IPP implementation seems to close the connection
966 * after every request - that is, it does *not* implement HTTP Keep-
967 * Alive, which is REQUIRED by HTTP/1.1...
978 response
= cupsDoRequest(http
, request
, resource
);
980 response
= cupsDoFileRequest(http
, request
, resource
, files
[0]);
982 ipp_status
= cupsLastError();
984 if (ipp_status
> IPP_OK_CONFLICT
)
991 if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
992 ipp_status
== IPP_PRINTER_BUSY
)
994 _cupsLangPuts(stderr
,
995 _("INFO: Printer busy; will retry in 10 seconds...\n"));
998 else if ((ipp_status
== IPP_BAD_REQUEST
||
999 ipp_status
== IPP_VERSION_NOT_SUPPORTED
) && version
== 1)
1002 * Switch to IPP/1.0...
1005 _cupsLangPuts(stderr
,
1006 _("INFO: Printer does not support IPP/1.1, trying "
1009 httpReconnect(http
);
1012 _cupsLangPrintf(stderr
, _("ERROR: Print file was not accepted (%s)!\n"),
1013 cupsLastErrorString());
1015 else if ((job_id_attr
= ippFindAttribute(response
, "job-id",
1016 IPP_TAG_INTEGER
)) == NULL
)
1018 _cupsLangPuts(stderr
,
1019 _("NOTICE: Print file accepted - job ID unknown.\n"));
1024 job_id
= job_id_attr
->values
[0].integer
;
1025 _cupsLangPrintf(stderr
, _("NOTICE: Print file accepted - job ID %d.\n"),
1029 ippDelete(response
);
1034 if (job_id
&& num_files
> 1)
1036 for (i
= 0; i
< num_files
; i
++)
1039 * Check for side-channel requests...
1042 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1045 * Send the next file in the job...
1048 request
= ippNewRequest(IPP_SEND_DOCUMENT
);
1050 request
->request
.op
.version
[1] = version
;
1052 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1055 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1059 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1060 "requesting-user-name", NULL
, argv
[2]);
1062 if ((i
+ 1) == num_files
)
1063 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
1065 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
1066 "document-format", NULL
, content_type
);
1068 ippDelete(cupsDoFileRequest(http
, request
, resource
, files
[i
]));
1070 if (cupsLastError() > IPP_OK_CONFLICT
)
1072 ipp_status
= cupsLastError();
1074 _cupsLangPrintf(stderr
,
1075 _("ERROR: Unable to add file %d to job: %s\n"),
1076 job_id
, cupsLastErrorString());
1082 if (ipp_status
<= IPP_OK_CONFLICT
&& argc
> 6)
1084 fprintf(stderr
, "PAGE: 1 %d\n", copies_sup
? atoi(argv
[4]) : 1);
1085 copies_remaining
--;
1087 else if (ipp_status
== IPP_SERVICE_UNAVAILABLE
||
1088 ipp_status
== IPP_PRINTER_BUSY
)
1091 copies_remaining
--;
1094 * Wait for the job to complete...
1097 if (!job_id
|| !waitjob
)
1100 _cupsLangPuts(stderr
, _("INFO: Waiting for job to complete...\n"));
1102 for (delay
= 1; !job_cancelled
;)
1105 * Check for side-channel requests...
1108 backendCheckSideChannel(snmp_fd
, http
->hostaddr
);
1111 * Build an IPP_GET_JOB_ATTRIBUTES request...
1114 request
= ippNewRequest(IPP_GET_JOB_ATTRIBUTES
);
1115 request
->request
.op
.version
[1] = version
;
1117 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1120 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id",
1124 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1125 "requesting-user-name", NULL
, argv
[2]);
1127 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1128 "requested-attributes", sizeof(jattrs
) / sizeof(jattrs
[0]),
1136 httpReconnect(http
);
1138 response
= cupsDoRequest(http
, request
, resource
);
1139 ipp_status
= cupsLastError();
1141 if (ipp_status
== IPP_NOT_FOUND
)
1144 * Job has gone away and/or the server has no job history...
1147 ippDelete(response
);
1149 ipp_status
= IPP_OK
;
1153 if (ipp_status
> IPP_OK_CONFLICT
)
1155 if (ipp_status
!= IPP_SERVICE_UNAVAILABLE
&&
1156 ipp_status
!= IPP_PRINTER_BUSY
)
1158 ippDelete(response
);
1160 _cupsLangPrintf(stderr
,
1161 _("ERROR: Unable to get job %d attributes (%s)!\n"),
1162 job_id
, cupsLastErrorString());
1169 if ((job_state
= ippFindAttribute(response
, "job-state",
1170 IPP_TAG_ENUM
)) != NULL
)
1173 * Stop polling if the job is finished or pending-held...
1176 if (job_state
->values
[0].integer
> IPP_JOB_STOPPED
)
1178 if ((job_sheets
= ippFindAttribute(response
,
1179 "job-media-sheets-completed",
1180 IPP_TAG_INTEGER
)) != NULL
)
1181 fprintf(stderr
, "PAGE: total %d\n",
1182 job_sheets
->values
[0].integer
);
1184 ippDelete(response
);
1190 ippDelete(response
);
1193 * Check the printer state and report it if necessary...
1196 check_printer_state(http
, uri
, resource
, argv
[2], version
, job_id
);
1199 * Wait 1-10 seconds before polling again...
1211 * Cancel the job as needed...
1214 if (job_cancelled
&& job_id
)
1215 cancel_job(http
, uri
, job_id
, resource
, argv
[2], version
);
1218 * Check the printer state and report it if necessary...
1221 check_printer_state(http
, uri
, resource
, argv
[2], version
, job_id
);
1224 * Collect the final page count as needed...
1228 !backendSNMPSupplies(snmp_fd
, http
->hostaddr
, &page_count
, NULL
) &&
1229 page_count
> start_count
)
1230 fprintf(stderr
, "PAGE: total %d\n", page_count
- start_count
);
1238 ippDelete(supported
);
1241 * Remove the temporary file(s) if necessary...
1245 unlink(tmpfilename
);
1250 for (i
= 0; i
< num_files
; i
++)
1253 #endif /* HAVE_LIBZ */
1258 #endif /* __APPLE__ */
1261 * Return the queue status...
1264 if (ipp_status
== IPP_NOT_AUTHORIZED
)
1267 * Authorization failures here mean that we need Kerberos. Username +
1268 * password authentication is handled in the password_cb function.
1271 fputs("ATTR: auth-info-required=negotiate\n", stderr
);
1272 return (CUPS_BACKEND_AUTH_REQUIRED
);
1274 else if (ipp_status
> IPP_OK_CONFLICT
)
1275 return (CUPS_BACKEND_FAILED
);
1277 return (CUPS_BACKEND_OK
);
1282 * 'cancel_job()' - Cancel a print job.
1286 cancel_job(http_t
*http
, /* I - HTTP connection */
1287 const char *uri
, /* I - printer-uri */
1288 int id
, /* I - job-id */
1289 const char *resource
, /* I - Resource path */
1290 const char *user
, /* I - requesting-user-name */
1291 int version
) /* I - IPP version */
1293 ipp_t
*request
; /* Cancel-Job request */
1296 _cupsLangPuts(stderr
, _("INFO: Canceling print job...\n"));
1298 request
= ippNewRequest(IPP_CANCEL_JOB
);
1299 request
->request
.op
.version
[1] = version
;
1301 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1303 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1305 if (user
&& user
[0])
1306 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1307 "requesting-user-name", NULL
, user
);
1313 ippDelete(cupsDoRequest(http
, request
, resource
));
1315 if (cupsLastError() > IPP_OK_CONFLICT
)
1316 _cupsLangPrintf(stderr
, _("ERROR: Unable to cancel job %d: %s\n"), id
,
1317 cupsLastErrorString());
1322 * 'check_printer_state()' - Check the printer state...
1326 check_printer_state(
1327 http_t
*http
, /* I - HTTP connection */
1328 const char *uri
, /* I - Printer URI */
1329 const char *resource
, /* I - Resource path */
1330 const char *user
, /* I - Username, if any */
1331 int version
, /* I - IPP version */
1332 int job_id
) /* I - Current job ID */
1334 ipp_t
*request
, /* IPP request */
1335 *response
; /* IPP response */
1336 static const char * const attrs
[] = /* Attributes we want */
1338 "printer-state-message",
1339 "printer-state-reasons"
1344 * Check on the printer state...
1347 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1348 request
->request
.op
.version
[1] = version
;
1350 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1353 if (user
&& user
[0])
1354 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1355 "requesting-user-name", NULL
, user
);
1357 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1358 "requested-attributes",
1359 (int)(sizeof(attrs
) / sizeof(attrs
[0])), NULL
, attrs
);
1365 if ((response
= cupsDoRequest(http
, request
, resource
)) != NULL
)
1367 report_printer_state(response
, job_id
);
1368 ippDelete(response
);
1375 * 'compress_files()' - Compress print files...
1379 compress_files(int num_files
, /* I - Number of files */
1380 char **files
) /* I - Files */
1382 int i
, /* Looping var */
1383 fd
; /* Temporary file descriptor */
1384 ssize_t bytes
; /* Bytes read/written */
1385 size_t total
; /* Total bytes read */
1386 cups_file_t
*in
, /* Input file */
1387 *out
; /* Output file */
1388 struct stat outinfo
; /* Output file information */
1389 char filename
[1024], /* Temporary filename */
1390 buffer
[32768]; /* Copy buffer */
1393 fprintf(stderr
, "DEBUG: Compressing %d job files...\n", num_files
);
1394 for (i
= 0; i
< num_files
; i
++)
1396 if ((fd
= cupsTempFd(filename
, sizeof(filename
))) < 0)
1398 _cupsLangPrintf(stderr
,
1399 _("ERROR: Unable to create temporary compressed print "
1400 "file: %s\n"), strerror(errno
));
1401 exit(CUPS_BACKEND_FAILED
);
1404 if ((out
= cupsFileOpenFd(fd
, "w9")) == NULL
)
1406 _cupsLangPrintf(stderr
,
1407 _("ERROR: Unable to open temporary compressed print "
1408 "file: %s\n"), strerror(errno
));
1409 exit(CUPS_BACKEND_FAILED
);
1412 if ((in
= cupsFileOpen(files
[i
], "r")) == NULL
)
1414 _cupsLangPrintf(stderr
,
1415 _("ERROR: Unable to open print file \"%s\": %s\n"),
1416 files
[i
], strerror(errno
));
1418 exit(CUPS_BACKEND_FAILED
);
1422 while ((bytes
= cupsFileRead(in
, buffer
, sizeof(buffer
))) > 0)
1423 if (cupsFileWrite(out
, buffer
, bytes
) < bytes
)
1425 _cupsLangPrintf(stderr
,
1426 _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1427 (int)bytes
, filename
, strerror(errno
));
1430 exit(CUPS_BACKEND_FAILED
);
1438 files
[i
] = strdup(filename
);
1440 if (!stat(filename
, &outinfo
))
1442 "DEBUG: File %d compressed to %.1f%% of original size, "
1443 CUPS_LLFMT
" bytes...\n",
1444 i
+ 1, 100.0 * outinfo
.st_size
/ total
,
1445 CUPS_LLCAST outinfo
.st_size
);
1448 #endif /* HAVE_LIBZ */
1452 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1455 static const char * /* O - Password */
1456 password_cb(const char *prompt
) /* I - Prompt (not used) */
1460 if (password
&& *password
&& password_tries
< 3)
1469 * If there is no password set in the device URI, return the
1470 * "authentication required" exit code...
1474 unlink(tmpfilename
);
1479 #endif /* __APPLE__ */
1481 fputs("ATTR: auth-info-required=username,password\n", stderr
);
1483 exit(CUPS_BACKEND_AUTH_REQUIRED
);
1485 return (NULL
); /* Eliminate compiler warning */
1491 * 'report_printer_state()' - Report the printer state.
1494 static int /* O - Number of reasons shown */
1495 report_printer_state(ipp_t
*ipp
, /* I - IPP response */
1496 int job_id
) /* I - Current job ID */
1498 int i
; /* Looping var */
1499 int count
; /* Count of reasons shown... */
1500 ipp_attribute_t
*psm
, /* pritner-state-message */
1501 *reasons
; /* printer-state-reasons */
1502 const char *reason
; /* Current reason */
1503 const char *message
; /* Message to show */
1504 char unknown
[1024]; /* Unknown message string */
1505 const char *prefix
; /* Prefix for STATE: line */
1506 char state
[1024]; /* State string */
1507 cups_lang_t
*language
; /* Current localization */
1510 if ((psm
= ippFindAttribute(ipp
, "printer-state-message",
1511 IPP_TAG_TEXT
)) != NULL
)
1512 fprintf(stderr
, "INFO: %s\n", psm
->values
[0].string
.text
);
1514 if ((reasons
= ippFindAttribute(ipp
, "printer-state-reasons",
1515 IPP_TAG_KEYWORD
)) == NULL
)
1520 language
= cupsLangDefault();
1522 for (i
= 0, count
= 0; i
< reasons
->num_values
; i
++)
1524 reason
= reasons
->values
[i
].string
.text
;
1526 if (job_id
== 0 || strcmp(reason
, "paused"))
1528 strlcat(state
, prefix
, sizeof(state
));
1529 strlcat(state
, reason
, sizeof(state
));
1536 if (!strncmp(reason
, "media-needed", 12))
1537 message
= _("Media tray needs to be filled.");
1538 else if (!strncmp(reason
, "media-jam", 9))
1539 message
= _("Media jam!");
1540 else if (!strncmp(reason
, "moving-to-paused", 16) ||
1541 !strncmp(reason
, "paused", 6) ||
1542 !strncmp(reason
, "shutdown", 8))
1543 message
= _("Printer offline.");
1544 else if (!strncmp(reason
, "toner-low", 9))
1545 message
= _("Toner low.");
1546 else if (!strncmp(reason
, "toner-empty", 11))
1547 message
= _("Out of toner!");
1548 else if (!strncmp(reason
, "cover-open", 10))
1549 message
= _("Cover open.");
1550 else if (!strncmp(reason
, "interlock-open", 14))
1551 message
= _("Interlock open.");
1552 else if (!strncmp(reason
, "door-open", 9))
1553 message
= _("Door open.");
1554 else if (!strncmp(reason
, "input-tray-missing", 18))
1555 message
= _("Media tray missing!");
1556 else if (!strncmp(reason
, "media-low", 9))
1557 message
= _("Media tray almost empty.");
1558 else if (!strncmp(reason
, "media-empty", 11))
1559 message
= _("Media tray empty!");
1560 else if (!strncmp(reason
, "output-tray-missing", 19))
1561 message
= _("Output tray missing!");
1562 else if (!strncmp(reason
, "output-area-almost-full", 23))
1563 message
= _("Output bin almost full.");
1564 else if (!strncmp(reason
, "output-area-full", 16))
1565 message
= _("Output bin full!");
1566 else if (!strncmp(reason
, "marker-supply-low", 17))
1567 message
= _("Ink/toner almost empty.");
1568 else if (!strncmp(reason
, "marker-supply-empty", 19))
1569 message
= _("Ink/toner empty!");
1570 else if (!strncmp(reason
, "marker-waste-almost-full", 24))
1571 message
= _("Ink/toner waste bin almost full.");
1572 else if (!strncmp(reason
, "marker-waste-full", 17))
1573 message
= _("Ink/toner waste bin full!");
1574 else if (!strncmp(reason
, "fuser-over-temp", 15))
1575 message
= _("Fuser temperature high!");
1576 else if (!strncmp(reason
, "fuser-under-temp", 16))
1577 message
= _("Fuser temperature low!");
1578 else if (!strncmp(reason
, "opc-near-eol", 12))
1579 message
= _("OPC almost at end-of-life.");
1580 else if (!strncmp(reason
, "opc-life-over", 13))
1581 message
= _("OPC at end-of-life!");
1582 else if (!strncmp(reason
, "developer-low", 13))
1583 message
= _("Developer almost empty.");
1584 else if (!strncmp(reason
, "developer-empty", 15))
1585 message
= _("Developer empty!");
1586 else if (strstr(reason
, "error") != NULL
)
1590 snprintf(unknown
, sizeof(unknown
), _("Unknown printer error (%s)!"),
1597 if (strstr(reasons
->values
[i
].string
.text
, "error"))
1598 fprintf(stderr
, "ERROR: %s\n", _cupsLangString(language
, message
));
1599 else if (strstr(reasons
->values
[i
].string
.text
, "warning"))
1600 fprintf(stderr
, "WARNING: %s\n", _cupsLangString(language
, message
));
1602 fprintf(stderr
, "INFO: %s\n", _cupsLangString(language
, message
));
1606 fprintf(stderr
, "%s\n", state
);
1614 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1617 * This step is required because the PICT format is not documented and
1618 * subject to change, so developing a filter for other OS's is infeasible.
1619 * Also, fonts required by the PICT file need to be embedded on the
1620 * client side (which has the fonts), so we run the filter to get a
1621 * PostScript file for printing...
1624 static int /* O - Exit status of filter */
1625 run_pictwps_filter(char **argv
, /* I - Command-line arguments */
1626 const char *filename
)/* I - Filename */
1628 struct stat fileinfo
; /* Print file information */
1629 const char *ppdfile
; /* PPD file for destination printer */
1630 int pid
; /* Child process ID */
1631 int fd
; /* Temporary file descriptor */
1632 int status
; /* Exit status of filter */
1633 const char *printer
; /* PRINTER env var */
1634 static char ppdenv
[1024]; /* PPD environment variable */
1638 * First get the PPD file for the printer...
1641 printer
= getenv("PRINTER");
1644 _cupsLangPuts(stderr
,
1645 _("ERROR: PRINTER environment variable not defined!\n"));
1649 if ((ppdfile
= cupsGetPPD(printer
)) == NULL
)
1651 _cupsLangPrintf(stderr
,
1652 _("ERROR: Unable to get PPD file for printer \"%s\" - "
1653 "%s.\n"), printer
, cupsLastErrorString());
1657 snprintf(ppdenv
, sizeof(ppdenv
), "PPD=%s", ppdfile
);
1662 * Then create a temporary file for printing...
1665 if ((fd
= cupsTempFd(pstmpname
, sizeof(pstmpname
))) < 0)
1667 _cupsLangPrintf(stderr
, _("ERROR: Unable to create temporary file - %s.\n"),
1675 * Get the owner of the spool file - it is owned by the user we want to run
1680 stat(argv
[6], &fileinfo
);
1684 * Use the OSX defaults, as an up-stream filter created the PICT
1688 fileinfo
.st_uid
= 1;
1689 fileinfo
.st_gid
= 80;
1693 chown(ppdfile
, fileinfo
.st_uid
, fileinfo
.st_gid
);
1695 fchown(fd
, fileinfo
.st_uid
, fileinfo
.st_gid
);
1698 * Finally, run the filter to convert the file...
1701 if ((pid
= fork()) == 0)
1704 * Child process for pictwpstops... Redirect output of pictwpstops to a
1715 * Change to an unpriviledged user...
1718 setgid(fileinfo
.st_gid
);
1719 setuid(fileinfo
.st_uid
);
1722 execlp("pictwpstops", printer
, argv
[1], argv
[2], argv
[3], argv
[4], argv
[5],
1724 _cupsLangPrintf(stderr
, _("ERROR: Unable to exec pictwpstops: %s\n"),
1737 _cupsLangPrintf(stderr
, _("ERROR: Unable to fork pictwpstops: %s\n"),
1745 * Now wait for the filter to complete...
1748 if (wait(&status
) < 0)
1750 _cupsLangPrintf(stderr
, _("ERROR: Unable to wait for pictwpstops: %s\n"),
1766 _cupsLangPrintf(stderr
, _("ERROR: pictwpstops exited with status %d!\n"),
1769 _cupsLangPrintf(stderr
, _("ERROR: pictwpstops exited on signal %d!\n"),
1776 * Return with no errors..
1781 #endif /* __APPLE__ */
1785 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1789 sigterm_handler(int sig
) /* I - Signal */
1791 (void)sig
; /* remove compiler warnings... */
1796 * Flag that the job should be cancelled...
1804 * The scheduler already tried to cancel us once, now just terminate
1805 * after removing our temp files!
1809 unlink(tmpfilename
);
1814 #endif /* __APPLE__ */
1821 * End of "$Id: ipp.c 7583 2008-05-16 17:47:16Z mike $".