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