]>
Commit | Line | Data |
---|---|---|
ba7599c9 AM |
1 | --- a/backend/Makefile |
2 | +++ b/backend/Makefile | |
3 | @@ -21,12 +21,12 @@ | |
4 | # Object files... | |
5 | # | |
6 | ||
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 | |
16 | ||
17 | ||
18 | @@ -218,6 +218,17 @@ | |
19 | ||
20 | ||
21 | # | |
22 | +# ipp14 | |
23 | +# | |
24 | + | |
25 | +ipp14: ipp14.o ../cups/$(LIBCUPS) libbackend.a | |
26 | + echo Linking $@... | |
27 | + $(CC) $(LDFLAGS) -o ipp14 ipp14.o libbackend.a $(LIBS) | |
28 | + #$(RM) http | |
29 | + #$(LN) ipp14 http | |
30 | + | |
31 | + | |
32 | +# | |
33 | # lpd | |
34 | # | |
35 | ||
36 | --- /dev/null | |
37 | +++ b/backend/ipp14.c | |
38 | @@ -0,0 +1,1953 @@ | |
39 | +/* | |
40 | + * "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $" | |
41 | + * | |
42 | + * IPP backend for the Common UNIX Printing System (CUPS). | |
43 | + * | |
44 | + * Copyright 2007-2010 by Apple Inc. | |
45 | + * Copyright 1997-2007 by Easy Software Products, all rights reserved. | |
46 | + * | |
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/". | |
52 | + * | |
53 | + * This file is subject to the Apple OS-Developed Software exception. | |
54 | + * | |
55 | + * Contents: | |
56 | + * | |
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 | |
66 | + * remotely. | |
67 | + * sigterm_handler() - Handle 'terminate' signals that stop the backend. | |
68 | + */ | |
69 | + | |
70 | +/* | |
71 | + * Include necessary headers. | |
72 | + */ | |
73 | + | |
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> | |
79 | + | |
80 | +/* | |
81 | + * Globals... | |
82 | + */ | |
83 | + | |
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 */ | |
88 | +#ifdef __APPLE__ | |
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? */ | |
93 | + | |
94 | + | |
95 | +/* | |
96 | + * Local functions... | |
97 | + */ | |
98 | + | |
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); | |
104 | +#ifdef HAVE_LIBZ | |
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); | |
110 | + | |
111 | +#ifdef __APPLE__ | |
112 | +static int run_pictwps_filter(char **argv, const char *filename); | |
113 | +#endif /* __APPLE__ */ | |
114 | +static void sigterm_handler(int sig); | |
115 | + | |
116 | + | |
117 | +/* | |
118 | + * 'main()' - Send a file to the printer or server. | |
119 | + * | |
120 | + * Usage: | |
121 | + * | |
122 | + * printer-uri job-id user title copies options [file] | |
123 | + */ | |
124 | + | |
125 | +int /* O - Exit status */ | |
126 | +main(int argc, /* I - Number of command-line args */ | |
127 | + char *argv[]) /* I - Command-line arguments */ | |
128 | +{ | |
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", | |
185 | + "marker-colors", | |
186 | + "marker-high-levels", | |
187 | + "marker-levels", | |
188 | + "marker-low-levels", | |
189 | + "marker-message", | |
190 | + "marker-names", | |
191 | + "marker-types", | |
192 | + "printer-is-accepting-jobs", | |
193 | + "printer-state", | |
194 | + "printer-state-message", | |
195 | + "printer-state-reasons", | |
196 | + }; | |
197 | + static const char * const jattrs[] = | |
198 | + { /* Job attributes we want */ | |
199 | + "job-media-sheets-completed", | |
200 | + "job-state" | |
201 | + }; | |
202 | + | |
203 | + | |
204 | + /* | |
205 | + * Make sure status messages are not buffered... | |
206 | + */ | |
207 | + | |
208 | + setbuf(stderr, NULL); | |
209 | + | |
210 | + /* | |
211 | + * Ignore SIGPIPE and catch SIGTERM signals... | |
212 | + */ | |
213 | + | |
214 | +#ifdef HAVE_SIGSET | |
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); | |
221 | + | |
222 | + sigemptyset(&action.sa_mask); | |
223 | + sigaddset(&action.sa_mask, SIGTERM); | |
224 | + action.sa_handler = sigterm_handler; | |
225 | + sigaction(SIGTERM, &action, NULL); | |
226 | +#else | |
227 | + signal(SIGPIPE, SIG_IGN); | |
228 | + signal(SIGTERM, sigterm_handler); | |
229 | +#endif /* HAVE_SIGSET */ | |
230 | + | |
231 | + /* | |
232 | + * Check command-line... | |
233 | + */ | |
234 | + | |
235 | + if (argc == 1) | |
236 | + { | |
237 | + char *s; | |
238 | + | |
239 | + if ((s = strrchr(argv[0], '/')) != NULL) | |
240 | + s ++; | |
241 | + else | |
242 | + s = argv[0]; | |
243 | + | |
244 | + printf("network %s \"Unknown\" \"%s (%s)\"\n", | |
245 | + s, _cupsLangString(cupsLangDefault(), | |
246 | + _("Internet Printing Protocol")), s); | |
247 | + return (CUPS_BACKEND_OK); | |
248 | + } | |
249 | + else if (argc < 6) | |
250 | + { | |
251 | + _cupsLangPrintf(stderr, | |
252 | + _("Usage: %s job-id user title copies options [file]\n"), | |
253 | + argv[0]); | |
254 | + return (CUPS_BACKEND_STOP); | |
255 | + } | |
256 | + | |
257 | + /* | |
258 | + * Get the (final) content type... | |
259 | + */ | |
260 | + | |
261 | + if ((content_type = getenv("CONTENT_TYPE")) == NULL) | |
262 | + content_type = "application/octet-stream"; | |
263 | + | |
264 | + if ((final_content_type = getenv("FINAL_CONTENT_TYPE")) == NULL) | |
265 | + { | |
266 | + final_content_type = content_type; | |
267 | + | |
268 | + if (!strncmp(final_content_type, "printer/", 8)) | |
269 | + final_content_type = "application/vnd.cups-raw"; | |
270 | + } | |
271 | + | |
272 | + /* | |
273 | + * Extract the hostname and printer name from the URI... | |
274 | + */ | |
275 | + | |
276 | + if ((device_uri = cupsBackendDeviceURI(argv)) == NULL) | |
277 | + return (CUPS_BACKEND_FAILED); | |
278 | + | |
279 | + httpSeparateURI(HTTP_URI_CODING_ALL, device_uri, scheme, sizeof(scheme), | |
280 | + username, sizeof(username), hostname, sizeof(hostname), &port, | |
281 | + resource, sizeof(resource)); | |
282 | + | |
283 | + if (!port) | |
284 | + port = IPP_PORT; /* Default to port 631 */ | |
285 | + | |
286 | + if (!strcmp(scheme, "https")) | |
287 | + cupsSetEncryption(HTTP_ENCRYPT_ALWAYS); | |
288 | + else | |
289 | + cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED); | |
290 | + | |
291 | + /* | |
292 | + * See if there are any options... | |
293 | + */ | |
294 | + | |
295 | + compression = 0; | |
296 | + version = 11; | |
297 | + waitjob = 1; | |
298 | + waitprinter = 1; | |
299 | + contimeout = 7 * 24 * 60 * 60; | |
300 | + | |
301 | + if ((optptr = strchr(resource, '?')) != NULL) | |
302 | + { | |
303 | + /* | |
304 | + * Yup, terminate the device name string and move to the first | |
305 | + * character of the optptr... | |
306 | + */ | |
307 | + | |
308 | + *optptr++ = '\0'; | |
309 | + | |
310 | + /* | |
311 | + * Then parse the optptr... | |
312 | + */ | |
313 | + | |
314 | + while (*optptr) | |
315 | + { | |
316 | + /* | |
317 | + * Get the name... | |
318 | + */ | |
319 | + | |
320 | + name = optptr; | |
321 | + | |
322 | + while (*optptr && *optptr != '=' && *optptr != '+' && *optptr != '&') | |
323 | + optptr ++; | |
324 | + | |
325 | + if ((sep = *optptr) != '\0') | |
326 | + *optptr++ = '\0'; | |
327 | + | |
328 | + if (sep == '=') | |
329 | + { | |
330 | + /* | |
331 | + * Get the value... | |
332 | + */ | |
333 | + | |
334 | + value = optptr; | |
335 | + | |
336 | + while (*optptr && *optptr != '+' && *optptr != '&') | |
337 | + optptr ++; | |
338 | + | |
339 | + if (*optptr) | |
340 | + *optptr++ = '\0'; | |
341 | + } | |
342 | + else | |
343 | + value = (char *)""; | |
344 | + | |
345 | + /* | |
346 | + * Process the option... | |
347 | + */ | |
348 | + | |
349 | + if (!strcasecmp(name, "waitjob")) | |
350 | + { | |
351 | + /* | |
352 | + * Wait for job completion? | |
353 | + */ | |
354 | + | |
355 | + waitjob = !strcasecmp(value, "on") || | |
356 | + !strcasecmp(value, "yes") || | |
357 | + !strcasecmp(value, "true"); | |
358 | + } | |
359 | + else if (!strcasecmp(name, "waitprinter")) | |
360 | + { | |
361 | + /* | |
362 | + * Wait for printer idle? | |
363 | + */ | |
364 | + | |
365 | + waitprinter = !strcasecmp(value, "on") || | |
366 | + !strcasecmp(value, "yes") || | |
367 | + !strcasecmp(value, "true"); | |
368 | + } | |
369 | + else if (!strcasecmp(name, "encryption")) | |
370 | + { | |
371 | + /* | |
372 | + * Enable/disable encryption? | |
373 | + */ | |
374 | + | |
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); | |
383 | + else | |
384 | + { | |
385 | + _cupsLangPrintf(stderr, | |
386 | + _("ERROR: Unknown encryption option value \"%s\"!\n"), | |
387 | + value); | |
388 | + } | |
389 | + } | |
390 | + else if (!strcasecmp(name, "version")) | |
391 | + { | |
392 | + if (!strcmp(value, "1.0")) | |
393 | + version = 10; | |
394 | + else if (!strcmp(value, "1.1")) | |
395 | + version = 11; | |
396 | + else if (!strcmp(value, "2.0")) | |
397 | + version = 20; | |
398 | + else if (!strcmp(value, "2.1")) | |
399 | + version = 21; | |
400 | + else | |
401 | + { | |
402 | + _cupsLangPrintf(stderr, | |
403 | + _("ERROR: Unknown version option value \"%s\"!\n"), | |
404 | + value); | |
405 | + } | |
406 | + } | |
407 | +#ifdef HAVE_LIBZ | |
408 | + else if (!strcasecmp(name, "compression")) | |
409 | + { | |
410 | + compression = !strcasecmp(value, "true") || | |
411 | + !strcasecmp(value, "yes") || | |
412 | + !strcasecmp(value, "on") || | |
413 | + !strcasecmp(value, "gzip"); | |
414 | + } | |
415 | +#endif /* HAVE_LIBZ */ | |
416 | + else if (!strcasecmp(name, "contimeout")) | |
417 | + { | |
418 | + /* | |
419 | + * Set the connection timeout... | |
420 | + */ | |
421 | + | |
422 | + if (atoi(value) > 0) | |
423 | + contimeout = atoi(value); | |
424 | + } | |
425 | + else | |
426 | + { | |
427 | + /* | |
428 | + * Unknown option... | |
429 | + */ | |
430 | + | |
431 | + _cupsLangPrintf(stderr, | |
432 | + _("ERROR: Unknown option \"%s\" with value \"%s\"!\n"), | |
433 | + name, value); | |
434 | + } | |
435 | + } | |
436 | + } | |
437 | + | |
438 | + /* | |
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 | |
441 | + * file. | |
442 | + */ | |
443 | + | |
444 | + if (argc == 6) | |
445 | + { | |
446 | + /* | |
447 | + * Copy stdin to a temporary file... | |
448 | + */ | |
449 | + | |
450 | + int fd; /* File descriptor */ | |
451 | + http_addrlist_t *addrlist; /* Address list */ | |
452 | + off_t tbytes; /* Total bytes copied */ | |
453 | + | |
454 | + | |
455 | + fputs("STATE: +connecting-to-device\n", stderr); | |
456 | + fprintf(stderr, "DEBUG: Looking up \"%s\"...\n", hostname); | |
457 | + | |
458 | + if ((addrlist = httpAddrGetList(hostname, AF_UNSPEC, "1")) == NULL) | |
459 | + { | |
460 | + _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"), | |
461 | + hostname); | |
462 | + return (CUPS_BACKEND_STOP); | |
463 | + } | |
464 | + | |
465 | + snmp_fd = _cupsSNMPOpen(addrlist->addr.addr.sa_family); | |
466 | + | |
467 | + if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0) | |
468 | + { | |
469 | + _cupsLangPrintError("ERROR", _("Unable to create temporary file")); | |
470 | + return (CUPS_BACKEND_FAILED); | |
471 | + } | |
472 | + | |
473 | + _cupsLangPuts(stderr, _("INFO: Copying print data...\n")); | |
474 | + | |
475 | + tbytes = backendRunLoop(-1, fd, snmp_fd, &(addrlist->addr), 0, 0, | |
476 | + backendNetworkSideCB); | |
477 | + | |
478 | + if (snmp_fd >= 0) | |
479 | + _cupsSNMPClose(snmp_fd); | |
480 | + | |
481 | + httpAddrFreeList(addrlist); | |
482 | + | |
483 | + close(fd); | |
484 | + | |
485 | + /* | |
486 | + * Don't try printing files less than 2 bytes... | |
487 | + */ | |
488 | + | |
489 | + if (tbytes <= 1) | |
490 | + { | |
491 | + _cupsLangPuts(stderr, _("ERROR: Empty print file!\n")); | |
492 | + unlink(tmpfilename); | |
493 | + return (CUPS_BACKEND_FAILED); | |
494 | + } | |
495 | + | |
496 | + /* | |
497 | + * Point to the single file from stdin... | |
498 | + */ | |
499 | + | |
500 | + filename = tmpfilename; | |
501 | + num_files = 1; | |
502 | + files = &filename; | |
503 | + send_options = 0; | |
504 | + } | |
505 | + else | |
506 | + { | |
507 | + /* | |
508 | + * Point to the files on the command-line... | |
509 | + */ | |
510 | + | |
511 | + num_files = argc - 6; | |
512 | + files = argv + 6; | |
513 | + send_options = 1; | |
514 | + | |
515 | +#ifdef HAVE_LIBZ | |
516 | + if (compression) | |
517 | + compress_files(num_files, files); | |
518 | +#endif /* HAVE_LIBZ */ | |
519 | + } | |
520 | + | |
521 | + fprintf(stderr, "DEBUG: %d files to send in job...\n", num_files); | |
522 | + | |
523 | + /* | |
524 | + * Set the authentication info, if any... | |
525 | + */ | |
526 | + | |
527 | + cupsSetPasswordCB(password_cb); | |
528 | + | |
529 | + if (username[0]) | |
530 | + { | |
531 | + /* | |
532 | + * Use authenticaion information in the device URI... | |
533 | + */ | |
534 | + | |
535 | + if ((password = strchr(username, ':')) != NULL) | |
536 | + *password++ = '\0'; | |
537 | + | |
538 | + cupsSetUser(username); | |
539 | + } | |
540 | + else if (!getuid()) | |
541 | + { | |
542 | + /* | |
543 | + * Try loading authentication information from the environment. | |
544 | + */ | |
545 | + | |
546 | + const char *ptr = getenv("AUTH_USERNAME"); | |
547 | + | |
548 | + if (ptr) | |
549 | + cupsSetUser(ptr); | |
550 | + | |
551 | + password = getenv("AUTH_PASSWORD"); | |
552 | + } | |
553 | + | |
554 | + /* | |
555 | + * Try connecting to the remote server... | |
556 | + */ | |
557 | + | |
558 | + delay = 5; | |
559 | + recoverable = 0; | |
560 | + start_time = time(NULL); | |
561 | + | |
562 | + fputs("STATE: +connecting-to-device\n", stderr); | |
563 | + | |
564 | + do | |
565 | + { | |
566 | + fprintf(stderr, "DEBUG: Connecting to %s:%d\n", hostname, port); | |
567 | + _cupsLangPuts(stderr, _("INFO: Connecting to printer...\n")); | |
568 | + | |
569 | + if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL) | |
570 | + { | |
571 | + if (job_cancelled) | |
572 | + break; | |
573 | + | |
574 | + if (getenv("CLASS") != NULL) | |
575 | + { | |
576 | + /* | |
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. | |
581 | + */ | |
582 | + | |
583 | + _cupsLangPuts(stderr, | |
584 | + _("INFO: Unable to contact printer, queuing on next " | |
585 | + "printer in class...\n")); | |
586 | + | |
587 | + if (tmpfilename[0]) | |
588 | + unlink(tmpfilename); | |
589 | + | |
590 | + /* | |
591 | + * Sleep 5 seconds to keep the job from requeuing too rapidly... | |
592 | + */ | |
593 | + | |
594 | + sleep(5); | |
595 | + | |
596 | + return (CUPS_BACKEND_FAILED); | |
597 | + } | |
598 | + | |
599 | + if (errno == ECONNREFUSED || errno == EHOSTDOWN || | |
600 | + errno == EHOSTUNREACH) | |
601 | + { | |
602 | + if (contimeout && (time(NULL) - start_time) > contimeout) | |
603 | + { | |
604 | + _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n")); | |
605 | + return (CUPS_BACKEND_FAILED); | |
606 | + } | |
607 | + | |
608 | + recoverable = 1; | |
609 | + | |
610 | + _cupsLangPrintf(stderr, | |
611 | + _("WARNING: recoverable: Network host \'%s\' is busy; " | |
612 | + "will retry in %d seconds...\n"), | |
613 | + hostname, delay); | |
614 | + | |
615 | + sleep(delay); | |
616 | + | |
617 | + if (delay < 30) | |
618 | + delay += 5; | |
619 | + } | |
620 | + else if (h_errno) | |
621 | + { | |
622 | + _cupsLangPrintf(stderr, _("ERROR: Unable to locate printer \'%s\'!\n"), | |
623 | + hostname); | |
624 | + return (CUPS_BACKEND_STOP); | |
625 | + } | |
626 | + else | |
627 | + { | |
628 | + recoverable = 1; | |
629 | + | |
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")); | |
634 | + sleep(30); | |
635 | + } | |
636 | + | |
637 | + if (job_cancelled) | |
638 | + break; | |
639 | + } | |
640 | + } | |
641 | + while (http == NULL); | |
642 | + | |
643 | + if (job_cancelled || !http) | |
644 | + { | |
645 | + if (tmpfilename[0]) | |
646 | + unlink(tmpfilename); | |
647 | + | |
648 | + return (CUPS_BACKEND_FAILED); | |
649 | + } | |
650 | + | |
651 | + fputs("STATE: -connecting-to-device\n", stderr); | |
652 | + _cupsLangPuts(stderr, _("INFO: Connected to printer...\n")); | |
653 | + | |
654 | +#ifdef AF_INET6 | |
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)); | |
659 | + else | |
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)); | |
665 | + | |
666 | + /* | |
667 | + * See if the printer supports SNMP... | |
668 | + */ | |
669 | + | |
670 | + if ((snmp_fd = _cupsSNMPOpen(http->hostaddr->addr.sa_family)) >= 0) | |
671 | + have_supplies = !backendSNMPSupplies(snmp_fd, http->hostaddr, &start_count, | |
672 | + NULL); | |
673 | + else | |
674 | + have_supplies = start_count = 0; | |
675 | + | |
676 | + /* | |
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... | |
680 | + */ | |
681 | + | |
682 | + httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), scheme, NULL, hostname, | |
683 | + port, resource); | |
684 | + | |
685 | + /* | |
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... | |
689 | + */ | |
690 | + | |
691 | + copies_sup = NULL; | |
692 | + format_sup = NULL; | |
693 | + supported = NULL; | |
694 | + | |
695 | + do | |
696 | + { | |
697 | + /* | |
698 | + * Check for side-channel requests... | |
699 | + */ | |
700 | + | |
701 | + backendCheckSideChannel(snmp_fd, http->hostaddr); | |
702 | + | |
703 | + /* | |
704 | + * Build the IPP request... | |
705 | + */ | |
706 | + | |
707 | + request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); | |
708 | + request->request.op.version[0] = version / 10; | |
709 | + request->request.op.version[1] = version % 10; | |
710 | + | |
711 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
712 | + NULL, uri); | |
713 | + | |
714 | + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
715 | + "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]), | |
716 | + NULL, pattrs); | |
717 | + | |
718 | + /* | |
719 | + * Do the request... | |
720 | + */ | |
721 | + | |
722 | + fputs("DEBUG: Getting supported attributes...\n", stderr); | |
723 | + | |
724 | + if (http->version < HTTP_1_1) | |
725 | + httpReconnect(http); | |
726 | + | |
727 | + if ((supported = cupsDoRequest(http, request, resource)) == NULL) | |
728 | + ipp_status = cupsLastError(); | |
729 | + else | |
730 | + ipp_status = supported->request.status.status_code; | |
731 | + | |
732 | + if (ipp_status > IPP_OK_CONFLICT) | |
733 | + { | |
734 | + if (ipp_status == IPP_PRINTER_BUSY || | |
735 | + ipp_status == IPP_SERVICE_UNAVAILABLE) | |
736 | + { | |
737 | + if (contimeout && (time(NULL) - start_time) > contimeout) | |
738 | + { | |
739 | + _cupsLangPuts(stderr, _("ERROR: Printer not responding!\n")); | |
740 | + return (CUPS_BACKEND_FAILED); | |
741 | + } | |
742 | + | |
743 | + recoverable = 1; | |
744 | + | |
745 | + _cupsLangPrintf(stderr, | |
746 | + _("WARNING: recoverable: Network host \'%s\' is busy; " | |
747 | + "will retry in %d seconds...\n"), | |
748 | + hostname, delay); | |
749 | + | |
750 | + report_printer_state(supported, 0); | |
751 | + | |
752 | + sleep(delay); | |
753 | + | |
754 | + if (delay < 30) | |
755 | + delay += 5; | |
756 | + } | |
757 | + else if ((ipp_status == IPP_BAD_REQUEST || | |
758 | + ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10) | |
759 | + { | |
760 | + /* | |
761 | + * Switch to IPP/1.0... | |
762 | + */ | |
763 | + | |
764 | + _cupsLangPrintf(stderr, | |
765 | + _("INFO: Printer does not support IPP/%d.%d, trying " | |
766 | + "IPP/1.0...\n"), version / 10, version % 10); | |
767 | + version = 10; | |
768 | + httpReconnect(http); | |
769 | + } | |
770 | + else if (ipp_status == IPP_NOT_FOUND) | |
771 | + { | |
772 | + _cupsLangPuts(stderr, _("ERROR: Destination printer does not exist!\n")); | |
773 | + | |
774 | + if (supported) | |
775 | + ippDelete(supported); | |
776 | + | |
777 | + return (CUPS_BACKEND_STOP); | |
778 | + } | |
779 | + else if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN) | |
780 | + { | |
781 | + if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE), | |
782 | + "Negotiate", 9)) | |
783 | + auth_info_required = "negotiate"; | |
784 | + | |
785 | + fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required); | |
786 | + return (CUPS_BACKEND_AUTH_REQUIRED); | |
787 | + } | |
788 | + else | |
789 | + { | |
790 | + _cupsLangPrintf(stderr, | |
791 | + _("ERROR: Unable to get printer status (%s)!\n"), | |
792 | + cupsLastErrorString()); | |
793 | + sleep(10); | |
794 | + } | |
795 | + | |
796 | + if (supported) | |
797 | + ippDelete(supported); | |
798 | + | |
799 | + continue; | |
800 | + } | |
801 | + else if ((copies_sup = ippFindAttribute(supported, "copies-supported", | |
802 | + IPP_TAG_RANGE)) != NULL) | |
803 | + { | |
804 | + /* | |
805 | + * Has the "copies-supported" attribute - does it have an upper | |
806 | + * bound > 1? | |
807 | + */ | |
808 | + | |
809 | + if (copies_sup->values[0].range.upper <= 1) | |
810 | + copies_sup = NULL; /* No */ | |
811 | + } | |
812 | + | |
813 | + format_sup = ippFindAttribute(supported, "document-format-supported", | |
814 | + IPP_TAG_MIMETYPE); | |
815 | + | |
816 | + if (format_sup) | |
817 | + { | |
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); | |
823 | + } | |
824 | + | |
825 | + report_printer_state(supported, 0); | |
826 | + } | |
827 | + while (ipp_status > IPP_OK_CONFLICT); | |
828 | + | |
829 | + /* | |
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... | |
832 | + */ | |
833 | + | |
834 | + if (getenv("CLASS") != NULL) | |
835 | + { | |
836 | + printer_state = ippFindAttribute(supported, "printer-state", | |
837 | + IPP_TAG_ENUM); | |
838 | + printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs", | |
839 | + IPP_TAG_BOOLEAN); | |
840 | + | |
841 | + if (printer_state == NULL || | |
842 | + (printer_state->values[0].integer > IPP_PRINTER_PROCESSING && | |
843 | + waitprinter) || | |
844 | + printer_accepting == NULL || | |
845 | + !printer_accepting->values[0].boolean) | |
846 | + { | |
847 | + /* | |
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. | |
852 | + */ | |
853 | + | |
854 | + _cupsLangPuts(stderr, | |
855 | + _("INFO: Unable to contact printer, queuing on next " | |
856 | + "printer in class...\n")); | |
857 | + | |
858 | + ippDelete(supported); | |
859 | + httpClose(http); | |
860 | + | |
861 | + if (tmpfilename[0]) | |
862 | + unlink(tmpfilename); | |
863 | + | |
864 | + /* | |
865 | + * Sleep 5 seconds to keep the job from requeuing too rapidly... | |
866 | + */ | |
867 | + | |
868 | + sleep(5); | |
869 | + | |
870 | + return (CUPS_BACKEND_FAILED); | |
871 | + } | |
872 | + } | |
873 | + | |
874 | + if (recoverable) | |
875 | + { | |
876 | + /* | |
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... | |
880 | + */ | |
881 | + | |
882 | + fputs("INFO: recovered: \n", stderr); | |
883 | + sleep(5); | |
884 | + } | |
885 | + | |
886 | + /* | |
887 | + * See if the printer supports multiple copies... | |
888 | + */ | |
889 | + | |
890 | + copies = atoi(argv[4]); | |
891 | + | |
892 | + if (copies_sup || argc < 7) | |
893 | + { | |
894 | + copies_remaining = 1; | |
895 | + | |
896 | + if (argc < 7) | |
897 | + copies = 1; | |
898 | + } | |
899 | + else | |
900 | + copies_remaining = copies; | |
901 | + | |
902 | + /* | |
903 | + * Then issue the print-job request... | |
904 | + */ | |
905 | + | |
906 | + job_id = 0; | |
907 | + | |
908 | + while (copies_remaining > 0) | |
909 | + { | |
910 | + /* | |
911 | + * Check for side-channel requests... | |
912 | + */ | |
913 | + | |
914 | + backendCheckSideChannel(snmp_fd, http->hostaddr); | |
915 | + | |
916 | + /* | |
917 | + * Build the IPP request... | |
918 | + */ | |
919 | + | |
920 | + if (job_cancelled) | |
921 | + break; | |
922 | + | |
923 | + if (num_files > 1) | |
924 | + request = ippNewRequest(IPP_CREATE_JOB); | |
925 | + else | |
926 | + request = ippNewRequest(IPP_PRINT_JOB); | |
927 | + | |
928 | + request->request.op.version[0] = version / 10; | |
929 | + request->request.op.version[1] = version % 10; | |
930 | + | |
931 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
932 | + NULL, uri); | |
933 | + | |
934 | + fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri); | |
935 | + | |
936 | + if (argv[2][0]) | |
937 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, | |
938 | + "requesting-user-name", NULL, argv[2]); | |
939 | + | |
940 | + fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]); | |
941 | + | |
942 | + /* | |
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)... | |
946 | + */ | |
947 | + | |
948 | + if (argv[3][0] && copies_sup) | |
949 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, | |
950 | + argv[3]); | |
951 | + | |
952 | + fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]); | |
953 | + | |
954 | +#ifdef HAVE_LIBZ | |
955 | + if (compression) | |
956 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
957 | + "compression", NULL, "gzip"); | |
958 | +#endif /* HAVE_LIBZ */ | |
959 | + | |
960 | + /* | |
961 | + * Handle options on the command-line... | |
962 | + */ | |
963 | + | |
964 | + options = NULL; | |
965 | + num_options = cupsParseOptions(argv[5], 0, &options); | |
966 | + | |
967 | +#ifdef __APPLE__ | |
968 | + if (!strcasecmp(final_content_type, "application/pictwps") && | |
969 | + num_files == 1) | |
970 | + { | |
971 | + if (format_sup != NULL) | |
972 | + { | |
973 | + for (i = 0; i < format_sup->num_values; i ++) | |
974 | + if (!strcasecmp(final_content_type, format_sup->values[i].string.text)) | |
975 | + break; | |
976 | + } | |
977 | + | |
978 | + if (format_sup == NULL || i >= format_sup->num_values) | |
979 | + { | |
980 | + /* | |
981 | + * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X) | |
982 | + * so convert the document to PostScript... | |
983 | + */ | |
984 | + | |
985 | + if (run_pictwps_filter(argv, files[0])) | |
986 | + { | |
987 | + if (pstmpname[0]) | |
988 | + unlink(pstmpname); | |
989 | + | |
990 | + if (tmpfilename[0]) | |
991 | + unlink(tmpfilename); | |
992 | + | |
993 | + return (CUPS_BACKEND_FAILED); | |
994 | + } | |
995 | + | |
996 | + files[0] = pstmpname; | |
997 | + | |
998 | + /* | |
999 | + * Change the MIME type to application/postscript and change the | |
1000 | + * number of copies to 1... | |
1001 | + */ | |
1002 | + | |
1003 | + final_content_type = "application/postscript"; | |
1004 | + copies = 1; | |
1005 | + copies_remaining = 1; | |
1006 | + send_options = 0; | |
1007 | + } | |
1008 | + } | |
1009 | +#endif /* __APPLE__ */ | |
1010 | + | |
1011 | + if (format_sup != NULL) | |
1012 | + { | |
1013 | + for (i = 0; i < format_sup->num_values; i ++) | |
1014 | + if (!strcasecmp(final_content_type, format_sup->values[i].string.text)) | |
1015 | + break; | |
1016 | + | |
1017 | + if (i < format_sup->num_values) | |
1018 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, | |
1019 | + "document-format", NULL, final_content_type); | |
1020 | + } | |
1021 | + | |
1022 | + if (copies_sup && version > 10 && send_options) | |
1023 | + { | |
1024 | + /* | |
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. | |
1031 | + */ | |
1032 | + | |
1033 | + cupsEncodeOptions(request, num_options, options); | |
1034 | + | |
1035 | + ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies", | |
1036 | + copies); | |
1037 | + } | |
1038 | + | |
1039 | + cupsFreeOptions(num_options, options); | |
1040 | + | |
1041 | + /* | |
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... | |
1046 | + */ | |
1047 | + | |
1048 | + if (!copies_sup) | |
1049 | + httpReconnect(http); | |
1050 | + | |
1051 | + /* | |
1052 | + * Do the request... | |
1053 | + */ | |
1054 | + | |
1055 | + if (http->version < HTTP_1_1) | |
1056 | + httpReconnect(http); | |
1057 | + | |
1058 | + if (num_files > 1) | |
1059 | + response = cupsDoRequest(http, request, resource); | |
1060 | + else | |
1061 | + response = cupsDoFileRequest(http, request, resource, files[0]); | |
1062 | + | |
1063 | + ipp_status = cupsLastError(); | |
1064 | + | |
1065 | + if (ipp_status > IPP_OK_CONFLICT) | |
1066 | + { | |
1067 | + job_id = 0; | |
1068 | + | |
1069 | + if (job_cancelled) | |
1070 | + break; | |
1071 | + | |
1072 | + if (ipp_status == IPP_SERVICE_UNAVAILABLE || | |
1073 | + ipp_status == IPP_PRINTER_BUSY) | |
1074 | + { | |
1075 | + _cupsLangPuts(stderr, | |
1076 | + _("INFO: Printer busy; will retry in 10 seconds...\n")); | |
1077 | + sleep(10); | |
1078 | + } | |
1079 | + else if ((ipp_status == IPP_BAD_REQUEST || | |
1080 | + ipp_status == IPP_VERSION_NOT_SUPPORTED) && version > 10) | |
1081 | + { | |
1082 | + /* | |
1083 | + * Switch to IPP/1.0... | |
1084 | + */ | |
1085 | + | |
1086 | + _cupsLangPrintf(stderr, | |
1087 | + _("INFO: Printer does not support IPP/%d.%d, trying " | |
1088 | + "IPP/1.0...\n"), version / 10, version % 10); | |
1089 | + version = 10; | |
1090 | + httpReconnect(http); | |
1091 | + } | |
1092 | + else | |
1093 | + { | |
1094 | + /* | |
1095 | + * Update auth-info-required as needed... | |
1096 | + */ | |
1097 | + | |
1098 | + _cupsLangPrintf(stderr, _("ERROR: Print file was not accepted (%s)!\n"), | |
1099 | + cupsLastErrorString()); | |
1100 | + | |
1101 | + if (ipp_status == IPP_NOT_AUTHORIZED || ipp_status == IPP_FORBIDDEN) | |
1102 | + { | |
1103 | + fprintf(stderr, "DEBUG: WWW-Authenticate=\"%s\"\n", | |
1104 | + httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE)); | |
1105 | + | |
1106 | + /* | |
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... | |
1111 | + */ | |
1112 | + | |
1113 | + if (!strncmp(httpGetField(http, HTTP_FIELD_WWW_AUTHENTICATE), | |
1114 | + "Negotiate", 9)) | |
1115 | + auth_info_required = "negotiate"; | |
1116 | + } | |
1117 | + } | |
1118 | + } | |
1119 | + else if ((job_id_attr = ippFindAttribute(response, "job-id", | |
1120 | + IPP_TAG_INTEGER)) == NULL) | |
1121 | + { | |
1122 | + _cupsLangPuts(stderr, | |
1123 | + _("NOTICE: Print file accepted - job ID unknown.\n")); | |
1124 | + job_id = 0; | |
1125 | + } | |
1126 | + else | |
1127 | + { | |
1128 | + job_id = job_id_attr->values[0].integer; | |
1129 | + _cupsLangPrintf(stderr, _("NOTICE: Print file accepted - job ID %d.\n"), | |
1130 | + job_id); | |
1131 | + } | |
1132 | + | |
1133 | + ippDelete(response); | |
1134 | + | |
1135 | + if (job_cancelled) | |
1136 | + break; | |
1137 | + | |
1138 | + if (job_id && num_files > 1) | |
1139 | + { | |
1140 | + for (i = 0; i < num_files; i ++) | |
1141 | + { | |
1142 | + /* | |
1143 | + * Check for side-channel requests... | |
1144 | + */ | |
1145 | + | |
1146 | + backendCheckSideChannel(snmp_fd, http->hostaddr); | |
1147 | + | |
1148 | + /* | |
1149 | + * Send the next file in the job... | |
1150 | + */ | |
1151 | + | |
1152 | + request = ippNewRequest(IPP_SEND_DOCUMENT); | |
1153 | + request->request.op.version[0] = version / 10; | |
1154 | + request->request.op.version[1] = version % 10; | |
1155 | + | |
1156 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
1157 | + NULL, uri); | |
1158 | + | |
1159 | + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", | |
1160 | + job_id); | |
1161 | + | |
1162 | + if (argv[2][0]) | |
1163 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, | |
1164 | + "requesting-user-name", NULL, argv[2]); | |
1165 | + | |
1166 | + if ((i + 1) == num_files) | |
1167 | + ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1); | |
1168 | + | |
1169 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE, | |
1170 | + "document-format", NULL, content_type); | |
1171 | + | |
1172 | + if (http->version < HTTP_1_1) | |
1173 | + httpReconnect(http); | |
1174 | + | |
1175 | + ippDelete(cupsDoFileRequest(http, request, resource, files[i])); | |
1176 | + | |
1177 | + if (cupsLastError() > IPP_OK_CONFLICT) | |
1178 | + { | |
1179 | + ipp_status = cupsLastError(); | |
1180 | + | |
1181 | + _cupsLangPrintf(stderr, | |
1182 | + _("ERROR: Unable to add file %d to job: %s\n"), | |
1183 | + job_id, cupsLastErrorString()); | |
1184 | + break; | |
1185 | + } | |
1186 | + } | |
1187 | + } | |
1188 | + | |
1189 | + if (ipp_status <= IPP_OK_CONFLICT && argc > 6) | |
1190 | + { | |
1191 | + fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1); | |
1192 | + copies_remaining --; | |
1193 | + } | |
1194 | + else if (ipp_status == IPP_SERVICE_UNAVAILABLE || | |
1195 | + ipp_status == IPP_PRINTER_BUSY) | |
1196 | + continue; | |
1197 | + else | |
1198 | + copies_remaining --; | |
1199 | + | |
1200 | + /* | |
1201 | + * Wait for the job to complete... | |
1202 | + */ | |
1203 | + | |
1204 | + if (!job_id || !waitjob) | |
1205 | + continue; | |
1206 | + | |
1207 | + _cupsLangPuts(stderr, _("INFO: Waiting for job to complete...\n")); | |
1208 | + | |
1209 | + for (delay = 1; !job_cancelled;) | |
1210 | + { | |
1211 | + /* | |
1212 | + * Check for side-channel requests... | |
1213 | + */ | |
1214 | + | |
1215 | + backendCheckSideChannel(snmp_fd, http->hostaddr); | |
1216 | + | |
1217 | + /* | |
1218 | + * Build an IPP_GET_JOB_ATTRIBUTES request... | |
1219 | + */ | |
1220 | + | |
1221 | + request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES); | |
1222 | + request->request.op.version[0] = version / 10; | |
1223 | + request->request.op.version[1] = version % 10; | |
1224 | + | |
1225 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
1226 | + NULL, uri); | |
1227 | + | |
1228 | + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", | |
1229 | + job_id); | |
1230 | + | |
1231 | + if (argv[2][0]) | |
1232 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, | |
1233 | + "requesting-user-name", NULL, argv[2]); | |
1234 | + | |
1235 | + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
1236 | + "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]), | |
1237 | + NULL, jattrs); | |
1238 | + | |
1239 | + /* | |
1240 | + * Do the request... | |
1241 | + */ | |
1242 | + | |
1243 | + if (!copies_sup || http->version < HTTP_1_1) | |
1244 | + httpReconnect(http); | |
1245 | + | |
1246 | + response = cupsDoRequest(http, request, resource); | |
1247 | + ipp_status = cupsLastError(); | |
1248 | + | |
1249 | + if (ipp_status == IPP_NOT_FOUND) | |
1250 | + { | |
1251 | + /* | |
1252 | + * Job has gone away and/or the server has no job history... | |
1253 | + */ | |
1254 | + | |
1255 | + ippDelete(response); | |
1256 | + | |
1257 | + ipp_status = IPP_OK; | |
1258 | + break; | |
1259 | + } | |
1260 | + | |
1261 | + if (ipp_status > IPP_OK_CONFLICT) | |
1262 | + { | |
1263 | + if (ipp_status != IPP_SERVICE_UNAVAILABLE && | |
1264 | + ipp_status != IPP_PRINTER_BUSY) | |
1265 | + { | |
1266 | + ippDelete(response); | |
1267 | + | |
1268 | + _cupsLangPrintf(stderr, | |
1269 | + _("ERROR: Unable to get job %d attributes (%s)!\n"), | |
1270 | + job_id, cupsLastErrorString()); | |
1271 | + break; | |
1272 | + } | |
1273 | + } | |
1274 | + | |
1275 | + if (response) | |
1276 | + { | |
1277 | + if ((job_state = ippFindAttribute(response, "job-state", | |
1278 | + IPP_TAG_ENUM)) != NULL) | |
1279 | + { | |
1280 | + /* | |
1281 | + * Stop polling if the job is finished or pending-held... | |
1282 | + */ | |
1283 | + | |
1284 | + if (job_state->values[0].integer > IPP_JOB_STOPPED) | |
1285 | + { | |
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); | |
1291 | + | |
1292 | + ippDelete(response); | |
1293 | + break; | |
1294 | + } | |
1295 | + } | |
1296 | + else | |
1297 | + { | |
1298 | + /* | |
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 | |
1301 | + * the job... | |
1302 | + */ | |
1303 | + | |
1304 | + fputs("DEBUG: No job-state available from printer - stopping queue.\n", | |
1305 | + stderr); | |
1306 | + ipp_status = IPP_INTERNAL_ERROR; | |
1307 | + break; | |
1308 | + } | |
1309 | + } | |
1310 | + | |
1311 | + ippDelete(response); | |
1312 | + | |
1313 | + /* | |
1314 | + * Check the printer state and report it if necessary... | |
1315 | + */ | |
1316 | + | |
1317 | + check_printer_state(http, uri, resource, argv[2], version, job_id); | |
1318 | + | |
1319 | + /* | |
1320 | + * Wait 1-10 seconds before polling again... | |
1321 | + */ | |
1322 | + | |
1323 | + sleep(delay); | |
1324 | + | |
1325 | + delay ++; | |
1326 | + if (delay > 10) | |
1327 | + delay = 1; | |
1328 | + } | |
1329 | + } | |
1330 | + | |
1331 | + /* | |
1332 | + * Cancel the job as needed... | |
1333 | + */ | |
1334 | + | |
1335 | + if (job_cancelled && job_id) | |
1336 | + cancel_job(http, uri, job_id, resource, argv[2], version); | |
1337 | + | |
1338 | + /* | |
1339 | + * Check the printer state and report it if necessary... | |
1340 | + */ | |
1341 | + | |
1342 | + check_printer_state(http, uri, resource, argv[2], version, job_id); | |
1343 | + | |
1344 | + /* | |
1345 | + * Collect the final page count as needed... | |
1346 | + */ | |
1347 | + | |
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); | |
1352 | + | |
1353 | +#ifdef HAVE_GSSAPI | |
1354 | + /* | |
1355 | + * See if we used Kerberos at all... | |
1356 | + */ | |
1357 | + | |
1358 | + if (http->gssctx) | |
1359 | + auth_info_required = "negotiate"; | |
1360 | +#endif /* HAVE_GSSAPI */ | |
1361 | + | |
1362 | + /* | |
1363 | + * Free memory... | |
1364 | + */ | |
1365 | + | |
1366 | + httpClose(http); | |
1367 | + | |
1368 | + ippDelete(supported); | |
1369 | + | |
1370 | + /* | |
1371 | + * Remove the temporary file(s) if necessary... | |
1372 | + */ | |
1373 | + | |
1374 | + if (tmpfilename[0]) | |
1375 | + unlink(tmpfilename); | |
1376 | + | |
1377 | +#ifdef HAVE_LIBZ | |
1378 | + if (compression) | |
1379 | + { | |
1380 | + for (i = 0; i < num_files; i ++) | |
1381 | + unlink(files[i]); | |
1382 | + } | |
1383 | +#endif /* HAVE_LIBZ */ | |
1384 | + | |
1385 | +#ifdef __APPLE__ | |
1386 | + if (pstmpname[0]) | |
1387 | + unlink(pstmpname); | |
1388 | +#endif /* __APPLE__ */ | |
1389 | + | |
1390 | + /* | |
1391 | + * Return the queue status... | |
1392 | + */ | |
1393 | + | |
1394 | + fprintf(stderr, "ATTR: auth-info-required=%s\n", auth_info_required); | |
1395 | + | |
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); | |
1402 | + else | |
1403 | + { | |
1404 | + _cupsLangPuts(stderr, _("INFO: Ready to print.\n")); | |
1405 | + return (CUPS_BACKEND_OK); | |
1406 | + } | |
1407 | +} | |
1408 | + | |
1409 | + | |
1410 | +/* | |
1411 | + * 'cancel_job()' - Cancel a print job. | |
1412 | + */ | |
1413 | + | |
1414 | +static void | |
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 */ | |
1421 | +{ | |
1422 | + ipp_t *request; /* Cancel-Job request */ | |
1423 | + | |
1424 | + | |
1425 | + _cupsLangPuts(stderr, _("INFO: Canceling print job...\n")); | |
1426 | + | |
1427 | + request = ippNewRequest(IPP_CANCEL_JOB); | |
1428 | + request->request.op.version[0] = version / 10; | |
1429 | + request->request.op.version[1] = version % 10; | |
1430 | + | |
1431 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
1432 | + NULL, uri); | |
1433 | + ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id); | |
1434 | + | |
1435 | + if (user && user[0]) | |
1436 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, | |
1437 | + "requesting-user-name", NULL, user); | |
1438 | + | |
1439 | + /* | |
1440 | + * Do the request... | |
1441 | + */ | |
1442 | + | |
1443 | + if (http->version < HTTP_1_1) | |
1444 | + httpReconnect(http); | |
1445 | + | |
1446 | + ippDelete(cupsDoRequest(http, request, resource)); | |
1447 | + | |
1448 | + if (cupsLastError() > IPP_OK_CONFLICT) | |
1449 | + _cupsLangPrintf(stderr, _("ERROR: Unable to cancel job %d: %s\n"), id, | |
1450 | + cupsLastErrorString()); | |
1451 | +} | |
1452 | + | |
1453 | + | |
1454 | +/* | |
1455 | + * 'check_printer_state()' - Check the printer state... | |
1456 | + */ | |
1457 | + | |
1458 | +static void | |
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 */ | |
1466 | +{ | |
1467 | + ipp_t *request, /* IPP request */ | |
1468 | + *response; /* IPP response */ | |
1469 | + static const char * const attrs[] = /* Attributes we want */ | |
1470 | + { | |
1471 | + "com.apple.print.recoverable-message", | |
1472 | + "marker-colors", | |
1473 | + "marker-levels", | |
1474 | + "marker-message", | |
1475 | + "marker-names", | |
1476 | + "marker-types", | |
1477 | + "printer-state-message", | |
1478 | + "printer-state-reasons" | |
1479 | + }; | |
1480 | + | |
1481 | + | |
1482 | + /* | |
1483 | + * Check on the printer state... | |
1484 | + */ | |
1485 | + | |
1486 | + request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES); | |
1487 | + request->request.op.version[0] = version / 10; | |
1488 | + request->request.op.version[1] = version % 10; | |
1489 | + | |
1490 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
1491 | + NULL, uri); | |
1492 | + | |
1493 | + if (user && user[0]) | |
1494 | + ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, | |
1495 | + "requesting-user-name", NULL, user); | |
1496 | + | |
1497 | + ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, | |
1498 | + "requested-attributes", | |
1499 | + (int)(sizeof(attrs) / sizeof(attrs[0])), NULL, attrs); | |
1500 | + | |
1501 | + /* | |
1502 | + * Do the request... | |
1503 | + */ | |
1504 | + | |
1505 | + if (http->version < HTTP_1_1) | |
1506 | + httpReconnect(http); | |
1507 | + | |
1508 | + if ((response = cupsDoRequest(http, request, resource)) != NULL) | |
1509 | + { | |
1510 | + report_printer_state(response, job_id); | |
1511 | + ippDelete(response); | |
1512 | + } | |
1513 | +} | |
1514 | + | |
1515 | + | |
1516 | +#ifdef HAVE_LIBZ | |
1517 | +/* | |
1518 | + * 'compress_files()' - Compress print files... | |
1519 | + */ | |
1520 | + | |
1521 | +static void | |
1522 | +compress_files(int num_files, /* I - Number of files */ | |
1523 | + char **files) /* I - Files */ | |
1524 | +{ | |
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 */ | |
1534 | + | |
1535 | + | |
1536 | + fprintf(stderr, "DEBUG: Compressing %d job files...\n", num_files); | |
1537 | + for (i = 0; i < num_files; i ++) | |
1538 | + { | |
1539 | + if ((fd = cupsTempFd(filename, sizeof(filename))) < 0) | |
1540 | + { | |
1541 | + _cupsLangPrintf(stderr, | |
1542 | + _("ERROR: Unable to create temporary compressed print " | |
1543 | + "file: %s\n"), strerror(errno)); | |
1544 | + exit(CUPS_BACKEND_FAILED); | |
1545 | + } | |
1546 | + | |
1547 | + if ((out = cupsFileOpenFd(fd, "w9")) == NULL) | |
1548 | + { | |
1549 | + _cupsLangPrintf(stderr, | |
1550 | + _("ERROR: Unable to open temporary compressed print " | |
1551 | + "file: %s\n"), strerror(errno)); | |
1552 | + exit(CUPS_BACKEND_FAILED); | |
1553 | + } | |
1554 | + | |
1555 | + if ((in = cupsFileOpen(files[i], "r")) == NULL) | |
1556 | + { | |
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); | |
1562 | + } | |
1563 | + | |
1564 | + total = 0; | |
1565 | + while ((bytes = cupsFileRead(in, buffer, sizeof(buffer))) > 0) | |
1566 | + if (cupsFileWrite(out, buffer, bytes) < bytes) | |
1567 | + { | |
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); | |
1574 | + } | |
1575 | + else | |
1576 | + total += bytes; | |
1577 | + | |
1578 | + cupsFileClose(out); | |
1579 | + cupsFileClose(in); | |
1580 | + | |
1581 | + files[i] = strdup(filename); | |
1582 | + | |
1583 | + if (!stat(filename, &outinfo)) | |
1584 | + fprintf(stderr, | |
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); | |
1589 | + } | |
1590 | +} | |
1591 | +#endif /* HAVE_LIBZ */ | |
1592 | + | |
1593 | + | |
1594 | +/* | |
1595 | + * 'password_cb()' - Disable the password prompt for cupsDoFileRequest(). | |
1596 | + */ | |
1597 | + | |
1598 | +static const char * /* O - Password */ | |
1599 | +password_cb(const char *prompt) /* I - Prompt (not used) */ | |
1600 | +{ | |
1601 | + (void)prompt; | |
1602 | + | |
1603 | + /* | |
1604 | + * Remember that we need to authenticate... | |
1605 | + */ | |
1606 | + | |
1607 | + auth_info_required = "username,password"; | |
1608 | + | |
1609 | + if (password && *password && password_tries < 3) | |
1610 | + { | |
1611 | + password_tries ++; | |
1612 | + | |
1613 | + return (password); | |
1614 | + } | |
1615 | + else | |
1616 | + { | |
1617 | + /* | |
1618 | + * Give up after 3 tries or if we don't have a password to begin with... | |
1619 | + */ | |
1620 | + | |
1621 | + return (NULL); | |
1622 | + } | |
1623 | +} | |
1624 | + | |
1625 | + | |
1626 | +/* | |
1627 | + * 'report_attr()' - Report an IPP attribute value. | |
1628 | + */ | |
1629 | + | |
1630 | +static void | |
1631 | +report_attr(ipp_attribute_t *attr) /* I - Attribute */ | |
1632 | +{ | |
1633 | + int i; /* Looping var */ | |
1634 | + char value[1024], /* Value string */ | |
1635 | + *valptr, /* Pointer into value string */ | |
1636 | + *attrptr; /* Pointer into attribute value */ | |
1637 | + | |
1638 | + | |
1639 | + /* | |
1640 | + * Convert the attribute values into quoted strings... | |
1641 | + */ | |
1642 | + | |
1643 | + for (i = 0, valptr = value; | |
1644 | + i < attr->num_values && valptr < (value + sizeof(value) - 10); | |
1645 | + i ++) | |
1646 | + { | |
1647 | + if (i > 0) | |
1648 | + *valptr++ = ','; | |
1649 | + | |
1650 | + switch (attr->value_tag) | |
1651 | + { | |
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); | |
1657 | + break; | |
1658 | + | |
1659 | + case IPP_TAG_TEXT : | |
1660 | + case IPP_TAG_NAME : | |
1661 | + case IPP_TAG_KEYWORD : | |
1662 | + *valptr++ = '\"'; | |
1663 | + for (attrptr = attr->values[i].string.text; | |
1664 | + *attrptr && valptr < (value + sizeof(value) - 10); | |
1665 | + attrptr ++) | |
1666 | + { | |
1667 | + if (*attrptr == '\\' || *attrptr == '\"') | |
1668 | + *valptr++ = '\\'; | |
1669 | + | |
1670 | + *valptr++ = *attrptr; | |
1671 | + } | |
1672 | + *valptr++ = '\"'; | |
1673 | + break; | |
1674 | + | |
1675 | + default : | |
1676 | + /* | |
1677 | + * Unsupported value type... | |
1678 | + */ | |
1679 | + | |
1680 | + return; | |
1681 | + } | |
1682 | + } | |
1683 | + | |
1684 | + *valptr = '\0'; | |
1685 | + | |
1686 | + /* | |
1687 | + * Tell the scheduler about the new values... | |
1688 | + */ | |
1689 | + | |
1690 | + fprintf(stderr, "ATTR: %s=%s\n", attr->name, value); | |
1691 | +} | |
1692 | + | |
1693 | + | |
1694 | +/* | |
1695 | + * 'report_printer_state()' - Report the printer state. | |
1696 | + */ | |
1697 | + | |
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 */ | |
1701 | +{ | |
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 */ | |
1712 | + | |
1713 | + | |
1714 | + if ((psm = ippFindAttribute(ipp, "printer-state-message", | |
1715 | + IPP_TAG_TEXT)) != NULL) | |
1716 | + fprintf(stderr, "INFO: %s\n", psm->values[0].string.text); | |
1717 | + | |
1718 | + if ((reasons = ippFindAttribute(ipp, "printer-state-reasons", | |
1719 | + IPP_TAG_KEYWORD)) == NULL) | |
1720 | + return (0); | |
1721 | + | |
1722 | + saw_caprw = 0; | |
1723 | + state[0] = '\0'; | |
1724 | + prefix = "STATE: "; | |
1725 | + | |
1726 | + for (i = 0, count = 0; i < reasons->num_values; i ++) | |
1727 | + { | |
1728 | + reason = reasons->values[i].string.text; | |
1729 | + | |
1730 | + if (!strcmp(reason, "com.apple.print.recoverable-warning")) | |
1731 | + saw_caprw = 1; | |
1732 | + else if (strcmp(reason, "paused")) | |
1733 | + { | |
1734 | + strlcat(state, prefix, sizeof(state)); | |
1735 | + strlcat(state, reason, sizeof(state)); | |
1736 | + | |
1737 | + prefix = ","; | |
1738 | + } | |
1739 | + } | |
1740 | + | |
1741 | + if (state[0]) | |
1742 | + fprintf(stderr, "%s\n", state); | |
1743 | + | |
1744 | + /* | |
1745 | + * Relay com.apple.print.recoverable-message... | |
1746 | + */ | |
1747 | + | |
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); | |
1753 | + | |
1754 | + /* | |
1755 | + * Relay the current marker-* attribute values... | |
1756 | + */ | |
1757 | + | |
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); | |
1775 | + | |
1776 | + return (count); | |
1777 | +} | |
1778 | + | |
1779 | + | |
1780 | +#ifdef __APPLE__ | |
1781 | +/* | |
1782 | + * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing | |
1783 | + * remotely. | |
1784 | + * | |
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... | |
1790 | + */ | |
1791 | + | |
1792 | +static int /* O - Exit status of filter */ | |
1793 | +run_pictwps_filter(char **argv, /* I - Command-line arguments */ | |
1794 | + const char *filename)/* I - Filename */ | |
1795 | +{ | |
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 */ | |
1803 | + | |
1804 | + | |
1805 | + /* | |
1806 | + * First get the PPD file for the printer... | |
1807 | + */ | |
1808 | + | |
1809 | + printer = getenv("PRINTER"); | |
1810 | + if (!printer) | |
1811 | + { | |
1812 | + _cupsLangPuts(stderr, | |
1813 | + _("ERROR: PRINTER environment variable not defined!\n")); | |
1814 | + return (-1); | |
1815 | + } | |
1816 | + | |
1817 | + if ((ppdfile = cupsGetPPD(printer)) == NULL) | |
1818 | + { | |
1819 | + _cupsLangPrintf(stderr, | |
1820 | + _("ERROR: Unable to get PPD file for printer \"%s\" - " | |
1821 | + "%s.\n"), printer, cupsLastErrorString()); | |
1822 | + } | |
1823 | + else | |
1824 | + { | |
1825 | + snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile); | |
1826 | + putenv(ppdenv); | |
1827 | + } | |
1828 | + | |
1829 | + /* | |
1830 | + * Then create a temporary file for printing... | |
1831 | + */ | |
1832 | + | |
1833 | + if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0) | |
1834 | + { | |
1835 | + _cupsLangPrintError("ERROR", _("Unable to create temporary file")); | |
1836 | + if (ppdfile) | |
1837 | + unlink(ppdfile); | |
1838 | + return (-1); | |
1839 | + } | |
1840 | + | |
1841 | + /* | |
1842 | + * Get the owner of the spool file - it is owned by the user we want to run | |
1843 | + * as... | |
1844 | + */ | |
1845 | + | |
1846 | + if (argv[6]) | |
1847 | + stat(argv[6], &fileinfo); | |
1848 | + else | |
1849 | + { | |
1850 | + /* | |
1851 | + * Use the OSX defaults, as an up-stream filter created the PICT | |
1852 | + * file... | |
1853 | + */ | |
1854 | + | |
1855 | + fileinfo.st_uid = 1; | |
1856 | + fileinfo.st_gid = 80; | |
1857 | + } | |
1858 | + | |
1859 | + if (ppdfile) | |
1860 | + chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid); | |
1861 | + | |
1862 | + fchown(fd, fileinfo.st_uid, fileinfo.st_gid); | |
1863 | + | |
1864 | + /* | |
1865 | + * Finally, run the filter to convert the file... | |
1866 | + */ | |
1867 | + | |
1868 | + if ((pid = fork()) == 0) | |
1869 | + { | |
1870 | + /* | |
1871 | + * Child process for pictwpstops... Redirect output of pictwpstops to a | |
1872 | + * file... | |
1873 | + */ | |
1874 | + | |
1875 | + dup2(fd, 1); | |
1876 | + close(fd); | |
1877 | + | |
1878 | + if (!getuid()) | |
1879 | + { | |
1880 | + /* | |
1881 | + * Change to an unpriviledged user... | |
1882 | + */ | |
1883 | + | |
1884 | + if (setgid(fileinfo.st_gid)) | |
1885 | + return (errno); | |
1886 | + | |
1887 | + if (setuid(fileinfo.st_uid)) | |
1888 | + return (errno); | |
1889 | + } | |
1890 | + | |
1891 | + execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5], | |
1892 | + filename, NULL); | |
1893 | + _cupsLangPrintf(stderr, _("ERROR: Unable to exec pictwpstops: %s\n"), | |
1894 | + strerror(errno)); | |
1895 | + return (errno); | |
1896 | + } | |
1897 | + | |
1898 | + close(fd); | |
1899 | + | |
1900 | + if (pid < 0) | |
1901 | + { | |
1902 | + /* | |
1903 | + * Error! | |
1904 | + */ | |
1905 | + | |
1906 | + _cupsLangPrintf(stderr, _("ERROR: Unable to fork pictwpstops: %s\n"), | |
1907 | + strerror(errno)); | |
1908 | + if (ppdfile) | |
1909 | + unlink(ppdfile); | |
1910 | + return (-1); | |
1911 | + } | |
1912 | + | |
1913 | + /* | |
1914 | + * Now wait for the filter to complete... | |
1915 | + */ | |
1916 | + | |
1917 | + if (wait(&status) < 0) | |
1918 | + { | |
1919 | + _cupsLangPrintf(stderr, _("ERROR: Unable to wait for pictwpstops: %s\n"), | |
1920 | + strerror(errno)); | |
1921 | + close(fd); | |
1922 | + if (ppdfile) | |
1923 | + unlink(ppdfile); | |
1924 | + return (-1); | |
1925 | + } | |
1926 | + | |
1927 | + if (ppdfile) | |
1928 | + unlink(ppdfile); | |
1929 | + | |
1930 | + close(fd); | |
1931 | + | |
1932 | + if (status) | |
1933 | + { | |
1934 | + if (status >= 256) | |
1935 | + _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited with status %d!\n"), | |
1936 | + status / 256); | |
1937 | + else | |
1938 | + _cupsLangPrintf(stderr, _("ERROR: pictwpstops exited on signal %d!\n"), | |
1939 | + status); | |
1940 | + | |
1941 | + return (status); | |
1942 | + } | |
1943 | + | |
1944 | + /* | |
1945 | + * Return with no errors.. | |
1946 | + */ | |
1947 | + | |
1948 | + return (0); | |
1949 | +} | |
1950 | +#endif /* __APPLE__ */ | |
1951 | + | |
1952 | + | |
1953 | +/* | |
1954 | + * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend. | |
1955 | + */ | |
1956 | + | |
1957 | +static void | |
1958 | +sigterm_handler(int sig) /* I - Signal */ | |
1959 | +{ | |
1960 | + (void)sig; /* remove compiler warnings... */ | |
1961 | + | |
1962 | + if (!job_cancelled) | |
1963 | + { | |
1964 | + /* | |
1965 | + * Flag that the job should be cancelled... | |
1966 | + */ | |
1967 | + | |
1968 | + job_cancelled = 1; | |
1969 | + return; | |
1970 | + } | |
1971 | + | |
1972 | + /* | |
1973 | + * The scheduler already tried to cancel us once, now just terminate | |
1974 | + * after removing our temp files! | |
1975 | + */ | |
1976 | + | |
1977 | + if (tmpfilename[0]) | |
1978 | + unlink(tmpfilename); | |
1979 | + | |
1980 | +#ifdef __APPLE__ | |
1981 | + if (pstmpname[0]) | |
1982 | + unlink(pstmpname); | |
1983 | +#endif /* __APPLE__ */ | |
1984 | + | |
1985 | + exit(1); | |
1986 | +} | |
1987 | + | |
1988 | + | |
1989 | +/* | |
1990 | + * End of "$Id: ipp.c 8950 2010-01-14 22:40:19Z mike $". | |
1991 | + */ |