7 -RBACKENDS = ipp lpd $(DNSSD_BACKEND)
8 +RBACKENDS = ipp ipp14 lpd $(DNSSD_BACKEND)
9 UBACKENDS = $(LEGACY_BACKENDS) serial snmp socket usb
10 UNITTESTS = test1284 testbackend testsupplies
11 TARGETS = libbackend.a $(RBACKENDS) $(UBACKENDS)
12 LIBOBJS = ieee1284.o network.o runloop.o snmp-supplies.o
13 -OBJS = ipp.o lpd.o dnssd.o parallel.o serial.o snmp.o \
14 +OBJS = ipp.o ipp14.o lpd.o dnssd.o parallel.o serial.o snmp.o \
15 socket.o test1284.o testbackend.o testsupplies.o usb.o
25 +ipp14: ipp14.o ../cups/$(LIBCUPS) libbackend.a
27 + $(CC) $(LDFLAGS) -o ipp14 ipp14.o libbackend.a $(LIBS)
40 + * "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $"
42 + * IPP backend for the Common UNIX Printing System (CUPS).
44 + * Copyright 2007-2010 by Apple Inc.
45 + * Copyright 1997-2007 by Easy Software Products, all rights reserved.
47 + * These coded instructions, statements, and computer programs are the
48 + * property of Apple Inc. and are protected by Federal copyright
49 + * law. Distribution and use rights are outlined in the file "LICENSE.txt"
50 + * "LICENSE" which should have been included with this file. If this
51 + * file is missing or damaged, see the license at "http://www.cups.org/".
53 + * This file is subject to the Apple OS-Developed Software exception.
57 + * main() - Send a file to the printer or server.
58 + * cancel_job() - Cancel a print job.
59 + * check_printer_state() - Check the printer state...
60 + * compress_files() - Compress print files...
61 + * password_cb() - Disable the password prompt for
62 + * cupsDoFileRequest().
63 + * report_attr() - Report an IPP attribute value.
64 + * report_printer_state() - Report the printer state.
65 + * run_pictwps_filter() - Convert PICT files to PostScript when printing
67 + * sigterm_handler() - Handle 'terminate' signals that stop the backend.
71 + * Include necessary headers.
74 +#include <cups/http-private.h>
75 +#include "backend-private.h"
76 +#include <sys/types.h>
77 +#include <sys/stat.h>
78 +#include <sys/wait.h>
84 +static char *password = NULL; /* Password for device URI */
85 +static int password_tries = 0; /* Password tries */
86 +static const char *auth_info_required = "none";
87 + /* New auth-info-required value */
89 +static char pstmpname[1024] = ""; /* Temporary PostScript file name */
90 +#endif /* __APPLE__ */
91 +static char tmpfilename[1024] = ""; /* Temporary spool file name */
92 +static int job_cancelled = 0; /* Job cancelled? */
96 + * Local functions...
99 +static void cancel_job(http_t *http, const char *uri, int id,
100 + const char *resource, const char *user, int version);
101 +static void check_printer_state(http_t *http, const char *uri,
102 + const char *resource, const char *user,
103 + int version, int job_id);
105 +static void compress_files(int num_files, char **files);
106 +#endif /* HAVE_LIBZ */
107 +static const char *password_cb(const char *);
108 +static void report_attr(ipp_attribute_t *attr);
109 +static int report_printer_state(ipp_t *ipp, int job_id);
112 +static int run_pictwps_filter(char **argv, const char *filename);
113 +#endif /* __APPLE__ */
114 +static void sigterm_handler(int sig);
118 + * 'main()' - Send a file to the printer or server.
122 + * printer-uri job-id user title copies options [file]
125 +int /* O - Exit status */
126 +main(int argc, /* I - Number of command-line args */
127 + char *argv[]) /* I - Command-line arguments */
129 + int i; /* Looping var */
130 + int send_options; /* Send job options? */
131 + int num_options; /* Number of printer options */
132 + cups_option_t *options; /* Printer options */
133 + const char *device_uri; /* Device URI */
134 + char scheme[255], /* Scheme in URI */
135 + hostname[1024], /* Hostname */
136 + username[255], /* Username info */
137 + resource[1024], /* Resource info (printer name) */
138 + addrname[256], /* Address name */
139 + *optptr, /* Pointer to URI options */
140 + *name, /* Name of option */
141 + *value, /* Value of option */
142 + sep; /* Separator character */
143 + int snmp_fd, /* SNMP socket */
144 + start_count, /* Page count via SNMP at start */
145 + page_count, /* Page count via SNMP */
146 + have_supplies; /* Printer supports supply levels? */
147 + int num_files; /* Number of files to print */
148 + char **files, /* Files to print */
149 + *filename; /* Pointer to single filename */
150 + int port; /* Port number (not used) */
151 + char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
152 + ipp_status_t ipp_status; /* Status of IPP request */
153 + http_t *http; /* HTTP connection */
154 + ipp_t *request, /* IPP request */
155 + *response, /* IPP response */
156 + *supported; /* get-printer-attributes response */
157 + time_t start_time; /* Time of first connect */
158 + int recoverable; /* Recoverable error shown? */
159 + int contimeout; /* Connection timeout */
160 + int delay; /* Delay for retries... */
161 + int compression, /* Do compression of the job data? */
162 + waitjob, /* Wait for job complete? */
163 + waitprinter; /* Wait for printer ready? */
164 + ipp_attribute_t *job_id_attr; /* job-id attribute */
165 + int job_id; /* job-id value */
166 + ipp_attribute_t *job_sheets; /* job-media-sheets-completed */
167 + ipp_attribute_t *job_state; /* job-state */
168 + ipp_attribute_t *copies_sup; /* copies-supported */
169 + ipp_attribute_t *format_sup; /* document-format-supported */
170 + ipp_attribute_t *printer_state; /* printer-state attribute */
171 + ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs */
172 + int copies, /* Number of copies for job */
173 + copies_remaining; /* Number of copies remaining */
174 + const char *content_type, /* CONTENT_TYPE environment variable */
175 + *final_content_type; /* FINAL_CONTENT_TYPE environment var */
176 +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
177 + struct sigaction action; /* Actions for POSIX signals */
178 +#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
179 + int version; /* IPP version */
180 + static const char * const pattrs[] =
181 + { /* Printer attributes we want */
182 + "com.apple.print.recoverable-message",
183 + "copies-supported",
184 + "document-format-supported",
186 + "marker-high-levels",
188 + "marker-low-levels",
192 + "printer-is-accepting-jobs",
194 + "printer-state-message",
195 + "printer-state-reasons",
197 + static const char * const jattrs[] =
198 + { /* Job attributes we want */
199 + "job-media-sheets-completed",
205 + * Make sure status messages are not buffered...
208 + setbuf(stderr, NULL);
211 + * Ignore SIGPIPE and catch SIGTERM signals...
215 + sigset(SIGPIPE, SIG_IGN);
216 + sigset(SIGTERM, sigterm_handler);
217 +#elif defined(HAVE_SIGACTION)
218 + memset(&action, 0, sizeof(action));
219 + action.sa_handler = SIG_IGN;
220 + sigaction(SIGPIPE, &action, NULL);
222 + sigemptyset(&action.sa_mask);
223 + sigaddset(&action.sa_mask, SIGTERM);
224 + action.sa_handler = sigterm_handler;
225 + sigaction(SIGTERM, &action, NULL);
227 + signal(SIGPIPE, SIG_IGN);
228 + signal(SIGTERM, sigterm_handler);
229 +#endif /* HAVE_SIGSET */
232 + * Check command-line...
239 + if ((s = strrchr(argv[0], '/')) != NULL)
244 + printf("network %s \"Unknown\" \"%s (%s)\"\n",
245 + s, _cupsLangString(cupsLangDefault(),
246 + _("Internet Printing Protocol")), s);
247 + return (CUPS_BACKEND_OK);
251 + _cupsLangPrintf(stderr,
252 + _("Usage: %s job-id user title copies options [file]\n"),
254 + return (CUPS_BACKEND_STOP);
258 + * Get the (final) content type...
261 + if ((content_type = getenv("CONTENT_TYPE")) == NULL)
262 + content_type = "application/octet-stream";
264 + if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL)
266 + final_content_type = content_type;
268 + if (!strncmp(final_content_type, "printer/", 8))
269 + final_content_type = "application/vnd.cups-raw";
273 + * Extract the hostname and printer name from the URI...
276 + if ((device_uri = cupsBackendDeviceURI(argv)) == NULL)
277 + return (CUPS_BACKEND_FAILED);
279 + httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme),
280 + username, sizeof(username), hostname, sizeof(hostname), &port,
281 + resource, sizeof(resource));
284 + port = IPP_PORT; /* Default to port 631 */
286 + if (!strcmp(scheme, "https"))
287 + cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
289 + cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
292 + * See if there are any options...
299 + contimeout = 7 * 24 * 60 * 60;
301 + if ((optptr = strchr(resource, '?')) != NULL)
304 + * Yup, terminate the device name string and move to the first
305 + * character of the optptr...
311 + * Then parse the optptr...
322 + while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&')
325 + if ((sep = *optptr) != '\0')
336 + while (*optptr && *optptr != '+' && *optptr != '&')
343 + value = (char *)"";
346 + * Process the option...
349 + if (!strcasecmp(name, "waitjob"))
352 + * Wait for job completion?
355 + waitjob = !strcasecmp(value, "on") ||
356 + !strcasecmp(value, "yes") ||
357 + !strcasecmp(value, "true");
359 + else if (!strcasecmp(name, "waitprinter"))
362 + * Wait for printer idle?
365 + waitprinter = !strcasecmp(value, "on") ||
366 + !strcasecmp(value, "yes") ||
367 + !strcasecmp(value, "true");
369 + else if (!strcasecmp(name, "encryption"))
372 + * Enable/disable encryption?
375 + if (!strcasecmp(value, "always"))
376 + cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
377 + else if (!strcasecmp(value, "required"))
378 + cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
379 + else if (!strcasecmp(value, "never"))
380 + cupsSetEncryption(HTTP_ENCRYPT_NEVER);
381 + else if (!strcasecmp(value, "ifrequested"))
382 + cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
385 + _cupsLangPrintf(stderr,
386 + _("ERROR: Unknown encryption option value \"%s\"!\n"),
390 + else if (!strcasecmp(name, "version"))
392 + if (!strcmp(value, "1.0"))
394 + else if (!strcmp(value, "1.1"))
396 + else if (!strcmp(value, "2.0"))
398 + else if (!strcmp(value, "2.1"))
402 + _cupsLangPrintf(stderr,
403 + _("ERROR: Unknown version option value \"%s\"!\n"),
408 + else if (!strcasecmp(name, "compression"))
410 + compression = !strcasecmp(value, "true") ||
411 + !strcasecmp(value, "yes") ||
412 + !strcasecmp(value, "on") ||
413 + !strcasecmp(value, "gzip");
415 +#endif /* HAVE_LIBZ */
416 + else if (!strcasecmp(name, "contimeout"))
419 + * Set the connection timeout...
422 + if (atoi(value) > 0)
423 + contimeout = atoi(value);
428 + * Unknown option...
431 + _cupsLangPrintf(stderr,
432 + _("ERROR: Unknown option \"%s\" with value \"%s\"!\n"),
439 + * If we have 7 arguments, print the file named on the command-line.
440 + * Otherwise, copy stdin to a temporary file and print the temporary
447 + * Copy stdin to a temporary file...
450 + int fd; /* File descriptor */
451 + http_addrlist_t *addrlist; /* Address list */
452 + off_t tbytes; /* Total bytes copied */
455 + fputs("STATE: +connecting-to-device\n", stderr);
456 + fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname);
458 + if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, "1")) == NULL)
460 + _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
462 + return (CUPS_BACKEND_STOP);
465 + snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family);
467 + if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
469 + _cupsLangPrintError("ERROR", _("Unable to create temporary file"));
470 + return (CUPS_BACKEND_FAILED);
473 + _cupsLangPuts(stderr, _("INFO: Copying print data...\n"));
475 + tbytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0,
476 + backendNetworkSideCB);
479 + _cupsSNMPClose(snmp_fd);
481 + httpAddrFreeList(addrlist);
486 + * Don't try printing files less than 2 bytes...
491 + _cupsLangPuts(stderr, _("ERROR: Empty print file!\n"));
492 + unlink(tmpfilename);
493 + return (CUPS_BACKEND_FAILED);
497 + * Point to the single file from stdin...
500 + filename = tmpfilename;
508 + * Point to the files on the command-line...
511 + num_files = argc - 6;
517 + compress_files(num_files, files);
518 +#endif /* HAVE_LIBZ */
521 + fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files);
524 + * Set the authentication info, if any...
527 + cupsSetPasswordCB(password_cb);
532 + * Use authenticaion information in the device URI...
535 + if ((password = strchr(username, ':')) != NULL)
536 + *password++ = '\0';
538 + cupsSetUser(username);
540 + else if (!getuid())
543 + * Try loading authentication information from the environment.
546 + const char *ptr = getenv("AUTH_USERNAME");
551 + password = getenv("AUTH_PASSWORD");
555 + * Try connecting to the remote server...
560 + start_time = time(NULL);
562 + fputs("STATE: +connecting-to-device\n", stderr);
566 + fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port);
567 + _cupsLangPuts(stderr, _("INFO: Connecting to printer...\n"));
569 + if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
574 + if (getenv("CLASS") != NULL)
577 + * If the CLASS environment variable is set, the job was submitted
578 + * to a class and not to a specific queue. In this case, we want
579 + * to abort immediately so that the job can be requeued on the next
580 + * available printer in the class.
583 + _cupsLangPuts(stderr,
584 + _("INFO: Unable to contact printer, queuing on next "
585 + "printer in class...\n"));
587 + if (tmpfilename[0])
588 + unlink(tmpfilename);
591 + * Sleep 5 seconds to keep the job from requeuing too rapidly...
596 + return (CUPS_BACKEND_FAILED);
599 + if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
600 + errno == EHOSTUNREACH)
602 + if (contimeout && (time(NULL) - start_time) > contimeout)
604 + _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
605 + return (CUPS_BACKEND_FAILED);
610 + _cupsLangPrintf(stderr,
611 + _("WARNING: recoverable: Network host \'%s\' is busy; "
612 + "will retry in %d seconds...\n"),
622 + _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"),
624 + return (CUPS_BACKEND_STOP);
630 + fprintf(stderr, "DEBUG: Connection error: %s\n", strerror(errno));
631 + _cupsLangPuts(stderr,
632 + _("ERROR: recoverable: Unable to connect to printer; will "
633 + "retry in 30 seconds...\n"));
641 + while (http == NULL);
643 + if (job_cancelled || !http)
645 + if (tmpfilename[0])
646 + unlink(tmpfilename);
648 + return (CUPS_BACKEND_FAILED);
651 + fputs("STATE: -connecting-to-device\n", stderr);
652 + _cupsLangPuts(stderr, _("INFO: Connected to printer...\n"));
655 + if (http->hostaddr->addr.sa_family == AF_INET6)
656 + fprintf(stderr, "DEBUG: Connected to [%s]:%d (IPv6)...\n",
657 + httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
658 + ntohs(http->hostaddr->ipv6.sin6_port));
660 +#endif /* AF_INET6 */
661 + if (http->hostaddr->addr.sa_family == AF_INET)
662 + fprintf(stderr, "DEBUG: Connected to %s:%d (IPv4)...\n",
663 + httpAddrString(http->hostaddr, addrname, sizeof(addrname)),
664 + ntohs(http->hostaddr->ipv4.sin_port));
667 + * See if the printer supports SNMP...
670 + if ((snmp_fd = _cupsSNMPOpen(http->hostaddr->addr.sa_family)) >= 0)
671 + have_supplies = !backendSNMPSupplies(snmp_fd, http->hostaddr, &start_count,
674 + have_supplies = start_count = 0;
677 + * Build a URI for the printer and fill the standard IPP attributes for
678 + * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
679 + * might contain username:password information...
682 + httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname,
686 + * First validate the destination and see if the device supports multiple
687 + * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
688 + * don't support the copies attribute...
698 + * Check for side-channel requests...
701 + backendCheckSideChannel(snmp_fd, http->hostaddr);
704 + * Build the IPP request...
707 + request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
708 + request->request.op.version[0] = version / 10;
709 + request->request.op.version[1] = version % 10;
711 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
714 + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
715 + "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
719 + * Do the request...
722 + fputs("DEBUG: Getting supported attributes...\n", stderr);
724 + if (http->version < HTTP_1_1)
725 + httpReconnect(http);
727 + if ((supported = cupsDoRequest(http, request, resource)) == NULL)
728 + ipp_status = cupsLastError();
730 + ipp_status = supported->request.status.status_code;
732 + if (ipp_status > IPP_OK_CONFLICT)
734 + if (ipp_status == IPP_PRINTER_BUSY ||
735 + ipp_status == IPP_SERVICE_UNAVAILABLE)
737 + if (contimeout && (time(NULL) - start_time) > contimeout)
739 + _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n"));
740 + return (CUPS_BACKEND_FAILED);
745 + _cupsLangPrintf(stderr,
746 + _("WARNING: recoverable: Network host \'%s\' is busy; "
747 + "will retry in %d seconds...\n"),
750 + report_printer_state(supported, 0);
757 + else if ((ipp_status == IPP_BAD_REQUEST ||
758 + ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
761 + * Switch to IPP/1.0...
764 + _cupsLangPrintf(stderr,
765 + _("INFO: Printer does not support IPP/%d.%d, trying "
766 + "IPP/1.0...\n"), version / 10, version % 10);
768 + httpReconnect(http);
770 + else if (ipp_status == IPP_NOT_FOUND)
772 + _cupsLangPuts(stderr, _("ERROR: Destination printer does not exist!\n"));
775 + ippDelete(supported);
777 + return (CUPS_BACKEND_STOP);
779 + else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
781 + if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
783 + auth_info_required = "negotiate";
785 + fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
786 + return (CUPS_BACKEND_AUTH_REQUIRED);
790 + _cupsLangPrintf(stderr,
791 + _("ERROR: Unable to get printer status (%s)!\n"),
792 + cupsLastErrorString());
797 + ippDelete(supported);
801 + else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
802 + IPP_TAG_RANGE)) != NULL)
805 + * Has the "copies-supported" attribute - does it have an upper
809 + if (copies_sup->values[0].range.upper <= 1)
810 + copies_sup = NULL; /* No */
813 + format_sup = ippFindAttribute(supported, "document-format-supported",
818 + fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
819 + format_sup->num_values);
820 + for (i = 0; i < format_sup->num_values; i ++)
821 + fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
822 + format_sup->values[i].string.text);
825 + report_printer_state(supported, 0);
827 + while (ipp_status > IPP_OK_CONFLICT);
830 + * See if the printer is accepting jobs and is not stopped; if either
831 + * condition is true and we are printing to a class, requeue the job...
834 + if (getenv("CLASS") != NULL)
836 + printer_state = ippFindAttribute(supported, "printer-state",
838 + printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
841 + if (printer_state == NULL ||
842 + (printer_state->values[0].integer > IPP_PRINTER_PROCESSING &&
844 + printer_accepting == NULL ||
845 + !printer_accepting->values[0].boolean)
848 + * If the CLASS environment variable is set, the job was submitted
849 + * to a class and not to a specific queue. In this case, we want
850 + * to abort immediately so that the job can be requeued on the next
851 + * available printer in the class.
854 + _cupsLangPuts(stderr,
855 + _("INFO: Unable to contact printer, queuing on next "
856 + "printer in class...\n"));
858 + ippDelete(supported);
861 + if (tmpfilename[0])
862 + unlink(tmpfilename);
865 + * Sleep 5 seconds to keep the job from requeuing too rapidly...
870 + return (CUPS_BACKEND_FAILED);
877 + * If we've shown a recoverable error make sure the printer proxies
878 + * have a chance to see the recovered message. Not pretty but
879 + * necessary for now...
882 + fputs("INFO: recovered: \n", stderr);
887 + * See if the printer supports multiple copies...
890 + copies = atoi(argv[4]);
892 + if (copies_sup || argc < 7)
894 + copies_remaining = 1;
900 + copies_remaining = copies;
903 + * Then issue the print-job request...
908 + while (copies_remaining > 0)
911 + * Check for side-channel requests...
914 + backendCheckSideChannel(snmp_fd, http->hostaddr);
917 + * Build the IPP request...
924 + request = ippNewRequest(IPP_CREATE_JOB);
926 + request = ippNewRequest(IPP_PRINT_JOB);
928 + request->request.op.version[0] = version / 10;
929 + request->request.op.version[1] = version % 10;
931 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
934 + fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
937 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
938 + "requesting-user-name", NULL, argv[2]);
940 + fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
943 + * Only add a "job-name" attribute if the remote server supports
944 + * copy generation - some IPP implementations like HP's don't seem
945 + * to like UTF-8 job names (STR #1837)...
948 + if (argv[3][0] && copies_sup)
949 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
952 + fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
956 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
957 + "compression", NULL, "gzip");
958 +#endif /* HAVE_LIBZ */
961 + * Handle options on the command-line...
965 + num_options = cupsParseOptions(argv[5], 0, &options);
968 + if (!strcasecmp(final_content_type, "application/pictwps") &&
971 + if (format_sup != NULL)
973 + for (i = 0; i < format_sup->num_values; i ++)
974 + if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
978 + if (format_sup == NULL || i >= format_sup->num_values)
981 + * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
982 + * so convert the document to PostScript...
985 + if (run_pictwps_filter(argv, files[0]))
990 + if (tmpfilename[0])
991 + unlink(tmpfilename);
993 + return (CUPS_BACKEND_FAILED);
996 + files[0] = pstmpname;
999 + * Change the MIME type to application/postscript and change the
1000 + * number of copies to 1...
1003 + final_content_type = "application/postscript";
1005 + copies_remaining = 1;
1009 +#endif /* __APPLE__ */
1011 + if (format_sup != NULL)
1013 + for (i = 0; i < format_sup->num_values; i ++)
1014 + if (!strcasecmp(final_content_type, format_sup->values[i].string.text))
1017 + if (i < format_sup->num_values)
1018 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1019 + "document-format", NULL, final_content_type);
1022 + if (copies_sup && version > 10 && send_options)
1025 + * Only send options if the destination printer supports the copies
1026 + * attribute and IPP/1.1. This is a hack for the HP and Lexmark
1027 + * implementations of IPP, which do not accept extension attributes
1028 + * and incorrectly report a client-error-bad-request error instead of
1029 + * the successful-ok-unsupported-attributes status. In short, at least
1030 + * some HP and Lexmark implementations of IPP are non-compliant.
1033 + cupsEncodeOptions(request, num_options, options);
1035 + ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
1039 + cupsFreeOptions(num_options, options);
1042 + * If copies aren't supported, then we are likely dealing with an HP
1043 + * JetDirect. The HP IPP implementation seems to close the connection
1044 + * after every request - that is, it does *not* implement HTTP Keep-
1045 + * Alive, which is REQUIRED by HTTP/1.1...
1049 + httpReconnect(http);
1052 + * Do the request...
1055 + if (http->version < HTTP_1_1)
1056 + httpReconnect(http);
1058 + if (num_files > 1)
1059 + response = cupsDoRequest(http, request, resource);
1061 + response = cupsDoFileRequest(http, request, resource, files[0]);
1063 + ipp_status = cupsLastError();
1065 + if (ipp_status > IPP_OK_CONFLICT)
1069 + if (job_cancelled)
1072 + if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1073 + ipp_status == IPP_PRINTER_BUSY)
1075 + _cupsLangPuts(stderr,
1076 + _("INFO: Printer busy; will retry in 10 seconds...\n"));
1079 + else if ((ipp_status == IPP_BAD_REQUEST ||
1080 + ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10)
1083 + * Switch to IPP/1.0...
1086 + _cupsLangPrintf(stderr,
1087 + _("INFO: Printer does not support IPP/%d.%d, trying "
1088 + "IPP/1.0...\n"), version / 10, version % 10);
1090 + httpReconnect(http);
1095 + * Update auth-info-required as needed...
1098 + _cupsLangPrintf(stderr, _("ERROR: Print file was not accepted (%s)!\n"),
1099 + cupsLastErrorString());
1101 + if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1103 + fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n",
1104 + httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE));
1107 + * Normal authentication goes through the password callback, which sets
1108 + * auth_info_required to "username,password". Kerberos goes directly
1109 + * through GSSAPI, so look for Negotiate in the WWW-Authenticate header
1110 + * here and set auth_info_required as needed...
1113 + if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE),
1115 + auth_info_required = "negotiate";
1119 + else if ((job_id_attr = ippFindAttribute(response, "job-id",
1120 + IPP_TAG_INTEGER)) == NULL)
1122 + _cupsLangPuts(stderr,
1123 + _("NOTICE: Print file accepted - job ID unknown.\n"));
1128 + job_id = job_id_attr->values[0].integer;
1129 + _cupsLangPrintf(stderr, _("NOTICE: Print file accepted - job ID %d.\n"),
1133 + ippDelete(response);
1135 + if (job_cancelled)
1138 + if (job_id && num_files > 1)
1140 + for (i = 0; i < num_files; i ++)
1143 + * Check for side-channel requests...
1146 + backendCheckSideChannel(snmp_fd, http->hostaddr);
1149 + * Send the next file in the job...
1152 + request = ippNewRequest(IPP_SEND_DOCUMENT);
1153 + request->request.op.version[0] = version / 10;
1154 + request->request.op.version[1] = version % 10;
1156 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1159 + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1163 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1164 + "requesting-user-name", NULL, argv[2]);
1166 + if ((i + 1) == num_files)
1167 + ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
1169 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
1170 + "document-format", NULL, content_type);
1172 + if (http->version < HTTP_1_1)
1173 + httpReconnect(http);
1175 + ippDelete(cupsDoFileRequest(http, request, resource, files[i]));
1177 + if (cupsLastError() > IPP_OK_CONFLICT)
1179 + ipp_status = cupsLastError();
1181 + _cupsLangPrintf(stderr,
1182 + _("ERROR: Unable to add file %d to job: %s\n"),
1183 + job_id, cupsLastErrorString());
1189 + if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
1191 + fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
1192 + copies_remaining --;
1194 + else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
1195 + ipp_status == IPP_PRINTER_BUSY)
1198 + copies_remaining --;
1201 + * Wait for the job to complete...
1204 + if (!job_id || !waitjob)
1207 + _cupsLangPuts(stderr, _("INFO: Waiting for job to complete...\n"));
1209 + for (delay = 1; !job_cancelled;)
1212 + * Check for side-channel requests...
1215 + backendCheckSideChannel(snmp_fd, http->hostaddr);
1218 + * Build an IPP_GET_JOB_ATTRIBUTES request...
1221 + request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
1222 + request->request.op.version[0] = version / 10;
1223 + request->request.op.version[1] = version % 10;
1225 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1228 + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
1232 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1233 + "requesting-user-name", NULL, argv[2]);
1235 + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1236 + "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1240 + * Do the request...
1243 + if (!copies_sup || http->version < HTTP_1_1)
1244 + httpReconnect(http);
1246 + response = cupsDoRequest(http, request, resource);
1247 + ipp_status = cupsLastError();
1249 + if (ipp_status == IPP_NOT_FOUND)
1252 + * Job has gone away and/or the server has no job history...
1255 + ippDelete(response);
1257 + ipp_status = IPP_OK;
1261 + if (ipp_status > IPP_OK_CONFLICT)
1263 + if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
1264 + ipp_status != IPP_PRINTER_BUSY)
1266 + ippDelete(response);
1268 + _cupsLangPrintf(stderr,
1269 + _("ERROR: Unable to get job %d attributes (%s)!\n"),
1270 + job_id, cupsLastErrorString());
1277 + if ((job_state = ippFindAttribute(response, "job-state",
1278 + IPP_TAG_ENUM)) != NULL)
1281 + * Stop polling if the job is finished or pending-held...
1284 + if (job_state->values[0].integer > IPP_JOB_STOPPED)
1286 + if ((job_sheets = ippFindAttribute(response,
1287 + "job-media-sheets-completed",
1288 + IPP_TAG_INTEGER)) != NULL)
1289 + fprintf(stderr, "PAGE: total %d\n",
1290 + job_sheets->values[0].integer);
1292 + ippDelete(response);
1299 + * If the printer does not return a job-state attribute, it does not
1300 + * conform to the IPP specification - break out immediately and fail
1304 + fputs("DEBUG: No job-state available from printer - stopping queue.\n",
1306 + ipp_status = IPP_INTERNAL_ERROR;
1311 + ippDelete(response);
1314 + * Check the printer state and report it if necessary...
1317 + check_printer_state(http, uri, resource, argv[2], version, job_id);
1320 + * Wait 1-10 seconds before polling again...
1332 + * Cancel the job as needed...
1335 + if (job_cancelled && job_id)
1336 + cancel_job(http, uri, job_id, resource, argv[2], version);
1339 + * Check the printer state and report it if necessary...
1342 + check_printer_state(http, uri, resource, argv[2], version, job_id);
1345 + * Collect the final page count as needed...
1348 + if (have_supplies &&
1349 + !backendSNMPSupplies(snmp_fd, http->hostaddr, &page_count, NULL) &&
1350 + page_count > start_count)
1351 + fprintf(stderr, "PAGE: total %d\n", page_count - start_count);
1355 + * See if we used Kerberos at all...
1359 + auth_info_required = "negotiate";
1360 +#endif /* HAVE_GSSAPI */
1368 + ippDelete(supported);
1371 + * Remove the temporary file(s) if necessary...
1374 + if (tmpfilename[0])
1375 + unlink(tmpfilename);
1380 + for (i = 0; i < num_files; i ++)
1383 +#endif /* HAVE_LIBZ */
1387 + unlink(pstmpname);
1388 +#endif /* __APPLE__ */
1391 + * Return the queue status...
1394 + fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required);
1396 + if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN)
1397 + return (CUPS_BACKEND_AUTH_REQUIRED);
1398 + else if (ipp_status == IPP_INTERNAL_ERROR)
1399 + return (CUPS_BACKEND_STOP);
1400 + else if (ipp_status > IPP_OK_CONFLICT)
1401 + return (CUPS_BACKEND_FAILED);
1404 + _cupsLangPuts(stderr, _("INFO: Ready to print.\n"));
1405 + return (CUPS_BACKEND_OK);
1411 + * 'cancel_job()' - Cancel a print job.
1415 +cancel_job(http_t *http, /* I - HTTP connection */
1416 + const char *uri, /* I - printer-uri */
1417 + int id, /* I - job-id */
1418 + const char *resource, /* I - Resource path */
1419 + const char *user, /* I - requesting-user-name */
1420 + int version) /* I - IPP version */
1422 + ipp_t *request; /* Cancel-Job request */
1425 + _cupsLangPuts(stderr, _("INFO: Canceling print job...\n"));
1427 + request = ippNewRequest(IPP_CANCEL_JOB);
1428 + request->request.op.version[0] = version / 10;
1429 + request->request.op.version[1] = version % 10;
1431 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1433 + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1435 + if (user && user[0])
1436 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1437 + "requesting-user-name", NULL, user);
1440 + * Do the request...
1443 + if (http->version < HTTP_1_1)
1444 + httpReconnect(http);
1446 + ippDelete(cupsDoRequest(http, request, resource));
1448 + if (cupsLastError() > IPP_OK_CONFLICT)
1449 + _cupsLangPrintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id,
1450 + cupsLastErrorString());
1455 + * 'check_printer_state()' - Check the printer state...
1459 +check_printer_state(
1460 + http_t *http, /* I - HTTP connection */
1461 + const char *uri, /* I - Printer URI */
1462 + const char *resource, /* I - Resource path */
1463 + const char *user, /* I - Username, if any */
1464 + int version, /* I - IPP version */
1465 + int job_id) /* I - Current job ID */
1467 + ipp_t *request, /* IPP request */
1468 + *response; /* IPP response */
1469 + static const char * const attrs[] = /* Attributes we want */
1471 + "com.apple.print.recoverable-message",
1477 + "printer-state-message",
1478 + "printer-state-reasons"
1483 + * Check on the printer state...
1486 + request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1487 + request->request.op.version[0] = version / 10;
1488 + request->request.op.version[1] = version % 10;
1490 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1493 + if (user && user[0])
1494 + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1495 + "requesting-user-name", NULL, user);
1497 + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1498 + "requested-attributes",
1499 + (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs);
1502 + * Do the request...
1505 + if (http->version < HTTP_1_1)
1506 + httpReconnect(http);
1508 + if ((response = cupsDoRequest(http, request, resource)) != NULL)
1510 + report_printer_state(response, job_id);
1511 + ippDelete(response);
1518 + * 'compress_files()' - Compress print files...
1522 +compress_files(int num_files, /* I - Number of files */
1523 + char **files) /* I - Files */
1525 + int i, /* Looping var */
1526 + fd; /* Temporary file descriptor */
1527 + ssize_t bytes; /* Bytes read/written */
1528 + size_t total; /* Total bytes read */
1529 + cups_file_t *in, /* Input file */
1530 + *out; /* Output file */
1531 + struct stat outinfo; /* Output file information */
1532 + char filename[1024], /* Temporary filename */
1533 + buffer[32768]; /* Copy buffer */
1536 + fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files);
1537 + for (i = 0; i < num_files; i ++)
1539 + if ((fd = cupsTempFd(filename, sizeof(filename))) < 0)
1541 + _cupsLangPrintf(stderr,
1542 + _("ERROR: Unable to create temporary compressed print "
1543 + "file: %s\n"), strerror(errno));
1544 + exit(CUPS_BACKEND_FAILED);
1547 + if ((out = cupsFileOpenFd(fd, "w9")) == NULL)
1549 + _cupsLangPrintf(stderr,
1550 + _("ERROR: Unable to open temporary compressed print "
1551 + "file: %s\n"), strerror(errno));
1552 + exit(CUPS_BACKEND_FAILED);
1555 + if ((in = cupsFileOpen(files[i], "r")) == NULL)
1557 + _cupsLangPrintf(stderr,
1558 + _("ERROR: Unable to open print file \"%s\": %s\n"),
1559 + files[i], strerror(errno));
1560 + cupsFileClose(out);
1561 + exit(CUPS_BACKEND_FAILED);
1565 + while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0)
1566 + if (cupsFileWrite(out, buffer, bytes) < bytes)
1568 + _cupsLangPrintf(stderr,
1569 + _("ERROR: Unable to write %d bytes to \"%s\": %s\n"),
1570 + (int)bytes, filename, strerror(errno));
1571 + cupsFileClose(in);
1572 + cupsFileClose(out);
1573 + exit(CUPS_BACKEND_FAILED);
1578 + cupsFileClose(out);
1579 + cupsFileClose(in);
1581 + files[i] = strdup(filename);
1583 + if (!stat(filename, &outinfo))
1585 + "DEBUG: File %d compressed to %.1f%% of original size, "
1586 + CUPS_LLFMT " bytes...\n",
1587 + i + 1, 100.0 * outinfo.st_size / total,
1588 + CUPS_LLCAST outinfo.st_size);
1591 +#endif /* HAVE_LIBZ */
1595 + * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1598 +static const char * /* O - Password */
1599 +password_cb(const char *prompt) /* I - Prompt (not used) */
1604 + * Remember that we need to authenticate...
1607 + auth_info_required = "username,password";
1609 + if (password && *password && password_tries < 3)
1611 + password_tries ++;
1613 + return (password);
1618 + * Give up after 3 tries or if we don't have a password to begin with...
1627 + * 'report_attr()' - Report an IPP attribute value.
1631 +report_attr(ipp_attribute_t *attr) /* I - Attribute */
1633 + int i; /* Looping var */
1634 + char value[1024], /* Value string */
1635 + *valptr, /* Pointer into value string */
1636 + *attrptr; /* Pointer into attribute value */
1640 + * Convert the attribute values into quoted strings...
1643 + for (i = 0, valptr = value;
1644 + i < attr->num_values && valptr < (value + sizeof(value) - 10);
1650 + switch (attr->value_tag)
1652 + case IPP_TAG_INTEGER :
1653 + case IPP_TAG_ENUM :
1654 + snprintf(valptr, sizeof(value) - (valptr - value), "%d",
1655 + attr->values[i].integer);
1656 + valptr += strlen(valptr);
1659 + case IPP_TAG_TEXT :
1660 + case IPP_TAG_NAME :
1661 + case IPP_TAG_KEYWORD :
1663 + for (attrptr = attr->values[i].string.text;
1664 + *attrptr && valptr < (value + sizeof(value) - 10);
1667 + if (*attrptr == '\\' || *attrptr == '\"')
1670 + *valptr++ = *attrptr;
1677 + * Unsupported value type...
1687 + * Tell the scheduler about the new values...
1690 + fprintf(stderr, "ATTR: %s=%s\n", attr->name, value);
1695 + * 'report_printer_state()' - Report the printer state.
1698 +static int /* O - Number of reasons shown */
1699 +report_printer_state(ipp_t *ipp, /* I - IPP response */
1700 + int job_id) /* I - Current job ID */
1702 + int i; /* Looping var */
1703 + int count; /* Count of reasons shown... */
1704 + ipp_attribute_t *caprm, /* com.apple.print.recoverable-message */
1705 + *psm, /* printer-state-message */
1706 + *reasons, /* printer-state-reasons */
1707 + *marker; /* marker-* attributes */
1708 + const char *reason; /* Current reason */
1709 + const char *prefix; /* Prefix for STATE: line */
1710 + char state[1024]; /* State string */
1711 + int saw_caprw; /* Saw com.apple.print.recoverable-warning state */
1714 + if ((psm = ippFindAttribute(ipp, "printer-state-message",
1715 + IPP_TAG_TEXT)) != NULL)
1716 + fprintf(stderr, "INFO: %s\n", psm->values[0].string.text);
1718 + if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1719 + IPP_TAG_KEYWORD)) == NULL)
1724 + prefix = "STATE: ";
1726 + for (i = 0, count = 0; i < reasons->num_values; i ++)
1728 + reason = reasons->values[i].string.text;
1730 + if (!strcmp(reason, "com.apple.print.recoverable-warning"))
1732 + else if (strcmp(reason, "paused"))
1734 + strlcat(state, prefix, sizeof(state));
1735 + strlcat(state, reason, sizeof(state));
1742 + fprintf(stderr, "%s\n", state);
1745 + * Relay com.apple.print.recoverable-message...
1748 + if ((caprm = ippFindAttribute(ipp, "com.apple.print.recoverable-message",
1749 + IPP_TAG_TEXT)) != NULL)
1750 + fprintf(stderr, "WARNING: %s: %s\n",
1751 + saw_caprw ? "recoverable" : "recovered",
1752 + caprm->values[0].string.text);
1755 + * Relay the current marker-* attribute values...
1758 + if ((marker = ippFindAttribute(ipp, "marker-colors", IPP_TAG_NAME)) != NULL)
1759 + report_attr(marker);
1760 + if ((marker = ippFindAttribute(ipp, "marker-high-levels",
1761 + IPP_TAG_INTEGER)) != NULL)
1762 + report_attr(marker);
1763 + if ((marker = ippFindAttribute(ipp, "marker-levels",
1764 + IPP_TAG_INTEGER)) != NULL)
1765 + report_attr(marker);
1766 + if ((marker = ippFindAttribute(ipp, "marker-low-levels",
1767 + IPP_TAG_INTEGER)) != NULL)
1768 + report_attr(marker);
1769 + if ((marker = ippFindAttribute(ipp, "marker-message", IPP_TAG_TEXT)) != NULL)
1770 + report_attr(marker);
1771 + if ((marker = ippFindAttribute(ipp, "marker-names", IPP_TAG_NAME)) != NULL)
1772 + report_attr(marker);
1773 + if ((marker = ippFindAttribute(ipp, "marker-types", IPP_TAG_KEYWORD)) != NULL)
1774 + report_attr(marker);
1782 + * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1785 + * This step is required because the PICT format is not documented and
1786 + * subject to change, so developing a filter for other OS's is infeasible.
1787 + * Also, fonts required by the PICT file need to be embedded on the
1788 + * client side (which has the fonts), so we run the filter to get a
1789 + * PostScript file for printing...
1792 +static int /* O - Exit status of filter */
1793 +run_pictwps_filter(char **argv, /* I - Command-line arguments */
1794 + const char *filename)/* I - Filename */
1796 + struct stat fileinfo; /* Print file information */
1797 + const char *ppdfile; /* PPD file for destination printer */
1798 + int pid; /* Child process ID */
1799 + int fd; /* Temporary file descriptor */
1800 + int status; /* Exit status of filter */
1801 + const char *printer; /* PRINTER env var */
1802 + static char ppdenv[1024]; /* PPD environment variable */
1806 + * First get the PPD file for the printer...
1809 + printer = getenv("PRINTER");
1812 + _cupsLangPuts(stderr,
1813 + _("ERROR: PRINTER environment variable not defined!\n"));
1817 + if ((ppdfile = cupsGetPPD(printer)) == NULL)
1819 + _cupsLangPrintf(stderr,
1820 + _("ERROR: Unable to get PPD file for printer \"%s\" - "
1821 + "%s.\n"), printer, cupsLastErrorString());
1825 + snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1830 + * Then create a temporary file for printing...
1833 + if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
1835 + _cupsLangPrintError("ERROR", _("Unable to create temporary file"));
1842 + * Get the owner of the spool file - it is owned by the user we want to run
1847 + stat(argv[6], &fileinfo);
1851 + * Use the OSX defaults, as an up-stream filter created the PICT
1855 + fileinfo.st_uid = 1;
1856 + fileinfo.st_gid = 80;
1860 + chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1862 + fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1865 + * Finally, run the filter to convert the file...
1868 + if ((pid = fork()) == 0)
1871 + * Child process for pictwpstops... Redirect output of pictwpstops to a
1881 + * Change to an unpriviledged user...
1884 + if (setgid(fileinfo.st_gid))
1887 + if (setuid(fileinfo.st_uid))
1891 + execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
1893 + _cupsLangPrintf(stderr, _("ERROR: Unable to exec pictwpstops: %s\n"),
1906 + _cupsLangPrintf(stderr, _("ERROR: Unable to fork pictwpstops: %s\n"),
1914 + * Now wait for the filter to complete...
1917 + if (wait(&status) < 0)
1919 + _cupsLangPrintf(stderr, _("ERROR: Unable to wait for pictwpstops: %s\n"),
1934 + if (status >= 256)
1935 + _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited with status %d!\n"),
1938 + _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited on signal %d!\n"),
1945 + * Return with no errors..
1950 +#endif /* __APPLE__ */
1954 + * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1958 +sigterm_handler(int sig) /* I - Signal */
1960 + (void)sig; /* remove compiler warnings... */
1962 + if (!job_cancelled)
1965 + * Flag that the job should be cancelled...
1968 + job_cancelled = 1;
1973 + * The scheduler already tried to cancel us once, now just terminate
1974 + * after removing our temp files!
1977 + if (tmpfilename[0])
1978 + unlink(tmpfilename);
1982 + unlink(pstmpname);
1983 +#endif /* __APPLE__ */
1990 + * End of "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $".