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