]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/ipp.c
Load cups into easysw/current.
[thirdparty/cups.git] / backend / ipp.c
1 /*
2 * "$Id: ipp.c 4926 2006-01-13 03:12:13Z mike $"
3 *
4 * IPP backend for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
7 *
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
13 * at:
14 *
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
19 *
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
23 *
24 * This file is subject to the Apple OS-Developed Software exception.
25 *
26 * Contents:
27 *
28 * main() - Send a file to the printer or server.
29 * check_printer_state() - Check the printer state...
30 * password_cb() - Disable the password prompt for
31 * cupsDoFileRequest().
32 * report_printer_state() - Report the printer state.
33 * run_pictwps_filter() - Convert PICT files to PostScript when printing
34 * remotely.
35 * sigterm_handler() - Handle 'terminate' signals that stop the backend.
36 */
37
38 /*
39 * Include necessary headers.
40 */
41
42 #include <cups/http-private.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <errno.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <cups/backend.h>
49 #include <cups/cups.h>
50 #include <cups/language.h>
51 #include <cups/string.h>
52 #include <signal.h>
53 #include <sys/wait.h>
54
55
56 /*
57 * Globals...
58 */
59
60 static char *password = NULL; /* Password for device URI */
61 #ifdef __APPLE__
62 static char pstmpname[1024] = ""; /* Temporary PostScript file name */
63 #endif /* __APPLE__ */
64 static char tmpfilename[1024] = ""; /* Temporary spool file name */
65
66
67 /*
68 * Local functions...
69 */
70
71 void check_printer_state(http_t *http, const char *uri,
72 const char *resource, const char *user,
73 int version);
74 const char *password_cb(const char *);
75 int report_printer_state(ipp_t *ipp);
76
77 #ifdef __APPLE__
78 int run_pictwps_filter(char **argv, const char *filename);
79 #endif /* __APPLE__ */
80 static void sigterm_handler(int sig);
81
82
83 /*
84 * 'main()' - Send a file to the printer or server.
85 *
86 * Usage:
87 *
88 * printer-uri job-id user title copies options [file]
89 */
90
91 int /* O - Exit status */
92 main(int argc, /* I - Number of command-line arguments (6 or 7) */
93 char *argv[]) /* I - Command-line arguments */
94 {
95 int i; /* Looping var */
96 int num_options; /* Number of printer options */
97 cups_option_t *options; /* Printer options */
98 char method[255], /* Method in URI */
99 hostname[1024], /* Hostname */
100 username[255], /* Username info */
101 resource[1024], /* Resource info (printer name) */
102 *optptr, /* Pointer to URI options */
103 name[255], /* Name of option */
104 value[255], /* Value of option */
105 *ptr; /* Pointer into name or value */
106 char *filename; /* File to print */
107 int port; /* Port number (not used) */
108 char uri[HTTP_MAX_URI]; /* Updated URI without user/pass */
109 ipp_status_t ipp_status; /* Status of IPP request */
110 http_t *http; /* HTTP connection */
111 ipp_t *request, /* IPP request */
112 *response, /* IPP response */
113 *supported; /* get-printer-attributes response */
114 int waitjob, /* Wait for job complete? */
115 waitprinter; /* Wait for printer ready? */
116 ipp_attribute_t *job_id_attr; /* job-id attribute */
117 int job_id; /* job-id value */
118 ipp_attribute_t *job_sheets; /* job-media-sheets-completed attribute */
119 ipp_attribute_t *job_state; /* job-state attribute */
120 ipp_attribute_t *copies_sup; /* copies-supported attribute */
121 ipp_attribute_t *format_sup; /* document-format-supported attribute */
122 ipp_attribute_t *printer_state; /* printer-state attribute */
123 ipp_attribute_t *printer_accepting; /* printer-is-accepting-jobs attribute */
124 int copies; /* Number of copies remaining */
125 const char *content_type; /* CONTENT_TYPE environment variable */
126 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
127 struct sigaction action; /* Actions for POSIX signals */
128 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
129 int version; /* IPP version */
130 int reasons; /* Number of printer-state-reasons shown */
131 static const char * const pattrs[] =
132 { /* Printer attributes we want */
133 "copies-supported",
134 "document-format-supported",
135 "printer-is-accepting-jobs",
136 "printer-state",
137 "printer-state-reasons",
138 };
139 static const char * const jattrs[] =
140 { /* Job attributes we want */
141 "job-media-sheets-completed",
142 "job-state"
143 };
144
145
146 /*
147 * Make sure status messages are not buffered...
148 */
149
150 setbuf(stderr, NULL);
151
152 /*
153 * Ignore SIGPIPE and catch SIGTERM signals...
154 */
155
156 #ifdef HAVE_SIGSET
157 sigset(SIGPIPE, SIG_IGN);
158 sigset(SIGTERM, sigterm_handler);
159 #elif defined(HAVE_SIGACTION)
160 memset(&action, 0, sizeof(action));
161 action.sa_handler = SIG_IGN;
162 sigaction(SIGPIPE, &action, NULL);
163
164 sigemptyset(&action.sa_mask);
165 sigaddset(&action.sa_mask, SIGTERM);
166 action.sa_handler = sigterm_handler;
167 sigaction(SIGTERM, &action, NULL);
168 #else
169 signal(SIGPIPE, SIG_IGN);
170 signal(SIGTERM, sigterm_handler);
171 #endif /* HAVE_SIGSET */
172
173 /*
174 * Check command-line...
175 */
176
177 if (argc == 1)
178 {
179 char *s;
180
181 if ((s = strrchr(argv[0], '/')) != NULL)
182 s ++;
183 else
184 s = argv[0];
185
186 printf("network %s \"Unknown\" \"Internet Printing Protocol (%s)\"\n", s, s);
187 return (CUPS_BACKEND_OK);
188 }
189 else if (argc < 6 || argc > 7)
190 {
191 fprintf(stderr, "Usage: %s job-id user title copies options [file]\n",
192 argv[0]);
193 return (CUPS_BACKEND_STOP);
194 }
195
196 /*
197 * Get the content type...
198 */
199
200 if (argc > 6)
201 content_type = getenv("CONTENT_TYPE");
202 else
203 content_type = "application/vnd.cups-raw";
204
205 if (content_type == NULL)
206 content_type = "application/octet-stream";
207
208 /*
209 * Extract the hostname and printer name from the URI...
210 */
211
212 if (getenv("DEVICE_URI") != NULL)
213 /* authentication information is only available in the env var */
214 httpSeparateURI(getenv("DEVICE_URI"), method, sizeof(method),
215 username, sizeof(username),
216 hostname, sizeof(hostname), &port,
217 resource, sizeof(resource));
218 else if (strchr(argv[0], ':') != NULL)
219 httpSeparateURI(argv[0], method, sizeof(method), username, sizeof(username),
220 hostname, sizeof(hostname), &port,
221 resource, sizeof(resource));
222 else
223 {
224 fputs("ERROR: Missing device URI on command-line and no DEVICE_URI environment variable!\n",
225 stderr);
226 return (CUPS_BACKEND_STOP);
227 }
228
229 if (!strcmp(method, "https"))
230 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
231
232 /*
233 * If we have 7 arguments, print the file named on the command-line.
234 * Otherwise, copy stdin to a temporary file and print the temporary
235 * file.
236 */
237
238 if (argc == 6)
239 {
240 /*
241 * Copy stdin to a temporary file...
242 */
243
244 int fd; /* Temporary file */
245 char buffer[8192]; /* Buffer for copying */
246 int bytes; /* Number of bytes read */
247
248
249 if ((fd = cupsTempFd(tmpfilename, sizeof(tmpfilename))) < 0)
250 {
251 perror("ERROR: unable to create temporary file");
252 return (CUPS_BACKEND_FAILED);
253 }
254
255 while ((bytes = fread(buffer, 1, sizeof(buffer), stdin)) > 0)
256 if (write(fd, buffer, bytes) < bytes)
257 {
258 perror("ERROR: unable to write to temporary file");
259 close(fd);
260 unlink(tmpfilename);
261 return (CUPS_BACKEND_FAILED);
262 }
263
264 close(fd);
265 filename = tmpfilename;
266 }
267 else
268 filename = argv[6];
269
270 /*
271 * See if there are any options...
272 */
273
274 version = 1;
275 waitjob = 1;
276 waitprinter = 1;
277
278 if ((optptr = strchr(resource, '?')) != NULL)
279 {
280 /*
281 * Yup, terminate the device name string and move to the first
282 * character of the optptr...
283 */
284
285 *optptr++ = '\0';
286
287 /*
288 * Then parse the optptr...
289 */
290
291 while (*optptr)
292 {
293 /*
294 * Get the name...
295 */
296
297 for (ptr = name; *optptr && *optptr != '=';)
298 if (ptr < (name + sizeof(name) - 1))
299 *ptr++ = *optptr++;
300 *ptr = '\0';
301
302 if (*optptr == '=')
303 {
304 /*
305 * Get the value...
306 */
307
308 optptr ++;
309
310 for (ptr = value; *optptr && *optptr != '+' && *optptr != '&';)
311 if (ptr < (value + sizeof(value) - 1))
312 *ptr++ = *optptr++;
313 *ptr = '\0';
314
315 if (*optptr == '+')
316 optptr ++;
317 }
318 else
319 value[0] = '\0';
320
321 /*
322 * Process the option...
323 */
324
325 if (!strcasecmp(name, "waitjob"))
326 {
327 /*
328 * Wait for job completion?
329 */
330
331 waitjob = !strcasecmp(value, "on") ||
332 !strcasecmp(value, "yes") ||
333 !strcasecmp(value, "true");
334 }
335 else if (!strcasecmp(name, "waitprinter"))
336 {
337 /*
338 * Wait for printer idle?
339 */
340
341 waitprinter = !strcasecmp(value, "on") ||
342 !strcasecmp(value, "yes") ||
343 !strcasecmp(value, "true");
344 }
345 else if (!strcasecmp(name, "encryption"))
346 {
347 /*
348 * Enable/disable encryption?
349 */
350
351 if (!strcasecmp(value, "always"))
352 cupsSetEncryption(HTTP_ENCRYPT_ALWAYS);
353 else if (!strcasecmp(value, "required"))
354 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
355 else if (!strcasecmp(value, "never"))
356 cupsSetEncryption(HTTP_ENCRYPT_NEVER);
357 else if (!strcasecmp(value, "ifrequested"))
358 cupsSetEncryption(HTTP_ENCRYPT_IF_REQUESTED);
359 else
360 {
361 fprintf(stderr, "ERROR: Unknown encryption option value \"%s\"!\n",
362 value);
363 }
364 }
365 else if (!strcasecmp(name, "version"))
366 {
367 if (!strcmp(value, "1.0"))
368 version = 0;
369 else if (!strcmp(value, "1.1"))
370 version = 1;
371 else
372 {
373 fprintf(stderr, "ERROR: Unknown version option value \"%s\"!\n",
374 value);
375 }
376 }
377 else
378 {
379 /*
380 * Unknown option...
381 */
382
383 fprintf(stderr, "ERROR: Unknown option \"%s\" with value \"%s\"!\n",
384 name, value);
385 }
386 }
387 }
388
389 /*
390 * Set the authentication info, if any...
391 */
392
393 cupsSetPasswordCB(password_cb);
394
395 if (username[0])
396 {
397 /*
398 * Use authenticaion information in the device URI...
399 */
400
401 if ((password = strchr(username, ':')) != NULL)
402 *password++ = '\0';
403
404 cupsSetUser(username);
405 }
406 else if (!getuid())
407 {
408 /*
409 * Try loading authentication information from the a##### file.
410 */
411
412 const char *request_root; /* CUPS_REQUESTROOT env var */
413 char afilename[1024], /* a##### filename */
414 aline[1024]; /* Line from file */
415 FILE *fp; /* File pointer */
416
417
418 if ((request_root = getenv("CUPS_REQUESTROOT")) != NULL)
419 {
420 /*
421 * Try opening authentication cache file...
422 */
423
424 snprintf(afilename, sizeof(afilename), "%s/a%05d", request_root,
425 atoi(argv[1]));
426 if ((fp = fopen(afilename, "r")) != NULL)
427 {
428 /*
429 * Read username...
430 */
431
432 if (fgets(aline, sizeof(aline), fp))
433 {
434 /*
435 * Decode username...
436 */
437
438 i = sizeof(username);
439 httpDecode64_2(username, &i, aline);
440
441 /*
442 * Read password...
443 */
444
445 if (fgets(aline, sizeof(aline), fp))
446 {
447 /*
448 * Decode password...
449 */
450
451 i = sizeof(password);
452 httpDecode64_2(password, &i, aline);
453 }
454 }
455
456 /*
457 * Close the file...
458 */
459
460 fclose(fp);
461 }
462 }
463 }
464
465 /*
466 * Try connecting to the remote server...
467 */
468
469 do
470 {
471 fprintf(stderr, "INFO: Connecting to %s on port %d...\n", hostname, port);
472
473 if ((http = httpConnectEncrypt(hostname, port, cupsEncryption())) == NULL)
474 {
475 if (getenv("CLASS") != NULL)
476 {
477 /*
478 * If the CLASS environment variable is set, the job was submitted
479 * to a class and not to a specific queue. In this case, we want
480 * to abort immediately so that the job can be requeued on the next
481 * available printer in the class.
482 */
483
484 fprintf(stderr, "INFO: Unable to connect to %s, queuing on next printer in class...\n",
485 hostname);
486
487 if (argc == 6 || strcmp(filename, argv[6]))
488 unlink(filename);
489
490 /*
491 * Sleep 5 seconds to keep the job from requeuing too rapidly...
492 */
493
494 sleep(5);
495
496 return (CUPS_BACKEND_FAILED);
497 }
498
499 if (errno == ECONNREFUSED || errno == EHOSTDOWN ||
500 errno == EHOSTUNREACH)
501 {
502 fprintf(stderr, "INFO: Network host \'%s\' is busy; will retry in 30 seconds...\n",
503 hostname);
504 sleep(30);
505 }
506 else if (h_errno)
507 {
508 fprintf(stderr, "INFO: Unable to lookup host \'%s\' - %s\n",
509 hostname, hstrerror(h_errno));
510 sleep(30);
511 }
512 else
513 {
514 perror("ERROR: Unable to connect to IPP host");
515 sleep(30);
516 }
517 }
518 }
519 while (http == NULL);
520
521 fprintf(stderr, "INFO: Connected to %s...\n", hostname);
522
523 /*
524 * Build a URI for the printer and fill the standard IPP attributes for
525 * an IPP_PRINT_FILE request. We can't use the URI in argv[0] because it
526 * might contain username:password information...
527 */
528
529 snprintf(uri, sizeof(uri), "%s://%s:%d%s", method, hostname, port, resource);
530
531 /*
532 * First validate the destination and see if the device supports multiple
533 * copies. We have to do this because some IPP servers (e.g. HP JetDirect)
534 * don't support the copies attribute...
535 */
536
537 copies_sup = NULL;
538 format_sup = NULL;
539 supported = NULL;
540
541 do
542 {
543 /*
544 * Build the IPP request...
545 */
546
547 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
548 request->request.op.version[1] = version;
549
550 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
551 NULL, uri);
552
553 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
554 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
555 NULL, pattrs);
556
557 /*
558 * Do the request...
559 */
560
561 fputs("DEBUG: Getting supported attributes...\n", stderr);
562
563 if ((supported = cupsDoRequest(http, request, resource)) == NULL)
564 ipp_status = cupsLastError();
565 else
566 ipp_status = supported->request.status.status_code;
567
568 if (ipp_status > IPP_OK_CONFLICT)
569 {
570 if (ipp_status == IPP_PRINTER_BUSY ||
571 ipp_status == IPP_SERVICE_UNAVAILABLE)
572 {
573 fputs("INFO: Printer busy; will retry in 10 seconds...\n", stderr);
574 report_printer_state(supported);
575 sleep(10);
576 }
577 else if ((ipp_status == IPP_BAD_REQUEST ||
578 ipp_status == IPP_VERSION_NOT_SUPPORTED) && version == 1)
579 {
580 /*
581 * Switch to IPP/1.0...
582 */
583
584 fputs("INFO: Printer does not support IPP/1.1, trying IPP/1.0...\n", stderr);
585 version = 0;
586 httpReconnect(http);
587 }
588 else if (ipp_status == IPP_NOT_FOUND)
589 {
590 fputs("ERROR: Destination printer does not exist!\n", stderr);
591
592 if (supported)
593 ippDelete(supported);
594
595 return (CUPS_BACKEND_STOP);
596 }
597 else
598 {
599 fprintf(stderr, "ERROR: Unable to get printer status (%s)!\n",
600 ippErrorString(ipp_status));
601 sleep(10);
602 }
603
604 if (supported)
605 ippDelete(supported);
606
607 continue;
608 }
609 else if ((copies_sup = ippFindAttribute(supported, "copies-supported",
610 IPP_TAG_RANGE)) != NULL)
611 {
612 /*
613 * Has the "copies-supported" attribute - does it have an upper
614 * bound > 1?
615 */
616
617 if (copies_sup->values[0].range.upper <= 1)
618 copies_sup = NULL; /* No */
619 }
620
621 format_sup = ippFindAttribute(supported, "document-format-supported",
622 IPP_TAG_MIMETYPE);
623
624 if (format_sup)
625 {
626 fprintf(stderr, "DEBUG: document-format-supported (%d values)\n",
627 format_sup->num_values);
628 for (i = 0; i < format_sup->num_values; i ++)
629 fprintf(stderr, "DEBUG: [%d] = \"%s\"\n", i,
630 format_sup->values[i].string.text);
631 }
632
633 report_printer_state(supported);
634 }
635 while (ipp_status > IPP_OK_CONFLICT);
636
637 /*
638 * See if the printer is accepting jobs and is not stopped; if either
639 * condition is true and we are printing to a class, requeue the job...
640 */
641
642 if (getenv("CLASS") != NULL)
643 {
644 printer_state = ippFindAttribute(supported, "printer-state",
645 IPP_TAG_ENUM);
646 printer_accepting = ippFindAttribute(supported, "printer-is-accepting-jobs",
647 IPP_TAG_BOOLEAN);
648
649 if (printer_state == NULL ||
650 (printer_state->values[0].integer > IPP_PRINTER_PROCESSING && waitprinter) ||
651 printer_accepting == NULL ||
652 !printer_accepting->values[0].boolean)
653 {
654 /*
655 * If the CLASS environment variable is set, the job was submitted
656 * to a class and not to a specific queue. In this case, we want
657 * to abort immediately so that the job can be requeued on the next
658 * available printer in the class.
659 */
660
661 fprintf(stderr, "INFO: Unable to queue job on %s, queuing on next printer in class...\n",
662 hostname);
663
664 ippDelete(supported);
665 httpClose(http);
666
667 if (argc == 6 || strcmp(filename, argv[6]))
668 unlink(filename);
669
670 /*
671 * Sleep 5 seconds to keep the job from requeuing too rapidly...
672 */
673
674 sleep(5);
675
676 return (CUPS_BACKEND_FAILED);
677 }
678 }
679
680 /*
681 * See if the printer supports multiple copies...
682 */
683
684 if (copies_sup || argc < 7)
685 copies = 1;
686 else
687 copies = atoi(argv[4]);
688
689 /*
690 * Then issue the print-job request...
691 */
692
693 reasons = 0;
694
695 while (copies > 0)
696 {
697 /*
698 * Build the IPP request...
699 */
700
701 request = ippNewRequest(IPP_PRINT_JOB);
702 request->request.op.version[1] = version;
703
704 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
705 NULL, uri);
706
707 fprintf(stderr, "DEBUG: printer-uri = \"%s\"\n", uri);
708
709 if (argv[2][0])
710 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
711 "requesting-user-name", NULL, argv[2]);
712
713 fprintf(stderr, "DEBUG: requesting-user-name = \"%s\"\n", argv[2]);
714
715 if (argv[3][0])
716 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL,
717 argv[3]);
718
719 fprintf(stderr, "DEBUG: job-name = \"%s\"\n", argv[3]);
720
721 /*
722 * Handle options on the command-line...
723 */
724
725 options = NULL;
726 num_options = cupsParseOptions(argv[5], 0, &options);
727
728 #ifdef __APPLE__
729 if (content_type != NULL && strcasecmp(content_type, "application/pictwps") == 0)
730 {
731 if (format_sup != NULL)
732 {
733 for (i = 0; i < format_sup->num_values; i ++)
734 if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
735 break;
736 }
737
738 if (format_sup == NULL || i >= format_sup->num_values)
739 {
740 /*
741 * Remote doesn't support "application/pictwps" (i.e. it's not MacOS X)
742 * so convert the document to PostScript...
743 */
744
745 if (run_pictwps_filter(argv, filename))
746 return (CUPS_BACKEND_FAILED);
747
748 filename = pstmpname;
749
750 /*
751 * Change the MIME type to application/postscript...
752 */
753
754 content_type = "application/postscript";
755 }
756 }
757 #endif /* __APPLE__ */
758
759 if (content_type != NULL && format_sup != NULL)
760 {
761 for (i = 0; i < format_sup->num_values; i ++)
762 if (strcasecmp(content_type, format_sup->values[i].string.text) == 0)
763 break;
764
765 if (i < format_sup->num_values)
766 num_options = cupsAddOption("document-format", content_type,
767 num_options, &options);
768 }
769
770 if (copies_sup)
771 {
772 /*
773 * Only send options if the destination printer supports the copies
774 * attribute. This is a hack for the HP JetDirect implementation of
775 * IPP, which does not accept extension attributes and incorrectly
776 * reports a client-error-bad-request error instead of the
777 * successful-ok-unsupported-attributes status. In short, at least
778 * some HP implementations of IPP are non-compliant.
779 */
780
781 cupsEncodeOptions(request, num_options, options);
782 ippAddInteger(request, IPP_TAG_JOB, IPP_TAG_INTEGER, "copies",
783 atoi(argv[4]));
784 }
785
786 cupsFreeOptions(num_options, options);
787
788 /*
789 * If copies aren't supported, then we are likely dealing with an HP
790 * JetDirect. The HP IPP implementation seems to close the connection
791 * after every request (that is, it does *not* implement HTTP Keep-
792 * Alive, which is REQUIRED by HTTP/1.1...
793 */
794
795 if (!copies_sup)
796 httpReconnect(http);
797
798 /*
799 * Do the request...
800 */
801
802 if ((response = cupsDoFileRequest(http, request, resource, filename)) == NULL)
803 ipp_status = cupsLastError();
804 else
805 ipp_status = response->request.status.status_code;
806
807 if (ipp_status > IPP_OK_CONFLICT)
808 {
809 job_id = 0;
810
811 if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
812 ipp_status == IPP_PRINTER_BUSY)
813 {
814 fputs("INFO: Printer is busy; retrying print job...\n", stderr);
815 sleep(10);
816 }
817 else
818 fprintf(stderr, "ERROR: Print file was not accepted (%s)!\n",
819 ippErrorString(ipp_status));
820 }
821 else if ((job_id_attr = ippFindAttribute(response, "job-id",
822 IPP_TAG_INTEGER)) == NULL)
823 {
824 fputs("NOTICE: Print file accepted - job ID unknown.\n", stderr);
825 job_id = 0;
826 }
827 else
828 {
829 job_id = job_id_attr->values[0].integer;
830 fprintf(stderr, "NOTICE: Print file accepted - job ID %d.\n", job_id);
831 }
832
833 if (response)
834 ippDelete(response);
835
836 if (ipp_status <= IPP_OK_CONFLICT && argc > 6)
837 {
838 fprintf(stderr, "PAGE: 1 %d\n", copies_sup ? atoi(argv[4]) : 1);
839 copies --;
840 }
841 else if (ipp_status == IPP_SERVICE_UNAVAILABLE ||
842 ipp_status == IPP_PRINTER_BUSY)
843 break;
844 else
845 copies --;
846
847 /*
848 * Wait for the job to complete...
849 */
850
851 if (!job_id || !waitjob)
852 continue;
853
854 fputs("INFO: Waiting for job to complete...\n", stderr);
855
856 for (;;)
857 {
858 /*
859 * Build an IPP_GET_JOB_ATTRIBUTES request...
860 */
861
862 request = ippNewRequest(IPP_GET_JOB_ATTRIBUTES);
863 request->request.op.version[1] = version;
864
865 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
866 NULL, uri);
867
868 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id",
869 job_id);
870
871 if (argv[2][0])
872 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
873 "requesting-user-name", NULL, argv[2]);
874
875 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
876 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
877 NULL, jattrs);
878
879 /*
880 * Do the request...
881 */
882
883 if (!copies_sup)
884 httpReconnect(http);
885
886 if ((response = cupsDoRequest(http, request, resource)) == NULL)
887 ipp_status = cupsLastError();
888 else
889 ipp_status = response->request.status.status_code;
890
891 if (ipp_status == IPP_NOT_FOUND)
892 {
893 /*
894 * Job has gone away and/or the server has no job history...
895 */
896
897 ippDelete(response);
898
899 ipp_status = IPP_OK;
900 break;
901 }
902
903 if (ipp_status > IPP_OK_CONFLICT)
904 {
905 if (ipp_status != IPP_SERVICE_UNAVAILABLE &&
906 ipp_status != IPP_PRINTER_BUSY)
907 {
908 if (response)
909 ippDelete(response);
910
911 fprintf(stderr, "ERROR: Unable to get job %d attributes (%s)!\n",
912 job_id, ippErrorString(ipp_status));
913 break;
914 }
915 }
916
917 if (response != NULL)
918 {
919 if ((job_state = ippFindAttribute(response, "job-state",
920 IPP_TAG_ENUM)) != NULL)
921 {
922 /*
923 * Stop polling if the job is finished or pending-held...
924 */
925
926 if (job_state->values[0].integer > IPP_JOB_PROCESSING ||
927 job_state->values[0].integer == IPP_JOB_HELD)
928 {
929 if ((job_sheets = ippFindAttribute(response,
930 "job-media-sheets-completed",
931 IPP_TAG_INTEGER)) != NULL)
932 fprintf(stderr, "PAGE: total %d\n", job_sheets->values[0].integer);
933
934 ippDelete(response);
935 break;
936 }
937 }
938 }
939
940 if (response)
941 ippDelete(response);
942
943 /*
944 * Check the printer state and report it if necessary...
945 */
946
947 check_printer_state(http, uri, resource, argv[2], version);
948
949 /*
950 * Wait 10 seconds before polling again...
951 */
952
953 sleep(10);
954 }
955 }
956
957 /*
958 * Check the printer state and report it if necessary...
959 */
960
961 /* if (!copies_sup)
962 httpReconnect(http);*/
963
964 check_printer_state(http, uri, resource, argv[2], version);
965
966 /*
967 * Free memory...
968 */
969
970 httpClose(http);
971
972 if (supported)
973 ippDelete(supported);
974
975 /*
976 * Remove the temporary file(s) if necessary...
977 */
978
979 if (tmpfilename[0])
980 unlink(tmpfilename);
981
982 #ifdef __APPLE__
983 if (pstmpname[0])
984 unlink(pstmpname);
985 #endif /* __APPLE__ */
986
987 /*
988 * Return the queue status...
989 */
990
991 return (ipp_status > IPP_OK_CONFLICT ? CUPS_BACKEND_FAILED : CUPS_BACKEND_OK);
992 }
993
994
995 /*
996 * 'check_printer_state()' - Check the printer state...
997 */
998
999 void
1000 check_printer_state(
1001 http_t *http, /* I - HTTP connection */
1002 const char *uri, /* I - Printer URI */
1003 const char *resource, /* I - Resource path */
1004 const char *user, /* I - Username, if any */
1005 int version) /* I - IPP version */
1006 {
1007 ipp_t *request, /* IPP request */
1008 *response; /* IPP response */
1009
1010
1011 /*
1012 * Check on the printer state...
1013 */
1014
1015 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1016 request->request.op.version[1] = version;
1017
1018 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1019 NULL, uri);
1020
1021 if (user && user[0])
1022 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1023 "requesting-user-name", NULL, user);
1024
1025 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1026 "requested-attributes", NULL, "printer-state-reasons");
1027
1028 /*
1029 * Do the request...
1030 */
1031
1032 if ((response = cupsDoRequest(http, request, resource)) != NULL)
1033 {
1034 report_printer_state(response);
1035 ippDelete(response);
1036 }
1037 }
1038
1039
1040 /*
1041 * 'password_cb()' - Disable the password prompt for cupsDoFileRequest().
1042 */
1043
1044 const char * /* O - Password */
1045 password_cb(const char *prompt) /* I - Prompt (not used) */
1046 {
1047 (void)prompt;
1048
1049 if (password)
1050 return (password);
1051 else
1052 {
1053 /*
1054 * If there is no password set in the device URI, return the
1055 * "authentication required" exit code...
1056 */
1057
1058 if (tmpfilename[0])
1059 unlink(tmpfilename);
1060
1061 #ifdef __APPLE__
1062 if (pstmpname[0])
1063 unlink(pstmpname);
1064 #endif /* __APPLE__ */
1065
1066 exit(CUPS_BACKEND_AUTH_REQUIRED);
1067 }
1068 }
1069
1070
1071 /*
1072 * 'report_printer_state()' - Report the printer state.
1073 */
1074
1075 int /* O - Number of reasons shown */
1076 report_printer_state(ipp_t *ipp) /* I - IPP response */
1077 {
1078 int i; /* Looping var */
1079 int count; /* Count of reasons shown... */
1080 ipp_attribute_t *reasons; /* printer-state-reasons */
1081 const char *reason; /* Current reason */
1082 const char *message; /* Message to show */
1083 char unknown[1024]; /* Unknown message string */
1084 const char *prefix; /* Prefix for STATE: line */
1085 char state[1024]; /* State string */
1086
1087
1088 if ((reasons = ippFindAttribute(ipp, "printer-state-reasons",
1089 IPP_TAG_KEYWORD)) == NULL)
1090 return (0);
1091
1092 state[0] = '\0';
1093 prefix = "STATE: ";
1094
1095 for (i = 0, count = 0; i < reasons->num_values; i ++)
1096 {
1097 reason = reasons->values[i].string.text;
1098
1099 strlcat(state, prefix, sizeof(state));
1100 strlcat(state, reason, sizeof(state));
1101
1102 prefix = ",";
1103 message = NULL;
1104
1105 if (!strncmp(reason, "media-needed", 12))
1106 message = "Media tray needs to be filled.";
1107 else if (!strncmp(reason, "media-jam", 9))
1108 message = "Media jam!";
1109 else if (!strncmp(reason, "moving-to-paused", 16) ||
1110 !strncmp(reason, "paused", 6) ||
1111 !strncmp(reason, "shutdown", 8))
1112 message = "Printer off-line.";
1113 else if (!strncmp(reason, "toner-low", 9))
1114 message = "Toner low.";
1115 else if (!strncmp(reason, "toner-empty", 11))
1116 message = "Out of toner!";
1117 else if (!strncmp(reason, "cover-open", 10))
1118 message = "Cover open.";
1119 else if (!strncmp(reason, "interlock-open", 14))
1120 message = "Interlock open.";
1121 else if (!strncmp(reason, "door-open", 9))
1122 message = "Door open.";
1123 else if (!strncmp(reason, "input-tray-missing", 18))
1124 message = "Media tray missing!";
1125 else if (!strncmp(reason, "media-low", 9))
1126 message = "Media tray almost empty.";
1127 else if (!strncmp(reason, "media-empty", 11))
1128 message = "Media tray empty!";
1129 else if (!strncmp(reason, "output-tray-missing", 19))
1130 message = "Output tray missing!";
1131 else if (!strncmp(reason, "output-area-almost-full", 23))
1132 message = "Output bin almost full.";
1133 else if (!strncmp(reason, "output-area-full", 16))
1134 message = "Output bin full!";
1135 else if (!strncmp(reason, "marker-supply-low", 17))
1136 message = "Ink/toner almost empty.";
1137 else if (!strncmp(reason, "marker-supply-empty", 19))
1138 message = "Ink/toner empty!";
1139 else if (!strncmp(reason, "marker-waste-almost-full", 24))
1140 message = "Ink/toner waste bin almost full.";
1141 else if (!strncmp(reason, "marker-waste-full", 17))
1142 message = "Ink/toner waste bin full!";
1143 else if (!strncmp(reason, "fuser-over-temp", 15))
1144 message = "Fuser temperature high!";
1145 else if (!strncmp(reason, "fuser-under-temp", 16))
1146 message = "Fuser temperature low!";
1147 else if (!strncmp(reason, "opc-near-eol", 12))
1148 message = "OPC almost at end-of-life.";
1149 else if (!strncmp(reason, "opc-life-over", 13))
1150 message = "OPC at end-of-life!";
1151 else if (!strncmp(reason, "developer-low", 13))
1152 message = "Developer almost empty.";
1153 else if (!strncmp(reason, "developer-empty", 15))
1154 message = "Developer empty!";
1155 else if (strstr(reason, "error") != NULL)
1156 {
1157 message = unknown;
1158
1159 snprintf(unknown, sizeof(unknown), "Unknown printer error (%s)!",
1160 reason);
1161 }
1162
1163 if (message)
1164 {
1165 count ++;
1166 if (strstr(reasons->values[i].string.text, "error"))
1167 fprintf(stderr, "ERROR: %s\n", message);
1168 else if (strstr(reasons->values[i].string.text, "warning"))
1169 fprintf(stderr, "WARNING: %s\n", message);
1170 else
1171 fprintf(stderr, "INFO: %s\n", message);
1172 }
1173 }
1174
1175 fprintf(stderr, "%s\n", state);
1176
1177 return (count);
1178 }
1179
1180
1181 #ifdef __APPLE__
1182 /*
1183 * 'run_pictwps_filter()' - Convert PICT files to PostScript when printing
1184 * remotely.
1185 *
1186 * This step is required because the PICT format is not documented and
1187 * subject to change, so developing a filter for other OS's is infeasible.
1188 * Also, fonts required by the PICT file need to be embedded on the
1189 * client side (which has the fonts), so we run the filter to get a
1190 * PostScript file for printing...
1191 */
1192
1193 int /* O - Exit status of filter */
1194 run_pictwps_filter(char **argv, /* I - Command-line arguments */
1195 const char *filename)/* I - Filename */
1196 {
1197 struct stat fileinfo; /* Print file information */
1198 const char *ppdfile; /* PPD file for destination printer */
1199 int pid; /* Child process ID */
1200 int fd; /* Temporary file descriptor */
1201 int status; /* Exit status of filter */
1202 const char *printer; /* PRINTER env var */
1203 static char ppdenv[1024]; /* PPD environment variable */
1204
1205
1206 /*
1207 * First get the PPD file for the printer...
1208 */
1209
1210 printer = getenv("PRINTER");
1211 if (!printer)
1212 {
1213 fputs("ERROR: PRINTER environment variable not defined!\n", stderr);
1214 return (-1);
1215 }
1216
1217 if ((ppdfile = cupsGetPPD(printer)) == NULL)
1218 {
1219 fprintf(stderr, "ERROR: Unable to get PPD file for printer \"%s\" - %s.\n",
1220 printer, ippErrorString(cupsLastError()));
1221 /*return (-1);*/
1222 }
1223 else
1224 {
1225 snprintf(ppdenv, sizeof(ppdenv), "PPD=%s", ppdfile);
1226 putenv(ppdenv);
1227 }
1228
1229 /*
1230 * Then create a temporary file for printing...
1231 */
1232
1233 if ((fd = cupsTempFd(pstmpname, sizeof(pstmpname))) < 0)
1234 {
1235 fprintf(stderr, "ERROR: Unable to create temporary file - %s.\n",
1236 strerror(errno));
1237 if (ppdfile)
1238 unlink(ppdfile);
1239 return (-1);
1240 }
1241
1242 /*
1243 * Get the owner of the spool file - it is owned by the user we want to run
1244 * as...
1245 */
1246
1247 if (argv[6])
1248 stat(argv[6], &fileinfo);
1249 else
1250 {
1251 /*
1252 * Use the OSX defaults, as an up-stream filter created the PICT
1253 * file...
1254 */
1255
1256 fileinfo.st_uid = 1;
1257 fileinfo.st_gid = 80;
1258 }
1259
1260 if (ppdfile)
1261 chown(ppdfile, fileinfo.st_uid, fileinfo.st_gid);
1262
1263 fchown(fd, fileinfo.st_uid, fileinfo.st_gid);
1264
1265 /*
1266 * Finally, run the filter to convert the file...
1267 */
1268
1269 if ((pid = fork()) == 0)
1270 {
1271 /*
1272 * Child process for pictwpstops... Redirect output of pictwpstops to a
1273 * file...
1274 */
1275
1276 close(1);
1277 dup(fd);
1278 close(fd);
1279
1280 if (!getuid())
1281 {
1282 /*
1283 * Change to an unpriviledged user...
1284 */
1285
1286 setgid(fileinfo.st_gid);
1287 setuid(fileinfo.st_uid);
1288 }
1289
1290 execlp("pictwpstops", printer, argv[1], argv[2], argv[3], argv[4], argv[5],
1291 filename, NULL);
1292 perror("ERROR: Unable to exec pictwpstops");
1293 return (errno);
1294 }
1295
1296 close(fd);
1297
1298 if (pid < 0)
1299 {
1300 /*
1301 * Error!
1302 */
1303
1304 perror("ERROR: Unable to fork pictwpstops");
1305 unlink(filename);
1306 if (ppdfile)
1307 unlink(ppdfile);
1308 return (-1);
1309 }
1310
1311 /*
1312 * Now wait for the filter to complete...
1313 */
1314
1315 if (wait(&status) < 0)
1316 {
1317 perror("ERROR: Unable to wait for pictwpstops");
1318 close(fd);
1319 unlink(filename);
1320 if (ppdfile)
1321 unlink(ppdfile);
1322 return (-1);
1323 }
1324
1325 if (ppdfile)
1326 unlink(ppdfile);
1327
1328 close(fd);
1329
1330 if (status)
1331 {
1332 if (status >= 256)
1333 fprintf(stderr, "ERROR: pictwpstops exited with status %d!\n",
1334 status / 256);
1335 else
1336 fprintf(stderr, "ERROR: pictwpstops exited on signal %d!\n",
1337 status);
1338
1339 unlink(filename);
1340 return (status);
1341 }
1342
1343 /*
1344 * Return with no errors..
1345 */
1346
1347 return (0);
1348 }
1349 #endif /* __APPLE__ */
1350
1351
1352 /*
1353 * 'sigterm_handler()' - Handle 'terminate' signals that stop the backend.
1354 */
1355
1356 static void
1357 sigterm_handler(int sig) /* I - Signal */
1358 {
1359 (void)sig; /* remove compiler warnings... */
1360
1361 /*
1362 * Remove the temporary file(s) if necessary...
1363 */
1364
1365 if (tmpfilename[0])
1366 unlink(tmpfilename);
1367
1368 #ifdef __APPLE__
1369 if (pstmpname[0])
1370 unlink(pstmpname);
1371 #endif /* __APPLE__ */
1372
1373 exit(1);
1374 }
1375
1376
1377 /*
1378 * End of "$Id: ipp.c 4926 2006-01-13 03:12:13Z mike $".
1379 */